A Workaround For VirtualPath Weirdness With Custom VirtualPathProviders
[[ Meta-blogging: as you may have noticed from the name/description change (and of course, this article) I’ve decided to shift the focus of this blog back to the “front lines” of Microsoft/.NET development technologies. All other rants and ramblings will go to Alex's Soapbox over at WTF ]]
If you've ever come across this error...
The VirtualPathProvider returned a VirtualFile object with VirtualPath set to '/global/initrode/embeddedControl.ascx' instead of the expected '//global/initrode/embeddedControl.ascx'
... then chances are you're implementing VirtualPathProvider in order to serve up embedded Page/Control resources or something fun like that. Let's just hope your not serving pages from a ZIP file. And if you have no idea what a VirtualPathProvider is, then do check out that MSDN article I linked to get an idea.
The reason behind this error is identified in Microsoft Bug #307978: ASP.NET is erroneously replacing page compilation errors with the bad virtual path error. While ensuring that your virtual-pathed page will compile is a sure-fire way to fix the error, finding the compilation errors can be a bit of pain.
Fortunately, there's a pretty easy workaround that will let you see some of the compilation errors. First, make sure that your custom VirtualPathProvider has a static method that can determine if given virtualPath is on disk or is virtualized (e.g. an embedded resource). Next, create an IHandlerFactory that inherits PageHandlerFactory, overrides the GetHandler method, and has a try/catch around a call to base.GetHandler(). In the event that an exception occurs, simply determine if the request's virtual path is "virtual" (through that static method) and, if so, rethrow the exception with only the error message. In other words,
public class MyPageHandlerFactory : PageHandlerFactory { public override IHttpHandler GetHandler(HttpContext context, string requestType, string virtualPath, string path) { try { return base.GetHandler(context, requestType, virtualPath, pathTranslated); } catch (Exception ex) { //TODO: ASP.NET 2.0 Bug Workaround // There is an error generating a stack trace for VirtualPathed files, so // we have to give up our stack trace if it's a resource file if (EmbeddedResourceVirtualPathProvider.IsResourcePath(virtualPath)) throw new Exception(ex.Message); else throw ex; } } }
Since we're only wrapping the GetHandler method (as opposed to the IHttpHandler's ProcessRequest method), the only errors you'll see wrapped like this are pre-ProcessRequest errors (e.g. compilation errors). And while this won't give you the full stack trace, at least you'll see something like this instead:
http://server//global/initrode/embeddedControl.ascx(5): error CS1002: ; expected