简体   繁体   中英

How to prevent WMI quotas from overflowing?

I am using a C# application to monitor the processes launched from a particular folder, and I am using WMI for monitoring. My WMI query is like

SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.ExecutablePath LIKE '{0}%'

where I substitute the parameter with the path to the folder which I am intereseted. The WMI query is working fine and I am subscribing to the event notifications to do some additional processsing when a process from the particular folder comes up. The monitoring tool runs fine for hours after which I start getting a WMI QuotaViolation exception in my app. Once this happens I need to restart the Windows Management Instrumentation service to get the thing working. I was initially using a

`SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'`

query and then checking the processes folder in the event notification, the modification in the query was done hoping it would reduce the result set and therefore prevent the Quota Violation.

Is there any way to flush the WMI quotas periodically or any other method whereby I can prevent the QuotaViolation? What is the best way to handle a QuotaViolation scenario?

Edit: This is my process watcher object :

public class ProcessWatcher : ManagementEventWatcher
{

    private string folder = "";

    // Process Events
    public event ProcessEventHandler ProcessCreated;  //notifies process creation
    //add any more event notifications required here

    // WMI WQL process query strings
    static readonly string WMI_OPER_EVENT_QUERY = @"SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'";
    static readonly string WMI_OPER_EVENT_QUERY_WITH_PROC =
        WMI_OPER_EVENT_QUERY + " and TargetInstance.Name = '{0}'";

    public ProcessWatcher(string basepath)
    {
        folder = basepath;
        Init(string.Empty);
    }

    public ProcessWatcher(string processName, string basepath)
    {
        folder = basepath;
        Init(processName);
    }

    private void Init(string processName)
    {
        this.Query.QueryLanguage = "WQL";
        if (string.IsNullOrEmpty(processName))
        {
            this.Query.QueryString = string.Format(WMI_OPER_EVENT_QUERY + @" AND TargetInstance.ExecutablePath LIKE '{0}%'", folder.Replace(@"\",@"\\")) ;
        }
        else
        {
            this.Query.QueryString =
                string.Format(WMI_OPER_EVENT_QUERY_WITH_PROC, processName);
        }

        this.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
    }

    private void watcher_EventArrived(object sender, EventArrivedEventArgs e)
    {
        try
        {
            ManagementBaseObject mObj = e.NewEvent["TargetInstance"] as ManagementBaseObject;
            if (mObj != null)
            {
                Win32_Process proc = new Win32_Process(mObj);
                if (proc != null)
                {
                    folder = folder.ToLower() ?? "";
                    string exepath = (string.IsNullOrEmpty(proc.ExecutablePath)) ? "" : proc.ExecutablePath.ToLower();
                    if (!string.IsNullOrEmpty(folder) && !string.IsNullOrEmpty(exepath) && exepath.Contains(folder))
                    {
                        if (ProcessCreated != null) ProcessCreated(proc);
                    }
                }
                proc.Dispose();
            }
            mObj.Dispose();
        }
        catch(Exception ex) { throw; }
        finally
        {
            e.NewEvent.Dispose();
        }
    }

I create a ProcessWatcher object at app startup, in a viewmodel constructor like :

        watch = new ProcessWatcher(BasePath);
        watch.ProcessCreated += new ProcessEventHandler(procWatcher_ProcessCreated);
        watch.Start();

The start call is where the QuotaViolation is raised if I try to start it a second time without restarting WMI. At app exit, I am disposing off the ProcessWatcher object like :

watch.Stop();
watch.Dispose();

The Relevant Stack trace is :

Exception InnerException [System.Management.ManagementException: Quota violation

at System.Management.ManagementException.ThrowWithExtendedInfo(ManagementStatus errorCode)

at System.Management.ManagementEventWatcher.Start()

at App.ProcessTabViewModel1..ctor()

System.Management.ManagementException: Quota violation

Yes, that happens. I wrote a little test program, based on your snippet after adding the missing pieces:

    static void Main(string[] args) {
        for (int ix = 0; ix < 1000; ++ix) {
            var obj = new ProcessWatcher("");
            obj.ProcessCreated += obj_ProcessCreated;
            obj.Start();
        }
    }

Kaboom! With the exact same stack trace as you quoted. It conked out at ix == 76. In other words, the WMI quota for this query is 75. Tested in Windows 8.1. Feels about right, this is a very expensive query, none too fast either.

You are going to have to do this fundamentally different, create only one query. One is enough, you probably got into trouble by doing this for many folders. Attack that differently, do your own filtering when you get the event. A rough example (I didn't quite get the filtering you want to do):

public class ProcessWatcher2 : IDisposable {
    public delegate void ProcessCreateEvent(string name, string path);
    public event ProcessCreateEvent ProcessCreated;

    public ProcessWatcher2(string folder) {
        this.folder = folder;
        lock (locker) {
            listeners.Add(this);
            if (watcher == null) Initialize();
        }
    }

    public void Dispose() {
        lock (locker) {
            listeners.Remove(this);
            if (listeners.Count == 0) {
                watcher.Stop();
                watcher.Dispose();
                watcher = null;
            }
        }
    }

    private static void Initialize() {
        var query = new WqlEventQuery(@"SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'");
        watcher = new ManagementEventWatcher(query);
        watcher.EventArrived += watcher_EventArrived;
        watcher.Start();
    }

    private static void watcher_EventArrived(object sender, EventArrivedEventArgs e) {
        using (var proc = (ManagementBaseObject)e.NewEvent["TargetInstance"]) {
            string name = (string)proc.Properties["Name"].Value;
            string path = (string)proc.Properties["ExecutablePath"].Value;
            lock (locker) {
                foreach (var listener in listeners) {
                    bool filtered = false;
                    // Todo: implement your filtering
                    //...
                    var handler = listener.ProcessCreated;
                    if (!filtered && handler != null) {
                        handler(name, path);
                    }
                }
            }
        }
    }

    private static ManagementEventWatcher watcher;
    private static List<ProcessWatcher2> listeners = new List<ProcessWatcher2>();
    private static object locker = new object();
    private string folder;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM