Thursday, March 15, 2007

Whats Behind W3WP Number Three?

Why?
I mean, they've got different names, different id's maybe even different users, so why on earth does IIS 6.0 makes it so hard to know which w3wp.exe process is running which Application pool?!

Although Microsoft offers us the IISApp.vbs script - which does exactly what I want (and even allows you to do certain actions like pool recycling etc.), this script works only on the current machine and has no support for remote servers.

The quickest - yet dirtiest solution would be to run the script remotely - whether it's by using PSEXEC, or through telnet - it's all the same, but not really recommended, especially not on production...

So, what can we do?

WMI to the Rescue!

After some work, I've found out that each w3wp process hosting an Application pool has some very interesting command-line arguments, I'll discuss these arguments in the future but right now the important thing is - the last argument w3wp gets is - hold your breath - the AppPool name it runs! hooray!

Here's a little code sample on how to implement this using .Net:


using System.Management; // Add reference to System.Management

/* A simple Value Object representing one Application Pool */
public class AppPool()
{
    public int ProcessId;
    public string Name;
    public long UsedMemory;
}

/* Gets remote server's name, returns list of all AppPools */
private List<AppPool> getAppPools(string serverName)
{
    ManagementScope scope;

    /* Change the query to select * if you want more details */
    SelectQuery query = new SelectQuery(
"SELECT ProcessId, CommandLine, WorkingSetSize FROM Win32_Process Where Name = 'w3wp.exe'");

    ManagementObjectSearcher searcher;
    List<AppPool> appPools = new List<AppPool>();

    /* If you do not have sufficient rights, impersonate */
    ConnectionOptions options = new ConnectionOptions();
    options.Username = @"MyDomain\admin";
    options.Password = "pwd";
    options.EnablePrivileges = true;

    scope = new ManagementScope(
        @"\\" + serverName + @"\root\cimv2", options);
    scope.Connect();

    searcher = new ManagementObjectSearcher(scope, query);

    foreach (ManagementObject mob in searcher.Get())
    {
      AppPool appPool = new AppPool();
      PropertyData property = mob.Properties["CommandLine"];

      int pos;

      /* Now we parse the AppPool name from the args */
      appPool.Name = property.Value.ToString();
      pos = appPool.Name.Length - 1;
      appPool.Name = appPool.Name.Substring(0, pos);
      pos = appPool.Name.LastIndexOf("\"") + 1;
      appPool.Name = appPool.Name.Substring(pos);

      /* And find the processId for this AppPool */
      property = mob.Properties["ProcessId"];
      appPool.ProcessId = Convert.ToInt32(property.Value);

      /* General process info, i.e: memory usage */
      property = mob.Properties["WorkingSetSize"];
      appPool.UsedMemory = int.parse(property.Value.ToString());

      /* Convert from Bytes, to MBs */
      appPool.UsedMemory = appPool.UsedMemory / 1048576;

      appPools.Add(appPool);

    }

    return appPools;

}


Although I've written this code in .Net 2.0, except for the Generics, I think it should work on 1.x too...

1 comment:

Anonymous said...

It is very a pity to me, that I can help nothing to you. I hope, to you here will help.