2014-10-07 58 views
0

我正在使用C#应用程序来监视从特定文件夹启动的进程,并且我正在使用WMI进行监视。我的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)

at System.Management.ManagementEventWatcher.Start()

在App.ProcessTabViewModel1..ctor()

+0

听起来像是你缺少物/关闭通话的地方... – 2014-10-07 09:40:24

+0

@SoMoS我的事件观察仍然在整个应用程序的生命周期当应用程序关闭时我正在处理它们。但是这是在应用程序的一次运行中发生的。这种方法有什么问题吗? – jester 2014-10-07 12:07:27

+1

您做错了的可能性很高,我需要查看异常的片段和堆栈跟踪。预期的文件,当你难以解释系统错误。 – 2014-10-10 09:42:29

回答

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中测试。感觉是正确的,这是一个非常昂贵的查询,也没有太快。

你将不得不这样做根本不同,只创建一个查询。一个就足够了,你可能会因为这个很多文件夹而陷入困境。以不同的方式进行攻击,当你得到事件时做你自己的过滤。一个粗略的例子(我并没有完全得到你想要做的过滤):

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

非常感谢我的代码中的努力,解释和缺失部分。我想我应该以不同的方式解决它。我试图获得的过滤是,我想要一个事件,当一个特定文件夹的进程启动时。我没有太多,但我有大约3到4个ProcessWatchers同时运行。但看来我仍然超出配额限制。我应该尝试一种不同的方法。 – jester 2014-10-14 09:36:09