Remote debugging ASP.NET applications using Visual Studio 2008
If you have recently upgraded a Visual Studio .NET 2003 ASP.NET application project or a Visual Studio 2005 Web Application project to a Visual Studio 2008 ASP.NET Web Application project you may have noticed the following popup. Needless to say this makes it a little more difficult to connect remotely to your project in order to run and debug it:
At this point I can only guess as to why connecting to a remote web application project no longer seems to work. I do have a few guesses but I won't bother mentioning what they are because inevitably somebody will tell me my guesses are wrong. Instead I thought I would share with you a relatively simple way to get back to work and remotely debug anyway.
In a nutshell, to remotely debug we want to run two instances of Visual Studio. One will be the "debugger" instance, and the other will be the "editor/compiler" instance.
Here are the steps.
- Install the Visual Studio 2008 Remote Debugger on your remote machine. You can find the installation file in the Remote Debugger folder on your Visual Studio 2008 installation disk.
- Once you have the remote debugger installed, run Visual Studio 2008 Remote Debugger Configuration Wizard from the start menu of the remote machine.
- Choose Run the "Visual Studio 2008 Remote Debugger" service. You could try running individual instances of the debugger but you may run into user rights or permissions related issues. Personally I prefer easy versus difficult. Your choice though.
- Leave the service user as LocalSystem and leave the password blank then click Next.
- If you see a Configure the Windows Firewall for Debugging option, choose the available option to suit your needs then click Next then Finished.
- Next in order to prepare your old project to be opened with minimal problems, open your vbproj or csproj file in notepad and remove the "IISUrl" related settings then save the project file.
- Back on your local workstation, open Visual Studio 2008 then click File --> Open --> Project\Solution... and browse to the UNC path where the project or solution file resides.
- Example: \\someserver\C$\inetpub\wwwroot\RemoveWebApp\myapp.csproj
- Note: If you are not an administrator on the remote machine then you cannot use the adminitrative "C$" share. Instead you will need to ask the administrator to create a share for you.
- Note: If you are not an administrator on the remote machine you will also need to ask the administrator to add you to the "Debugger Users" group.
- You can leave the Web settings for the project properties set to Use Visual Studio Development Server or Use IIS Web server. It does not matter since we aren't going to be launching the project that way. If you do choose to create a virtual directory you will to create it locally on your workstation of course to avoid the error mentioned above. This will actually create a virtual directory that points to the UNC path where your content lives.
- This first instance of Visual Studio will be used to edit source files and compile the application as necessary.
- From your local workstation, open a second instance of Visual Studio 2008 but do not open any projects.
- Click Tools --> Attach to process...
- Enter the appropriate remote server name in the Qualifier textbox then click the Refresh button near the bottom.
- Choose the correct remote process (aspnet_wp.exe or w3wp.exe for example) and click Attach
- If there are multiple w3wp.exe processes running and you aren't sure which to pick you can run "cscript c:\Windows\System32\iisapp.vbs" on the remote machine to get the right PID. iisapp.vbs simply lists the running W3WP.exe processes along with their Application Pool name and PID.
- If there are *no* processes running that you know you need to attach to, then you should make at least one request to http://someServer/RemoteWebApp" in order to spawn the worker process.
- To make retrieving the PID a little easier I have included some sample VBS code below to get it for you.
- Click File --> Open --> File and open the source file that you want to debug from your UNC path.
- Example: \\remotemachine\C$\inetpub/wwwroot\RemoveWebApp\Default.aspx.cs
- Set a breakpoint somewhere.
- This second instance of Visual Studio will be used to "debug" your project
- Open IE and request your page and you should break at the desired location.
NOTE: Something you might want to do to avoid the need to attach over and over while writing your code is to disable the idle timeout settings and possibly the recycling settings of the application pool that your ASP.NET application is using in IIS. To find which application pool your application is using you need to go to the remote server and open the IIS manager. Then go into your application's properties and look at either the "[Virtual] Directory" tab or "Home Directory" tab depending on whether it is a web site or directory level application.
To find the application pool name go into the properties of your application.
To disable the AppPool idle timeout go into the properties of the correct AppPool:
And finally... here is a script I wrote to make obtaining your remote PID a little easier. To use this:
- Copy/paste the text below into notepad
- Save the file with a VBS extension
- Edit the first 3 variables machineName, appPoolName, and url so they are correct for your remote server.
- Run this from your workstation.
'''''''''''''''''''''''''''''''' ' Variables machineName = "remoteMachineName" 'This to your remote machine. appPoolName = "DefaultAppPool" 'This is the AppPool your ASP.NET is using. url = "http://remoteMachineName/" 'This is the URL that will launch your AppPool wmiPath = "winmgmts://" & machineName & "/root/cimv2" wmiQuery = "SELECT * FROM Win32_Process WHERE Name='w3wp.exe'" '''''''''''''''''''''''''''''''' ' Code execution begins Set wmiObj = GetObject(wmiPath) Set wpList = wmiObj.ExecQuery(wmiQuery) ShowAppPoolPid() response = AskAboutRequestingPage() If (response = vbYes) Then SpawnWorkerProcess() MsgBox "Unable to locate PID for '" & appPoolName & "'" End If '''''''''''''''''''''''''''''''' 'Methods Sub ShowAppPoolPid() message = "" For Each wp In wpList name = GetAppPoolId(wp.CommandLine) If (UCase(appPoolName) = UCase(name)) Then message = message & vbCrLf & " " & wp.ProcessID End If Next If (message <> "") Then MsgBox "'" & appPoolName & "' is running under the following PID(s):" & message WScript.Quit() End If End Sub Function GetAppPoolId(strArg) Dim Submatches Dim strPoolId Dim re Dim Matches On Error Resume Next Set re = New RegExp re.Pattern = "-ap ""(.+)""" re.IgnoreCase = True Set Matches = re.Execute(strArg) Set SubMatches = Matches(0).Submatches strPoolId = Submatches(0) GetAppPoolId = strPoolId End Function Function AskAboutRequestingPage() message = "'" & appPoolName & "' is not currently running on '" & machineName & "'. Would you like to make a request to '" & url & "' in order to spawn the process?" style = vbYesNo + vbCritical + vbDefaultButton2 title = "AppPool not running" AskAboutRequestingPage = MsgBox(message, style, title) End Function Sub SpawnWorkerProcess Dim ie: Set ie = WScript.CreateObject("InternetExplorer.Application", "Ie_") ie.Navigate url ie.Visible = True Set ie = Nothing End Sub Sub Ie_NavigateComplete2(ByVal pDisp, URL) ShowAppPoolPid() End Sub