Mike Volodarsky's blog

Formerly the core server PM for IIS 7.0 and ASP.NET, now I run LeanSentry.
UPDATES: New troubleshooting guide released! Fix IIS website hangs →

Fix problems with Visual Studio F5 debugging of ASP.NET applications on IIS7 Vista

********************************************************************** 
UPDATE - 4/8/2008

I've collected the various how-tos and gotchas about using Visual Studio 2005 and Visual Studio 2008 with IIS 7.0 applications into two detailed articles:
Using Visual Studio 2005 with IIS 7.0
Using Visual Studio 2008 with IIS 7.0
Be sure to check them out for detailed info on working with both local and remote IIS 7.0 applications on Windows Vista SP1 and Windows Server 2008 RTM, doing local and remote debugging, and other stuff.

**********************************************************************
UPDATE - 6/20/2007
A Visual Studio patch that fixes the Visual Studio F5 debugging of IIS7 applications on Windows Vista Home Premium and above has been released!  This patch solves the "An authentication error occurred while communicating with the web server.  Please see Help for assistance.", and "Debugging failed because integrated Windows authentication is not enabled.  Please see Help for assistance" errors, that until now had to be worked around using the instructions below.

This patch also enables customers using Windows Vista Home Premium customers to use Visual Studio F5 debugging, who were not able to use this feature at all previously because Windows Authentication was not available on Windows Vista Home Premium.

You can download the patch from connect here: Download.  Also see the KB article 937523 (it is being published today, so may not be available yet).

Thank you for your patience so far, and I hope that this resolves your problems.  If not, please let me know.

Thanks,

Mike
**********************************************************************

A number of people have been reporting problems when trying to debug their ASP.NET applications on Windows Vista with Visual Studio 2005 F5 debugging support.  There are a handful of posts about trying to get this to work in various ways ... most of which are missing key information needed to *really* get it to work.

I am going to try and provide you with step-by-step instructions below, and explain the several tradeoffs that you may need to make along the way.

A little note about the purpose of Visual Studio's F5 debugging support:

Its designed to help you attach the debugger to the worker process running your ASP.NET application.  You can always manually attach the debugger via the Menu: Tools>Attach to Process ... option, assuming you know what instance of W3WP.EXE worker process on your server the application is running in.  The F5 debugging mode figures this out for you, and starts a convinient browser window for you to play around with the app.  Other than that, it is the same as debugging it manually. 

If you are debugging from a remote machine, you will need to run the Visual Studio Debugging Monitor on the remote machine, and open the firewall so that Visual Studio can connect to the debugger (msvsmon.exe does this for you by default).  Then, you can still attach directly via the Attach to Process ... dialog, by specifying the server's machine name.

So, the bottom line is, if you know which process you need to debug, you don't need F5 debugging to debug ASP.NET apps.  With that in mind, here is what it takes to get the convinient F5 Debugging working on Vista:

******************************************************************
NOTE: Be sure to get the Visual Studio 2005 SP1 update if you are running Visual Studio 2005 on Vista.  This update fixes a number of incompatibilities you may otherwise hit.  If you have issues installing SP1, please be sure to review Heath Stewart's blog entry at http://blogs.msdn.com/heaths/archive/2007/01/11/known-issues-with-visual-studio-2005-service-pack-1.aspx.
*******************************************************************

1) Run Visual Studio 2005 as Administrator

Unfortunately, Visual Studio 2005 SP1 still doesn't support the required elevation functionality to be able to do debugging when run by a LUA user.  I hope it will very soon - in the meantime, in order to enable debugging, you need to run it as Administrator:

Run Visual Studio 2005 as Administrator

2) Install required IIS components

IIS7 in Vista and Longhorn server has been completely modularized to reduce the security surface area and improve performance, and installs a pretty small set of features by default.  In order to enable F5 debugging, you will need at minimum the following components:

  1. ASP.NET
  2. Windows Authentication Module (Provides support for Windows authentication with NTLM and Kerberous)
  3. Metabase compatibility layer (Provides support for legacy IIS configuration APIs used by existing software to manage IIS.  Note that this is required to connect to your ASP.NET application from Visual Studio, even before you attempt debugging.)

Install them from Control Panel > Programs > Turn Windows Features on and off:

Install required IIS components for Visual Studio debugging

After doing this, you may get the following error if you attempt to debug:

Visual Studio wrong ASP.NET version warning

Always press No.  This message is incorrect, and is due to a change in how versioning is done for ASP.NET applications on IIS7 that is not being properly handled by Visual Studio 2005.

3) Enable Windows Authentication for your ASP.NET application

Visual Studio 2005 debugging requires the ASP.NET application to have Windows Authentication enabled.  This requirement also exists for Windows XP / Windows Server 2003.  To enable Windows Authentication, open the IIS Manager administration tool by running Start > type inetmgr in the search box, navigate to your application, choose the Authentication tab, and Enable Windows Authentication:

Enable Windows Authentication for the ASP.NET application

Note: If you have Anonymous / Forms authentication enabled for your application, leave it on.

4) Enable your ASP.NET application to provide debugging auto-attach support

At this point, if you press F5 and try to attach to your application, you may either see nothing or get the following error:

Authentication error when using Visual Studio F5 debugging

This is the dreaded error that frazzles most people.  The unfortunate fact is, there is a bug in ASP.NET Integrated mode (the default ASP.NET mode for IIS7) that prevents authentication from taking place correctly for the debug auto-attach feature.  The bug manifests only in the following case:

  1. ASP.NET application is running in an application pool using Integrated mode (Default), and
  2. ASP.NET application has global.asax event handlers or custom modules that subscribe to pipeline notifications prior to AuthenticateRequest (not default) - so, BeginRequest, PostBeginRequest, and in some cases early in AuthenticateRequest.

The following workarounds are avaialble:

  1. Remove global.asax / modules causing this.  This is a farily bad option because they are the ones you want to debug a lot of the time :) .
  2. Move the application to Classic mode.  This may be acceptable, but not ideal - especially if you are developing an application that takes advantage of Integrated mode specific features.  Even if you are not, you may sometime in the future so running your application in Integrated mode will help you make sure you are well positioned for it.
  3. Use the debug assistant module (below).  This is a hack I wrote that temporarily enables the debugging auto-attach to work correctly in integrated mode.  But, since you are not debugging your Vista site while its live on the internet (right? right?), you can temporarily install and use it while you are developing and debugging.

For how to move your application to Classic mode, you can consult one of the existing blog posts about this issue - for example, Rajiv's post here.

5) Enable the Debugging Assistant to enable F5 Debugging in Integrated mode

Like I said, don't put this module on a production system.  But, its entirely safe to use it for your development / test machine to help with F5 debugging.  Just make sure that you turn off debugging-related stuff, and remove this module before deploying in production as a general deployment practice.  Hopefully I havent scared you too much, since the module doesnt really do anything major - it just allows and tweaks the debug attach request to enable ASP.NET debug attach to work.  It does however disable authorization for requests mapped to the DebugHandler below (although the handler will require an authenticated Windows user).

Here is how to set this up on your machine:

  1. Download the Debug Assistant module (disclaimer: This module is not provided by Microsoft, and is not supported in any way.)
    ***********************************
    NOTE: If you are running on a 64bit OS, download the 64bit Debug Assistant.  It will replace the 32 bit version if you erroneously installed it on your 64bit machine - currently, using both on the same machine is not supported (but you can do it if you know how :) ).
    ***********************************
  2. Unzip to a local directory
  3. Right click on install.bat, and chose "Run as Administrator"
  4. Add the following in your application's web.config file:

<configuration>

      <system.webServer>

            <modules>

                  <add name="debugassistant" />

            </modules>

            <handlers>

                  <add name="DebugHandler" path="DebugAttach.aspx" verb="DEBUG" type="System.Web.HttpDebugHandler" />

            </handlers>

      </system.webServer>

      <system.web>

            <compilation debug="true" />

      </system.web>

</configuration>

This enables the Debug Assistant module in this application, and explicitly declares the DebugHandler handler (this is mandatory in order for debugging to work).  The debug=true directive is something you need to have also but VS 2005 will warn you about it if you dont.

Well, that's it, hope it helps.  Please let me know if this doesnt work for you, or you experience other issues.

IIS7 modules vs. IIS6 ISAPI: Managing request state in your module

For post #4 in the IIS7 Modules vs. IIS6 ISAPI series, let's take a look at another common pattern in IIS module development - storing request-specific state inside your module.

When developing a module that participates in request processing, it is often necessary to store request-specific state that the module computes in one stage of the pipeline, and then uses again in a later stage.  For example, if you are writing a module that calcuates the duration of the request, you will need to capture the initial timestamp at the beginning of the pipeline, and then compare it with the timestamp at the end of request processing in order to determine how long the request took.

At a high level, in order to do this, the module needs to allocate some memory to store the state, and associate it with the particular request.  In subsequent request pipeline stages, it needs to look it up, and eventually, deallocate the memory used to store it when the request is finished.

If you just want to see how this is done with IIS7 module APIs, jump straight to the IIS7 part.

Let's first see what is involved when using IIS6 ISAPI filters.

In the ISAPI filter world, each request notification is delivered to the ISAPI DLL via the static HttpFilterProc entrypoint function, passing along the notification id and the filter context structure: 

DWORD WINAPI HttpFilterProc(
  PHTTP_FILTER_CONTEXT pfc,
  DWORD NotificationType,
  LPVOID pvNotification
);

Then, depending on the notification, your HttpFilterProc implementation will provide the required request processing.  In our case, what we are interested in is to store some information, such as our timestamp, during an early notification such as SF_NOTIFY_PREPROC_HEADERS, and then access it later in a late notification such as SF_NOTIFY_END_OF_REQUEST

First, some basics - because your ISAPI filter will be notified separately for the different notifications, you cannot store the state in a local variable inside a function.  Also, since the ISAPI filter will be responsible for processing many requests concurrently, you cannot simply store the state in a static variable inside your DLL.  Instead, you must either:

  1. Create your own table indexed by the request being processed, and store/lookup state in that table, providing the proper locking to insure thread safety. (DONT DO THIS [:)])
  2. Use the server-provided mechanism to associate your own state "context" object with the request, and use it to keep your state between notifications.  This context is automatically associated with the current request, and provided to your filter during each notification inside the HTTP_FILTER_CONTEXT structure.

Our code would look something like this:

class CMyContext

{

public:

    CMyContext(clock_t time)

        : startTime( time )

    {

    }

       

    clock_t GetStartTime()

    {

        return startTime;

    }

       

private:

    clock_t startTime;

};

 

DWORD

WINAPI

HttpFilterProc(

   PHTTP_FILTER_CONTEXT pfc,

   DWORD notificationType,

   LPVOID pvNotification

)

{

    CMyContext*             pContext = NULL;

    clock_t                 time;

       

    switch(notificationType)

    {

    case SF_NOTIFY_PREPROC_HEADERS:

        time = clock();

       

        //

        //  Create and store our per-request context instance

        //

        pfc->pFilterContext = new CMyContext(time);

        break;

    case SF_NOTIFY_END_OF_REQUEST:

       

        //

        //  Get the context from the filter

        //

        pContext = (CMyContext *)pfc->pFilterContext;

        time = clock() - pContext->GetStartTime();

       

        //

        //  Clean up the context at the end of the request

        //

        delete pContext;

        pfc->pFilterContext = NULL;

       

        break;

    default:

        break;

    }

       

    return SF_STATUS_REQ_NEXT_NOTIFICATION;

}

 

Some notes about this code:

  1. I had to create my own context class, CMyContext, to store the request state data I need.
  2. I have to allocate the context for each request, and then make sure to clean it up at the end of the request to avoid memory leaks (alternatively, I could have used the the request memory pool that doesnt need deallocation by allocating the memory with the AllocMem filter support function).

Now, lets take a look at how IIS7 does it

In IIS7, the server will create a new instance of your module class for each request (see this post for more explanation of the IIS7 module class).  Because of this, storing request state is as simple as declaring member variables inside your module class.  You can set the values of these variables as you compute the state during one or more notifications, and use them directly from other notifications.

With this in mind, your IIS7 code looks like this:

class CMyModule : public CHttpModule

{

public:

    CMyModule()

        : startTime ( 0 )

    {

    }

       

    REQUEST_NOTIFICATION_STATUS

    OnBeginRequest(

        IN IHttpContext *                       pHttpContext,

        IN OUT IHttpEventProvider *             pProvider

    )

    {

        startTime = clock();

       

        return RQ_NOTIFICATION_CONTINUE;

    }

       

    REQUEST_NOTIFICATION_STATUS

    OnEndRequest(

        IN IHttpContext *                       pHttpContext,

        IN OUT IHttpEventProvider *             pProvider

    )

    {

        clock_t elapsed = clock() - startTime;

       

        return RQ_NOTIFICATION_CONTINUE;

    }

       

private:

    clock_t     startTime;

};


This eliminates the need to create a separate state class, and associate it with he request via an external mechanism.  You can declare and use all of the request state as type-safe member variables directly inside your module.

Because there is only one thread processing a single request at any given time, and there is a separate instance of your module for each request, you don't need to worry about thread safety (as long as you are using instance variables).

When the request is finished, your CHttpModule instance is automaticaly cleaned up by the server, so you dont need to worry about managing the lifetime of the state.  Just make sure to release whatever resources you are using inside your module's destructor like you normally would for any C++ class.

What about non-request state?

Sometimes, you will need to store state that is not associated with a particular request.  This is necessary whenever the lifetime of the state extends beyond the request, or is scoped to a particular server object like connection, application, or an arbitrary url - for example, in order to keep track of the number of requests to a particular site.  IIS7 provides a mechanism to do this by associating your own context (sorry, extra class required) with the object of choice via the IHttpModuleContextContainer inteface avaialble from many server objects. 

More on this in a future post ...