如何防止 WMI 配额溢出
本文关键字:溢出 WMI 何防止 | 更新日期: 2023-09-27 18:25:42
我正在使用 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 配额或任何其他方法,从而防止配额冲突?处理配额冲突情况的最佳方法是什么?
编辑:这是我的进程观察器对象:
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();
启动调用是引发QuotaViolation
的位置,如果我尝试在不重新启动 WMI 的情况下再次启动它。在应用程序退出时,我正在处理ProcessWatcher
对象,例如:
watch.Stop();
watch.Dispose();
相关的堆栈跟踪是:
异常内部异常 [系统.管理.管理异常:配额冲突
at System.Management.ManagementException.ThrowWithExtendedInfo(ManagementStatus errorCode(
at System.Management.ManagementEventWatcher.Start((
at App.ProcessTabViewModel1..ctor((
系统.管理.管理异常:配额冲突
是的,这会发生。 在添加缺失部分后,我根据您的片段编写了一个小测试程序:
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;
}