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

6 Comments

  • When I use this with MVC I receive the error, "The file /Mvc.axd' does not exist. Any ideas?

  • To rethrow, just use the statement "throw;"
    Never throw an existing exception explicitly (try saying that five times fast) because the stack trace will indicate the point of your re-throw and not the original throw location.

  • > To rethrow, just use the statement "throw;"
    >
    > Never throw an existing exception explicitly
    > (try saying that five times fast) because the
    > stack trace will indicate the point of your
    > re-throw and not the original throw location.

    That's not the point here. The point was obviously to NOT rethrow the EXACT same exception, since it contains extra contextual information that leads to reproducing this ASP.NET 2.0 bug.

    We ONLY need to throw a similar-looking exception with ONLY the original Message to prevent this bug from occurring.

  • > We ONLY need to throw a similar-looking exception with ONLY the original Message to prevent this bug from occurring.

    That branch of the code ( "throw new Exception(ex.Message);" ) is fine. I'm talking about the other branch ( "throw ex;" ).

  • Is anyone else bothered by the fact that if you go the MS Connect bug report it says it was resolved over a year ago and yet I just had this same problem today using VS 2008 and Vista, granted I am compiling for 2.0 though. Am I missing an update?

    Has this been corrected in a patch, SP, etc???

    These virtual path providers are very handy, albeit after you waste several days doing research and testing workarounds for weird issues. Seems like no one has it right, except maybe internally at MS.

  • Thanks for the blogpost Alex ! This helped me with implementing the workaround for my VirtualPathProvider that also serves embedded resources.

Comments have been disabled for this content.