Second Life of a Hungarian SharePoint Geek

July 15, 2012

Automatically setting the next statement to a specific position when debugging in Visual Studio

Filed under: Debugging, Macros, Tips & Tricks, Visual Studio — Tags: , , , — Peter Holpar @ 21:58

Have you ever faced to the problem during debugging in Visual Studio, that you find a problem in a code block, and would like to avoid this code without altering the code and rebuilding / restarting  the application? In this case you need to set the next statement to a specific line of code using the context menu (see below).

image

It is trivial when you need to do it just  a few time, but it can be rather tedious, if not impossible, if you are – for example – in a loop processing several hundreds of items, and should do it for each items.

Fortunately, it is easy to achieve this goal using Visual Studio macros. The following method – defined in a module called in this example DebugHelpers – jumps to the next bookmark, and sets the next statement to that position.

Sub GoToNextBookmark()
    DTE.ExecuteCommand("Edit.NextBookmark")
    DTE.ExecuteCommand("Debug.SetNextStatement")
End Sub

Having this method, all you need to do is to set a new breakpoint with When Hit… option at the line of code you want to jump from,

image

and set the GoToNextBookmark method in the Run a macro list:

image

Then mark the target of the jump with a bookmark, and you are ready.

October 24, 2011

Getting the process ID of the IIS / SharePoint application pool using PowerShell

Filed under: Debugging, PowerShell, SP 2010 — Tags: , , — Peter Holpar @ 22:28

It is easy to attach your debugger to the right SharePoint application pool using Visual Studio, especially if specific extensions like CKS.Dev or my VS extension is installed.

Sometimes life is not so trivial, you don’t have Visual Studio installed on the server and / or you should use other tools to debug your application. It would be great to have the right process ID in this case as well.

For example, assume you have to attach WinDbg or other debugging tools like Deblector to the process of the IIS application pool of a selected SharePoint application.

Two years ago I wrote a post about how to attach the VS debugger from a VS macro. In that post I’ve illustrated how to get the right process based on the application pool name (see the GetProcessIdByAppPoolName and GetAppPoolNameFromCommandLine methods in that post). In the post about my VS extension mentioned above you can find a similar solution as well.

In this post I show you two PowerShell methods that can be used when there is no Visual Studio on the computer or you simply do not want to work with that.

You should specify the name of the application pool for the Get-AppPoolProcessIdByName method and it displays the process ID of the matching application pool:

function Get-AppPoolProcessIdByName($name) 

     Get-WmiObject Win32_Process |
       Where-Object { $_.CommandLine -like "*w3wp.exe*" } |
       ForEach-Object { [regex]::Matches($_.CommandLine, "-ap ""(.+)"" -v") |
       Add-Member NoteProperty -Name ProcessId -Value $_.ProcessId -PassThru } |
       Where-Object { $_.Success -and $_.Groups.Count -gt 1 -and $_.Groups[1].Value -eq $name } |
       ForEach-Object { Write-Host $_.ProcessId }
}

Usage:

Get-AppPoolProcessIdByName("SharePoint – 80")

The Get-SPAppPoolProcessIdByUrl method first determines the corresponding SharePoint web application, then calls the Get-AppPoolProcessIdByName method to get the process ID.

function Get-SPAppPoolProcessIdByUrl($url) 

     $app = Get-SPWebApplication $url
     Get-AppPoolProcessIdByName($app.DisplayName)
}

Usage:

Get-SPAppPoolProcessIdByUrl("http://sp2010")

You can use Get-AppPoolProcessIdByName for arbitrary IIS application pool, but as you can see from the code (and as I intended to sign with the Get-SP prefix) the Get-SPAppPoolProcessIdByUrl method is SharePoint specific.

June 12, 2009

I got tired of attaching debugger to w3wp.exe. And you?

Filed under: Debugging, SharePoint — Tags: , — Peter Holpar @ 04:18

As a SharePoint developer, I spend most of my days developing and testing web parts, custom pages, fields and event receivers. Part of this activity is debugging, where one should attach the debugger to the w3wp.exe process instance that belongs to the application pool of the web site we developing on.
If you have done that yet, you know it is not an exciting task to select the correct process if you have dozens of instances. You can do that by trial and error, and checking if the red lights next to the breakpoints are turned on, or you should look for the next instance. It is one step better to run iisapps.vbs as described here, and use the process id it displays for the correct application pool, but it is still a cumbersome manual process, that a developer does not enjoy.
Developers enjoy development, and developing tools that help development. A good platform for those tools is the Visual Studio IDE and in that environment maybe the simplest way to create tools is writing macros.
To help my job I created a macro in Visual Studio 2005 that attaches the debugger to the w3wp.exe instance of the configured application pool. I hope it works for Visual Studio 2008 too, but I have not tested it with that yet.
Configuring the name of the application pool in Visual Studio
The configuration can be done using the project properties. These properties are stored in the csproj.user file of the project, so if you use version control and work in a team, the value can be different for teammates. I selected the Command line arguments text field on the Debug tab to store the name of the application pool we want to attach the debugger to.
There might be multiple projects in the solution, and each project may be multiple configuration (like debug or release), so I decided to use the debug configuration for the first startup project. It is important, because there may be multiple startup projects in a solution.

clip_image002

The macro
After this introduction, let’s see the code of the macro. We need the import the following namespaces:

  1. Imports System
  2. Imports EnvDTE
  3. Imports EnvDTE80
  4. Imports System.Management
  5. Imports System.Text.RegularExpressions

 
The entry point for the macro is the AttachMacro sub. In that we first determine the project whose configuration we will use. If there is only a single project in the solution we can go with that, if there are multiple projects then at least one of the must be set as startup project, but only the configuration of the first one will be used.
If we found the projects, we use the GetArgumentsForDebug function (see details later) to read the application pool name from the configuration.
Finally we call the AttachToAppPool function (see details later) to attach or debugger to the process of the application pool.
 
  1. Public Sub AttachMacro()
  2.  
  3.     Try
  4.         ' get the startup project first
  5.         Dim project As Project
  6.         Dim solutionBuild As SolutionBuild = DTE.Solution.SolutionBuild
  7.         Dim startUpProjs As Array = solutionBuild.StartupProjects
  8.         Dim projName As String
  9.  
  10.         If startUpProjs.Length = 0 Then
  11.             If DTE.Solution.Projects.Count = 1 Then
  12.                 project = DTE.Solution.Projects.Item(1)
  13.             Else
  14.                 MsgBox("There is no startup project and solution contains multiple project!", MsgBoxStyle.Exclamation, "Alert")
  15.                 Exit Sub
  16.             End If
  17.         Else
  18.             projName = solutionBuild.StartupProjects(0)
  19.             project = GetProjectByName(projName)
  20.             If project Is Nothing Then
  21.                 MsgBox(String.Format("Startup project '{0}' not found by name!", projName), MsgBoxStyle.Exclamation, "Alert")
  22.                 Exit Sub
  23.             End If
  24.         End If
  25.  
  26.         Dim appPoolName As String = GetArgumentsForDebug(project)
  27.  
  28.         If String.IsNullOrEmpty(appPoolName) Then
  29.             MsgBox(String.Format("Command line arguments property is not set for stratup project '{0}', debug mode!", projName), MsgBoxStyle.Exclamation, "Alert")
  30.             Exit Sub
  31.         End If
  32.  
  33.         Dim processFound As Boolean = AttachToAppPool(appPoolName)
  34.  
  35.         If Not processFound Then
  36.             MsgBox(String.Format("No worker process found for application pool called '{0}'!", appPoolName), MsgBoxStyle.Exclamation, "Alert")
  37.             Exit Sub
  38.         End If
  39.  
  40.     Catch ex As System.Exception
  41.         MsgBox(ex.Message)
  42.     End Try
  43.  
  44. End Sub

 
The GetProjectByName function is a simple helper method to get
the project object using the name of the startup project.
 
  1. Private Function GetProjectByName(ByVal projectName As String) As Project
  2.  
  3.     Dim result As Project = Nothing
  4.     For Each project As Project In DTE.Solution.Projects
  5.         If project.UniqueName = projectName Then
  6.             result = project
  7.             Exit For
  8.         End If
  9.     Next
  10.  
  11.     GetProjectByName = result
  12.  
  13. End Function

In the GetArgumentsForDebug function we read the Command line arguments value that is stored in the StartArguments parameter from the debug config of the specifed project.
 
  1. Private Function GetArgumentsForDebug(ByVal project As Project) As String
  2.  
  3.     Dim configuration As EnvDTE.Configuration
  4.     GetArgumentsForDebug = String.Empty
  5.  
  6.     For Each configuration In project.ConfigurationManager
  7.         If configuration.ConfigurationName = "Debug" Then
  8.             Dim startArgsObj As Object = configuration.Properties.Item("StartArguments")
  9.             If Not startArgsObj Is Nothing Then
  10.                 GetArgumentsForDebug = CType(startArgsObj.Value, String)
  11.             End If
  12.             Exit Function
  13.         End If
  14.     Next
  15.  
  16. End Function

 
An interesting part of the macro is the GetProcessIdByAppPoolName function. In this function we use WMI (IMPORTANT: don’t forget to reference the System.Management assembly!) to get the list of all w3wp.exe processes, and select the one that belongs to the specified application pool. The ID of the process is returned.
In the comparison we use the GetAppPoolNameFromCommandLine function (see later), that receives the CommandLine property of the process, that looks like this for a w3wp.exe process:
c:\windows\system32\inetsrv\w3wp.exe -a \\.\pipe\iisipm5f3cda83-745a-423a-88b2-103a2f632200 -ap "MyAppPool"
You can see that the name of the application pool is at the end of the string.
 
  1. Private Function GetProcessIdByAppPoolName(ByVal appPoolName As String) As Long
  2.  
  3.     GetProcessIdByAppPoolName = -1
  4.     Dim scope As ManagementScope = New ManagementScope("\\localhost\root\cimv2")
  5.  
  6.     Dim searcher As ManagementObjectSearcher = New ManagementObjectSearcher("select * from Win32_Process where Name='w3wp.exe'")
  7.     searcher.Scope = scope
  8.  
  9.     For Each process As ManagementObject In searcher.Get()
  10.         Dim commandLine As String = process.GetPropertyValue("CommandLine")
  11.         If GetAppPoolNameFromCommandLine(commandLine).ToUpper() = appPoolName.ToUpper() Then
  12.             GetProcessIdByAppPoolName = process.GetPropertyValue("ProcessId")
  13.             Exit For
  14.         End If
  15.     Next
  16.  
  17. End Function

In the GetAppPoolNameFromCommandLine function we use a simple regular expression to get the name of the application pool from the CommandLine string.
 
  1. Private Function GetAppPoolNameFromCommandLine(ByVal commandLine As String) As String
  2.  
  3.     GetAppPoolNameFromCommandLine = String.Empty
  4.     Dim re As Regex = New Regex("-ap ""(.+)""", RegexOptions.IgnoreCase)
  5.     Dim matches As MatchCollection = re.Matches(commandLine)
  6.     If matches.Count = 1 Then
  7.         If matches.Item(0).Groups.Count > 1 Then
  8.             GetAppPoolNameFromCommandLine = matches.Item(0).Groups(1).Value
  9.         End If
  10.     End If
  11.  
  12. End Function

 
In the AttachToAppPool function we attach the debugger to the process having the same process ID that we determined earlier.
 
  1. Private Function AttachToAppPool(ByVal appPoolName As String) As Boolean
  2.  
  3.     Dim dbg2 As EnvDTE80.Debugger2 = DTE.Debugger
  4.     Dim trans As EnvDTE80.Transport = dbg2.Transports.Item("Default")
  5.     Dim dbgeng(3) As EnvDTE80.Engine
  6.     dbgeng(0) = trans.Engines.Item("T-SQL")
  7.     dbgeng(1) = trans.Engines.Item("T-SQL")
  8.     dbgeng(2) = trans.Engines.Item("Managed")
  9.  
  10.     AttachToAppPool = False
  11.     For Each process As EnvDTE80.Process2 In dbg2.LocalProcesses
  12.         If process.ProcessID = GetProcessIdByAppPoolName(appPoolName) Then
  13.             process.Attach()
  14.             AttachToAppPool = True
  15.             Exit For
  16.         End If
  17.     Next
  18.  
  19. End Function

 
To make things even more comfortable it is the best to assign a keyboard shortcut to the macro using the Visual Studio Tools/Options… menu item as shown on the screenshot below:
clip_image004

Theme: Shocking Blue Green. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 42 other followers