Search code examples
c#scopestaticwmi

C# problem with Scopes and static Functions


I am writing a program, that will expand/minimize depending on if another process (Game Client), is opened/terminated. It is going to be a login helper, which shows the accounts on a windows form.

For monitoring if the process is opened, I use WMI for C#. Here is my Code to check the status of the Client:

public static int Main(string[] args)
    {
        // query for filtering which process is montitored.
        string pol = "2";
        string queryString =
            "SELECT *" +
            "  FROM __InstanceOperationEvent " +
            "WITHIN  " + pol +
            " WHERE TargetInstance ISA 'Win32_Process' " +
            "   AND TargetInstance.Name = 'RiotClientCrashHandler.exe'"; // look for that process every 2 seconds.
        string scope = @"\\.\root\CIMV2";

        ManagementEventWatcher startWatch = new ManagementEventWatcher(scope, queryString);
        startWatch.EventArrived += new EventArrivedEventHandler(startWatch_EventArrived); // event handler
        startWatch.Start();

        ManagementEventWatcher stopWatch = new ManagementEventWatcher(scope, queryString);
        stopWatch.EventArrived += new EventArrivedEventHandler(stopWatch_EventArrived); // event handler for terminating
        stopWatch.Start();

        Console.WriteLine("Press any key to exit");
        while (!Console.KeyAvailable) System.Threading.Thread.Sleep(50); // wait loop.

        startWatch.Stop();
        stopWatch.Stop();
        return 0;
    }

    
    static void stopWatch_EventArrived(object sender, EventArrivedEventArgs e)
    {
        Console.WriteLine("Process Stopped! Riot Client");
    }

    
    static void startWatch_EventArrived(object sender, EventArrivedEventArgs e)
    {
        Console.WriteLine("Process opened! Riot Client");
    }

Pretty basic procedure. Two callback functions for the events, that the process is started/terminated.

the problem

Instead, of calling the two events once, the client seems to restart the processes every second. In a matter of a few seconds it repeats the call: Console App Behavior

I wanted to implement a timer with the timer class:

        Timer timer = new Timer();
        timer.Elapsed += new ElapsedEventHandler(ClosingEvent);
        timer.Interval = 3000;
        timer.Start();

It should wait for a set time, after the closed event is called (meaning that the process must be complelety terminated). And reset if the opened event is called.

I am relatively new to C# from C/Python/Javascript so dont be to mean with me. It seems that I cannot declare a global variable here so I am kind of confused how to solve this. I cannot call a non static function out of a static function as well. Is there a way to solve this?

  1. Should I solve this even with a timer?
  2. Is there a different approach using the WMI?

Solution

  • A few things for this. Using the __InstanceOperationEvent class is going to give you all create, delete, and modification events for the namespace. This means any time any property on the process instance changes, such as memory (which happens frequently), you'll receive a event to be handled. That's why you are getting so many events being raised.

    The other thing I noticed is that your startWatch and stopWatch code share the same query. This means that you are going to receive duplicate events anyway even if the WMI events were only being raised on creation and deletion. You can combine these into one object and event handler then handle which kind of event is being processed in your code like below.

        public static int Main(string[] args)
        {
            // query for filtering which process is montitored.
            string pol = "2";
            string queryString =
                "SELECT *" +
                "  FROM __InstanceOperationEvent " +
                "WITHIN  " + pol +
                " WHERE TargetInstance ISA 'Win32_Process' " +
            "   AND TargetInstance.Name = 'RiotClientCrashHandler.exe'"; // look for that process every 2 seconds.
            string scope = @"\\.\root\CIMV2";
    
            ManagementEventWatcher processWatcher = new ManagementEventWatcher(scope, queryString);
            processWatcher.EventArrived += new EventArrivedEventHandler(ProcessEventArrived); // event handler
            processWatcher.Start();
    
            Console.WriteLine("Press any key to exit");
            while (!Console.KeyAvailable) System.Threading.Thread.Sleep(50); // wait loop.
    
            processWatcher.Stop();
            return 0;
        }
    
    
        static void ProcessEventArrived(object sender, EventArrivedEventArgs e)
        {
            // Check the event arguments to see if the event being raised is creation or deletion
            switch (e.NewEvent.ClassPath.ClassName)
            {
                case "__InstanceCreationEvent":
                    Console.WriteLine("Process opened! Riot Client");
                    break;
                case "__InstanceDeletionEvent":
                    Console.WriteLine("Process Stopped! Riot Client");
                    break;
                default:
                    break;
            }
        }
    

    All that being said, if possible, try to use the event class Win32_ProcessTrace for increased performance benefits. This class is designed to only handle process start and stops so its leaner than looking for WMI instance modifications. The only drawback is that it requires elevated privileges (admin rights) which may not be possible in your environment.