C# 中的 WMI 无效查询,但在 wbemtest 实用程序中有效
Posted
技术标签:
【中文标题】C# 中的 WMI 无效查询,但在 wbemtest 实用程序中有效【英文标题】:WMI Invalid Query in C# but works in wbemtest utility 【发布时间】:2017-01-11 17:56:01 【问题描述】:我正在尝试创建一个 WQLEventQuery 以在 C# 小程序中运行,该小程序在指定文件夹中创建新文件夹时触发。我以前使用过 WMI,我熟悉它的工作原理。当将新文件添加到特定文件夹时,我成功地创建了相同类型的事件查询。奇怪的是,在调试中运行小程序时出现异常,但如果我采用相同的查询并从 Windows 内置的“wbemtest”实用程序运行它,则在删除时不会抛出“无效查询”在子句内。但是,如果我没有在 C# 代码中对 WQLEventQuery 对象设置 WithinInterval 属性,则会引发与所需轮询间隔相关的不同异常。以下是一些用于上下文的代码 sn-ps:
FolderMonitor.cs
using System;
using System.Configuration;
using System.Management;
using MyProject.core.interfaces;
namespace MyProject.monitor.WmiEventMonitors
public class FolderMonitor : IWQLMonitor
private const string _eventClassName = "__InstanceCreationEvent";
private const string _isaType = "Win32_SubDirectory";
private readonly IEventListenerManager _eListenerManager;
private readonly IFileProcessService _fileProcessService;
public WqlEventQuery Query get;
public string Path get;
public FolderMonitor(string path, IEventListenerManager eListenerManager, IFileProcessService fileProcessService)
_eListenerManager = eListenerManager;
_fileProcessService = fileProcessService;
if (string.IsNullOrEmpty(path))
path = GetConfiguredDirectory();
Path = path;
var queryParamPath = path.Replace(@"\", @"\\");
//Query = new WqlEventQuery();
//Query.QueryString = $@"Select * From _eventClassName Within 1 Where TargetInstance Isa '_isaType' And TargetInstance.GroupComponent = 'Win32_Directory.Name=queryParamPath'";
Query = new WqlEventQuery
EventClassName = _eventClassName,
Condition = $"TargetInstance isa '_isaType' And TargetInstance.GroupComponent = 'Win32_Directory.Name=queryParamPath'"
WithinInterval = new TimeSpan(0,5,0)
;
public void HandleEvent(object sender, EventArrivedEventArgs e)
// when a new subfolder is created:
// 1) Log it somewhere?
// 2) Create a new WMI listener for that subfolder to detect file creation
string newDirPath = null;
try
foreach (PropertyData pd in e.NewEvent.Properties)
if (pd.Name != "TargetInstance") continue;
ManagementBaseObject mbo;
if ((mbo = pd.Value as ManagementBaseObject) != null)
using (mbo)
var newSubDir = mbo.Properties["PartComponent"];
var newDir = newSubDir.Value as ManagementBaseObject;
newDirPath = $"newDir.Properties["Drive"].ValuenewDir.Properties["Path"].Value";
catch (Exception ex)
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
throw;
if (!string.IsNullOrEmpty(newDirPath))
var newFileMonitorEvent = new FileMonitor(newDirPath, _fileProcessService);
_eListenerManager.Add(newFileMonitorEvent);
private static string GetConfiguredDirectory()
return ConfigurationManager.AppSettings["Directory"].Trim();
我的活动报名课
using System;
using System.Management;
using MyProject.monitor.WmiEventMonitors;
namespace MyProject.monitor
public interface IFileMonitorEventRegistrar
ManagementEventWatcher RegisterEventListener(IWQLMonitor newMonitorCandidate);
bool UnregisterEventListener(ManagementEventWatcher listener);
public class FileMonitorEventRegistrar : IFileMonitorEventRegistrar
public ManagementEventWatcher RegisterEventListener(IWQLMonitor newMonitorCandidate)
var scope = WmiUtility.GetConnectionScope();
ManagementEventWatcher watcher = null;
try
watcher = new ManagementEventWatcher(scope, newMonitorCandidate.Query);
watcher.EventArrived += new EventArrivedEventHandler(newMonitorCandidate.HandleEvent);
watcher.Start();
catch (Exception e)
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
throw;
return watcher;
public bool UnregisterEventListener(ManagementEventWatcher listener)
listener.Stop();
listener.Dispose();
return true;
我的 WMI 实用程序类
using System;
using System.Management;
namespace MyProject.monitor
public static class WmiUtility
private static ManagementScope _connectionScope;
private static ConnectionOptions _connectionOptions;
public static ManagementScope GetConnectionScope()
EstablishConnection(null, null, null, Environment.MachineName);
return _connectionScope;
private static ConnectionOptions SetConnectionOptions()
return new ConnectionOptions
Impersonation = ImpersonationLevel.Impersonate,
Authentication = AuthenticationLevel.Default,
EnablePrivileges = true
;
private static ManagementScope SetConnectionScope(string machineName, ConnectionOptions options)
ManagementScope connectScope = new ManagementScope();
connectScope.Path = new ManagementPath(@"\\" + machineName + @"\root\CIMV2");
connectScope.Options = options;
try
connectScope.Connect();
catch (ManagementException e)
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
throw;
return connectScope;
private static void EstablishConnection(string userName, string password, string domain, string machineName)
_connectionOptions = SetConnectionOptions();
if (domain != null || userName != null)
_connectionOptions.Username = domain + "\\" + userName;
_connectionOptions.Password = password;
_connectionScope = SetConnectionScope(machineName, _connectionOptions);
我的 EventQuery 管理器类
using System;
using System.Collections.Generic;
using System.Management;
using MyProejct.monitor.WmiEventMonitors;
namespace MyProject.monitor
public interface IEventListenerManager : IDisposable
IDictionary<string, ManagementEventWatcher> RegisteredEvents get;
bool Add(IWQLMonitor eListener);
bool Remove(string monitoredPath);
public class EventListenerManager : IEventListenerManager
private bool _disposed;
private readonly IFileMonitorEventRegistrar _eventRegistrar;
public IDictionary<string, ManagementEventWatcher> RegisteredEvents get;
public EventListenerManager(IFileMonitorEventRegistrar eventRegistrar)
_eventRegistrar = eventRegistrar;
RegisteredEvents = new Dictionary<string, ManagementEventWatcher>();
public bool Add(IWQLMonitor eListener)
RegisteredEvents.Add(eListener.Path, _eventRegistrar.RegisterEventListener(eListener));
return true;
public bool Remove(string monitoredPath)
if (RegisteredEvents.ContainsKey(monitoredPath))
_eventRegistrar.UnregisterEventListener(RegisteredEvents[monitoredPath]);
return RegisteredEvents.Remove(monitoredPath);
return true;
protected virtual void Dispose(bool disposing)
if (!_disposed)
if (disposing)
foreach (var item in RegisteredEvents)
Remove(item.Key);
_disposed = true;
public void Dispose()
Dispose(true);
编排器类
using System;
using System.Configuration;
using System.IO;
using System.Linq;
using MyProejct.monitor.WmiEventMonitors;
using MyProject.core.interfaces;
namespace MyProject.monitor
public interface IFileMonitorService : IDisposable
void Initialize();
public class FileMonitorService : IFileMonitorService
private bool _disposed;
private readonly IEventListenerManager _eventListenerManager;
private readonly IFileMonitorEventRegistrar _eventRegistrar;
private readonly IFileProcessService _fileProcessService;
private string _parentDirectory;
public FileMonitorService(IFileMonitorEventRegistrar eventRegistrar, IFileProcessService fileProcessService)
_eventRegistrar = eventRegistrar;
_fileProcessService = fileProcessService;
_eventListenerManager = new EventListenerManager(_eventRegistrar);
public void Initialize()
if (string.IsNullOrEmpty(_parentDirectory))
_parentDirectory = ConfigurationManager.AppSettings["SFTPDirectory"].Trim();
if (!_eventListenerManager.RegisteredEvents.Any())
GenerateFileEventListeners();
GenerateParentFolderListener();
public void GenerateFileEventListeners()
if (!Directory.Exists(_parentDirectory))
return;
var foldersToMonitor = Directory.EnumerateDirectories(_parentDirectory);
foreach (var folderPath in foldersToMonitor)
// Create a listener
var fileMonitor = new FileMonitor(folderPath, _fileProcessService);
_eventListenerManager.Add(fileMonitor);
public void GenerateParentFolderListener()
var folderMonitor = new FolderMonitor(_parentDirectory, _eventListenerManager, _fileProcessService);
_eventListenerManager.Add(folderMonitor);
public virtual void Dispose(bool disposing)
if (!_disposed)
if (disposing)
_eventListenerManager.Dispose();
_parentDirectory = null;
_disposed = true;
public void Dispose()
Dispose(true);
所以查询字符串本质上是 "select * from __InstanceCreationEvent within 1 where TargetInstance isa 'Win32_SubDirectory' And TargetInstance.GroupComponent = 'Win32_Directory.Name=C:\\MonitoredDocs'"
如果采用该查询字符串并删除 inside 子句,wbemtest 将其作为有效的 WMI 查询接受。当出现with子句时,它表示这是一个无效查询。我正在使用来自this article 的答案。任何帮助弄清楚如何让这个 WQL 事件查询工作的帮助将不胜感激。
【问题讨论】:
【参考方案1】:因此,我更加专注地阅读了我在上面发布的文章,并找到了一个实际有效的替代查询。查询字符串如下所示:
Select * From __InstanceCreationEvent Within 1 Where TargetInstance Isa 'Win32_Directory' And TargetInstance.Drive = 'C:' And TargetInstance.Path = '\\path\\to\\monitored\\directory\\'
*一个可能令人困惑的细节是路径值必须包含结尾的“\\”
在代码方面,我修改后的 FolderMonitor.cs 类如下所示:(更改标记为 *!*!*)
public class FolderMonitor : IWQLMonitor
private const string _eventClassName = "__InstanceCreationEvent";
*!*!*private const string _isaType = "Win32_Directory";*!*!*
private readonly IEventListenerManager _eListenerManager;
private readonly IFileProcessService _fileProcessService;
public WqlEventQuery Query get;
public string Path get;
public FolderMonitor(string path, IEventListenerManager eListenerManager, IFileProcessService fileProcessService)
_eListenerManager = eListenerManager;
_fileProcessService = fileProcessService;
if (string.IsNullOrEmpty(path))
path = GetConfiguredDirectory();
Path = path;
*!*!*var drive = Path.Substring(0, 2);*!*!*
*!*!*var queryPath = Path.Substring(2) + @"\";*!*!*
var queryParamPath = queryPath.Replace(@"\", @"\\");
Query = new WqlEventQuery
EventClassName = _eventClassName,
*!*!*Condition = $"TargetInstance Isa '_isaType' And TargetInstance.Drive = 'drive' And TargetInstance.Path = 'queryParamPath'",*!*!*
WithinInterval = new TimeSpan(0,0,1)
;
【讨论】:
以上是关于C# 中的 WMI 无效查询,但在 wbemtest 实用程序中有效的主要内容,如果未能解决你的问题,请参考以下文章
C# - Windows 服务 - 远程 WMI 查询抛出错误:找不到 RPC
为啥强制转换 SQL MAX(DateTime) 命令在 SSMS 中有效,但在 C# 中无效? [复制]