Share via


Process and ServiceProcess Caching [Robert Villahermosa]

I’ve seen several questions over the last month or two about refreshing the status of certain properties on the Process and ServiceProcess classes.  I’d like to take this opportunity to take a look at an example from both these classes and see how we can resolve these issues.

Issue One: Process.MainWindowHandle resturns 0, even though a window is created

A user found that even though they waited for the UI Window of their application to be displayed, they were getting a MainWindowHandle of 0.  User code may look something like this:

    Process myProc = new Process();    myProc.StartInfo.FileName = "notepad.exe";    myProc.Start();    IntPtr myWinHandle = myProc.MainWindowHandle;    System.Threading.Thread.Sleep(5000);    Console.WriteLine(myWinHandle);

After this executes, myWinHandle has a value of 0, even though the sleep should be sufficient for the Window to pop up.  The first proposed solution I saw was to do the following:

    Process myProc = new Process();    myProc.StartInfo.FileName = "notepad.exe";    myProc.Start();    System.Threading.Thread.Sleep(5000);    IntPtr myWinHandle = myProc.MainWindowHandle;    Console.WriteLine(myWinHandle);

Better, but we still have the problem with the Sleep – we don’t know if the window is up or not when the thread awakes.  It is important to realize that the Process class actually takes a snapshot of Process state and caches it, and does not continually update the state information.  That is why there is a Refresh() method on this class, this should be called if you want updated Process state information.  Also, a better way to determine if the Window has come up is to use the WaitForInputIdle() method.   The fixed code looks something like this:

    Process myProc = new Process();    myProc.StartInfo.FileName = "notepad.exe";    myProc.Start();    myProc.WaitForInputIdle(5000);  //at this point we know the window is up                                    //we could handle the time-out based on                                    //the return value of WaitForInputIdle                                    //it’s omitted for clarity    myProc.Refresh();    IntPtr myWinHandle = myProc.MainWindowHandle;    Console.WriteLine(myWinHandle);

Issue 2: Changing the ServiceName property does not refresh DependentServices/ServicesDependedOn

This customer found that changing the service name didn’t update the DependentServices or ServicesDepended on.  A code snippet for the repro looked something like:

    ServiceController sc = new ServiceController("workstation");    Console.WriteLine("Workstation dependent services:");    foreach (ServiceController dependency in sc.DependentServices)        Console.WriteLine(dependency.ServiceName);    Console.WriteLine();    sc.ServiceName = "alerter";    Console.WriteLine("Alerter dependent services:");    foreach (ServiceController dependency in sc.DependentServices)        Console.WriteLine(dependency.ServiceName);

This would output:

    Workstation dependent services:    RpcLocator    Netlogon    Messenger    Browser    Alerter    Alerter dependent services:    RpcLocator    Netlogon    Messenger    Browser    Alerter

Like Process, ServiceProcess caches this information so Refresh() needs to be called.  In the documentation, we say that calling Refresh() sets the DependentServices and ServicesDependedOn properties to null.  Technically, this is true as in our internal implementation this is indeed what happens.  However, the next time the getter for either of these properties is called, a null check is performed and if either are null we will update them to their current values.  We’ve updated the documentation for the next release to make this clearer.  The fix is one line adding the call to Refresh():

    ServiceController sc = new ServiceController("workstation");    Console.WriteLine("Workstation dependent services:");    foreach (ServiceController dependency in sc.DependentServices)        Console.WriteLine(dependency.ServiceName);    Console.WriteLine();    sc.ServiceName = "alerter";    sc.Refresh();    Console.WriteLine("Alerter dependent services:");    foreach (ServiceController dependency in sc.DependentServices)        Console.WriteLine(dependency.ServiceName);

Comments

  • Anonymous
    October 28, 2006
    Hi, Would the BCL team care to give some behind the scene design commentary on the System.Diagnostics.Process and to explain why this is literally the only way in .Net to launch a .Net Remoting Server and is placed in a System.Diagnostics namespace. Since in .Net you cannot write a COM local server and expecting CoCreateInstance() to launch it, why is this class be placed in a System.Diagnostics namespace and is not in say System namespace? Thanks.

  • Anonymous
    October 31, 2006
    Leon, I asked Brian Grunkemeyer about this and he wasn't sure why Process ended up in System.Diagnostics: "I think the team that came up with most of our diagnostics stuff also came up with the Process class, and it didn’t occur to them to use a different namespace.  We were also changing namespaces left & right in V1, so it was a little chaotic.  We could have moved it somewhere else, if there was an obvious place.  But maybe they just wanted to let the dust settle, then forgot..." Thanks, Justin Van Patten BCL Program Manager

  • Anonymous
    November 01, 2006
    En lisant le post Process and ServiceProcess Caching sur le blog de la team BCL, je me suis rappeler

  • Anonymous
    November 03, 2006
    I feel uncomfortable with the service example. I would never change the ServiceName property to access another service, but create a new ServiceController instance. Furthermore, because ServiceName is writable, I guess Joe Developer would expect that you can change the service's name instead of binding the ServiceController instance to another service.

  • Anonymous
    November 16, 2006
    Hi Thomas, Yes! I was surprised that I saw more than one example of people doing this!  Hence the blog - seeing this made us feel uneasy. To answer Leon's question, one partial explanation of why the Process class is in the Diagnostics namespace is that Process used to have a dependency on Perf Counters, so at the time the namespace made sense.