【问题标题】:How to prevent WMI quotas from overflowing?如何防止 WMI 配额溢出?
【发布时间】:2014-12-01 11:41:02
【问题描述】:

我正在使用 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 场景的最佳方法是什么?

编辑: 这是我的进程观察对象:

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

我在应用程序启动时创建了一个ProcessWatcher 对象,在如下视图模型构造函数中:

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

如果我尝试在不重新启动 WMI 的情况下第二次启动它,则启动调用是引发 QuotaViolation 的地方。 在应用程序退出时,我正在处理 ProcessWatcher 对象,例如:

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

相关堆栈跟踪是:

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

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

在 System.Management.ManagementEventWatcher.Start()

在 App.ProcessTabViewModel1..ctor()

【问题讨论】:

  • 听起来你在某处缺少 Dispose / Close call ...
  • @SoMoS 我的事件观察者会在应用程序的整个生命周期中保持不变,并且我会在应用程序关闭时处理它们。但这是在应用程序的一次运行中发生的。这种方法有什么问题吗?
  • 您做错的可能性很高,我需要查看异常的 sn-p 和堆栈跟踪。遇到难以解释的系统错误时的预期文档。
  • 我不知道怎么刷,但是可以增加配额:support2.microsoft.com/kb/2404366
  • 增加配额可能只会隐藏真正的问题(更长一段时间)。很有可能,您在 WMI 中遇到了内存泄漏。如果可以,请搜索已知问题/修补程序,或联系 Microsoft 支持。

标签: c# wmi wmi-query


【解决方案1】:

System.Management.ManagementException:违反配额

是的,会发生这种情况。添加缺失部分后,我根据您的 sn-p 编写了一个小测试程序:

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

咔嚓!使用与您引用的完全相同的堆栈跟踪。它在 ix == 76 处失败。换句话说,此查询的 WMI 配额为 75。在 Windows 8.1 中测试。感觉差不多,这是一个非常昂贵的查询,也不会太快。

您将不得不做这完全不同的事情,只创建 一个 查询。一个就足够了,您可能会因为对 许多 文件夹执行此操作而遇到麻烦。以不同的方式进行攻击,当您收到事件时进行自己的过滤。一个粗略的例子(我没有完全得到你想要做的过滤):

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

【讨论】:

  • 非常感谢我的代码中的努力、解释和缺失的部分。我认为我应该以不同的方式解决它。我试图实现的过滤是,当启动来自特定文件夹的进程时,我想要一个事件。而且我没有太多,但我有大约 3 到 4 个 ProcessWatchers 同时运行。但似乎我仍然超出了配额限制。我应该尝试不同的方法。
猜你喜欢
  • 2020-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多