Tome's Land of IT

IT Notes from the Powertoe – Tome Tanasovski

Finding the thread (PID) that belongs to a tab in IE 8 with PowerShell

If you run multiple tabs in Internet Explorer 8 you may have noticed that process explorer will show you multiple instances of iexplore.exe.

The question is how can you associate those PIDs (Process IDs) with the tabs they belong to so that you can gather data about the client-side performance of a web applications that are running on your users’ desktops.

The answer is not very easily obtained.  The only article I could find on the subject used process explorer to identify the title of the tab.  This is great if I wanted to stand over a desktop and jot down this information on a piece of paper, but this doesn’t even come close to being a solution for my need to pull the title from the tabs of thousands of computers.  Regardless, I knew that if sysinternals found a method to get this info, I could too – with PowerShell.

Our first stop is get-process.  We know that we can get the PIDs associated with iexplore by doing the following:

Get-Process iexplore |select id

I knew that I only cared about the PIDs for the child threads.  By inspecting the objects returned by Get-Process I found that the master thread was the only one that had a value set for the MainWindowTitle property.  That meant I could use the following bit of PowerShell to get the processes associated with the tabs by only returning those that did not have the MainWindowTitle property set:

$iethreads = get-process iexplore |?{!$_.MainWindowTitle} |%{$_.ID}
# ? stands for Where-Object
# % stands for foreach

(It’s really unfortunate that MainWindowTitle didn’t just have my answer, but where would the fun be then?)

To take this to the next level I figured that there would be some way of getting the HWND of the process, and then perhaps a way to inspect that HWND for more information.  If you never coded in C++ or used old-school non-.NET Windows programming the term HWND is probably new to you.  Put simply it is a reference to a window in the Windows operating systems.  It is called the handle to the window or the window’s handle.

I found this post that discusses a method in C++ to handle (no pun intended) the first part of what I thought would be the final solution, i.e., getting the HWND associated with a PID.  The technique uses a few functions in user32.dll.  Here’s the general overview:

  1. Get the HWND for the first window using GetTopWindow
  2. Get the PID associated with the thread by using GetWindowThreadProcessID
  3. Get the HWNDs for the remaining Windows by using GetNextWindow (which really translates to using GetWindow against the last HWND and the uint value of GW_HWNDNEXT as the second parameter)
  4. Use the same method from step 2 to geth the PID associated with each HWND returned by step 3

While exploring user32.dll on Pinvoke.Net I decided to look more closely at the other functions to see if I could find the final piece that would tell me what the window associated with the HWND was.  I found a function called GetWindowsText that would tell me the title of the Window.  This is exactly what process explorer gets you when you hover over iexplore.exe so I knew I found the needle I was looking for.

I imported GetWindowsText and GetWindowsTextLength (needed in order to obtain and pass the proper value to the maxCount parameter of GetWindowsText).  I found that it was successful, but I was receiving more titles than I wanted.  I saw things like GDI+,SysFader, MSCTFIME UI, and Default IME.  These are all components of the IE Window, but they were unnecessary to achieving my end goal of finding the title of the tab.  Fortunately, I found that all of the values I was interested in ended with the words “Windows Internet Explorer”.  I added a quick regex to filter for this, and voila!  Script complete.  I should note that my company also brands its browser so the regex might need to be tweaked accordingly, e.g., “Windows Internet Explorer$|CompanyName IE$”.  You might also want to use a smaller regex – something like “win” rather than “Windows Internet Explorer$” because it is possible for the title to get truncated.  The method is not foolproof, but it’s a great start. After some thorough testing I have determined that the SysFader, MSCTFIME UI, and Default IME are better to test against.  I want everything that is not one of these.  The truncation of titles became a bigger problem.  The new regex to match is: ’^MSCTFIME UI$|^Default IME$|^SysFader$|^MCI command handling window$’.  At the moment I cannot duplicate the GDI+ window I was getting.  Please post comments if you find any more I should be excluding in this regex.

So, here it is in all it’s glory!  The below code will spit out PowerShell objects with a property for the PID and a property for the title:

$sig = @"
[DllImport("user32.dll", SetLastError=true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

[DllImport("user32.dll")]
public static extern IntPtr GetTopWindow(IntPtr hWnd);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);

public enum GetWindow_Cmd : uint {
    GW_HWNDFIRST = 0,
    GW_HWNDLAST = 1,
    GW_HWNDNEXT = 2,
    GW_HWNDPREV = 3,
    GW_OWNER = 4,
    GW_CHILD = 5,
    GW_ENABLEDPOPUP = 6
}

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
public static extern int GetWindowTextLength(IntPtr hWnd);
"@
Add-Type -MemberDefinition $sig -Namespace User32 -Name Util -UsingNamespace System.Text

$iethreads = get-process iexplore |?{!$_.MainWindowTitle} |%{$_.ID}

$p=0
$window = [User32.Util]::GetTopWindow(0)

while ($window -ne 0) {
    [User32.util]::GetWindowThreadProcessId($window, [ref]$p) |out-null
    if ($iethreads -contains $p) {
        $length = [User32.Util]::GetWindowTextLength($window)
        if ($length -gt 0) {
            $string = New-Object System.Text.Stringbuilder 1024
            [User32.Util]::GetWindowText($window,$string,($length+1)) |out-null
            if ($string.tostring() -notmatch '^MSCTFIME UI$|^Default IME$|^SysFader$|^MCI command handling window$') {
                new-object psobject -Property @{PID = $p;Title = $string.tostring()}
            }
        }
    }
    $window = [User32.Util]::GetWindow($window, 2)
}

Because the script returns PowerShell objects you can easily pipe the script into any of your favorite export or formatting functions. I personally love Out-GridView (shown below):

That’s it! Thanks for coming along with me! I’m happy to give such an in-depth technical article on the anniversary of the site!

Advertisements

5 responses to “Finding the thread (PID) that belongs to a tab in IE 8 with PowerShell

  1. Pingback: Episode 131 – MVP Sean Kearney on the Mic « PowerScripting Podcast

  2. Richard Howells January 29, 2012 at 6:45 am

    Fabulous script. Thanks for sharing it.

  3. orbtax June 12, 2012 at 3:18 pm

    Really nice powershell example code. I am looking forward to future posts like this.

  4. Dat October 30, 2013 at 6:11 pm

    Hi Tome,
    Is it possilbe to Activate/Bring it to Font (IE Tab) Once you have found the PID from iexplore thread?

    $sig = ‘[DllImport(“user32.dll”)] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);’
    Add-Type -MemberDefinition $sig -name NativeMethods -namespace Win32

    $PID = 1184
    [Win32.NativeMethods]::ShowWindowAsync($PID, 1)

    Could you show me how.
    Thanks.

    • Dat October 30, 2013 at 6:26 pm

      Hi Tome,

      I’ve also tried (powershell community extensions), but the IE tab does not switch.
      Ipmo c:\program files\powershell community extensions\pscx3\pscx\pscx.psd1
      $PID = 1184
      Set-ForegroundWindow (Get-Process -id $PID).MainWindowHandle

      Thanks.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: