首页 文章

如何防止WMI配额溢出?

提问于
浏览
0

我正在使用C#应用程序来监视从特定文件夹启动的进程,我正在使用WMI进行监视 . 我的WMI查询就像

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

我将参数替换为我所关注的文件夹的路径 . WMI查询工作正常,我订阅事件通知,以便在特定文件夹的进程出现时执行一些额外的处理 . 监控工具运行良好数小时后,我开始在我的应用程序中获得 WMI QuotaViolation 异常 . 一旦发生这种情况,我需要重新启动 Windows Management Instrumentation 服务以使工作正常 . 我最初使用的是

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

查询然后检查事件通知中的进程文件夹,查询中的修改已完成,希望它会减少结果集,从而防止配额违规 .

有没有办法定期刷新WMI配额或任何其他方法,我可以阻止QuotaViolation?处理QuotaViolation方案的最佳方法是什么?

Edit: 这是我的进程观察对象:

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();
        }
    }

我在app启动时在viewmodel构造函数中创建一个 ProcessWatcher 对象,如:

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

如果我尝试再次启动它而不重新启动WMI,则启动调用是 QuotaViolation 被引发的位置 . 在app退出时,我正在处理 ProcessWatcher 对象,如:

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

相关堆栈跟踪是:

异常InnerException [System.Management.ManagementException:配额违规

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

在System.Management.ManagementEventWatcher.Start()

在App.ProcessTabViewModel1..ctor()

1 回答

  • 3

    System.Management.ManagementException:配额违规

    是的,这发生了 . 在添加缺失的部分之后,我根据您的代码段编写了一个小测试程序:

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

    KABOOM!使用与您引用的完全相同的堆栈跟踪 . 它在ix == 76时结束 . 换句话说,此查询的WMI配额为75.在Windows 8.1中测试 . 感觉很对,这是一个非常昂贵的查询,也没有太快 .

    您将不得不从根本上做到这一点,只创建 one 查询 . 一个就够了,你可能会因为许多文件夹这样做而遇到麻烦 . 以不同的方式进行攻击,在获得事件时进行自己的过滤 . 一个粗略的例子(我没有完全得到你想做的过滤):

    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;
    }
    

相关问题