使用 WMI.NET 提供程序扩展 2.0 编写耦合 WMI 提供程序
使用 WMI.NET 提供程序扩展 2.0 编写耦合 WMI 提供程序
加布里埃尔·吉齐拉
Microsoft Corporation
2008 年 1 月
总结:详细说明如何使用 .NET Framework 3.5 中随附的 WMI.NET 提供程序扩展 2.0 编写耦合 WMI 提供程序
目录
简介
一个简单的 .NET 类
程序集级别属性
类级别 WMI.NET 属性
运行时要求
使用 WMI 注册
通过继承扩展类
实现方法
异常和错误报告
其他提示
结论
列表 1 - SCMInterop.cs
清单 2 – WIN32ServiceHost.cs
简介
Windows Management Instrumentation (WMI) 是一种广泛使用的基础结构,用于管理 Windows 和 Windows 应用程序。 尽管在系统管理员和管理应用程序中非常可扩展且很受欢迎,但由于需要实现的本机接口的复杂性,许多开发人员还是对编写 WMI 提供程序感到冷淡。
虽然初始版本的.NET Framework附带了一组用于实现 WMI 提供程序的对象和模式,但这些对象和模式仅限于应用程序管理,它们不允许你定义方法和实例的密钥是自动生成的。 WMI.NET 提供程序扩展 v2 (WMI.NET) 是 Orcas (.NET Framework 3.5) 中的一个新基础结构,可用于实现一组完整的 WMI 提供程序功能。 此新基础结构与以前版本的 WMI.NET 提供程序模型共存,但它更强大且可扩展。
本文重点介绍如何编写 WMI 耦合提供程序,这是 WMI.NET 最重要的功能之一。 分离提供程序的编写方式没有显著差异,因此本文可以为尝试使用 WMI.NET 编写任何类型的 WMI 提供程序的读者提供良好的开端。 本文将介绍如何从简单的 .NET 类开始创建耦合 WMI 提供程序,然后使用一些额外功能对其进行扩充。 目标是能够枚举托管 Windows 服务的进程,能够枚举每个此类进程中的 Windows 服务,并将此功能集成到 WMI 中。
一个简单的 .NET 类
首先,我们将创建一个 C# 类,用于为托管 Windows 服务的进程建模。 每个实例都会显示关联进程中的托管服务列表。 类具有一个静态方法,该方法返回与系统中运行的服务主机关联的所有实例。
public 类 WIN32ServiceHost
{
类是 Process 类的包装器。 innerProcess 字段将存储对 Process 对象的引用。
Process innerProcess;
类有一个接受进程对象作为参数的构造函数。
public WIN32ServiceHost (Process innerProcess)
{
this.innerProcess = innerProcess;
}
我们包括进程 ID 的访问器。
public int ID
{
get { return this.innerProcess.Id; }
}
Services 属性将返回一个数组,其中包含托管服务的名称。 如果进程中未运行任何服务,则此属性为 null。
public string[] Services
{
get
{
空闲进程不承载任何服务。
如果 (innerProcess.Id == 0)
返回 null;
获取系统上所有 Windows 服务的列表
ServiceController[] services = ServiceController.GetServices () ;
列出<ServiceController> servicesForProcess = new List<ServiceController> () ;
using (SCM scm = new SCM () )
{
for (int svcIndex = 0;svcIndex < 服务。长度;svcIndex++)
{
ServiceController crtService = services[svcIndex];
int processId = scm。GetProcessId (crtService.ServiceName) ;
将运行服务的进程 ID 与当前进程的 ID 进行比较。
if (processId == innerProcess.Id)
{
servicesForProcess.Add (services[svcIndex]) ;
}
}
}
if (servicesForProcess.Count == 0)
返回 null;
准备、填充并返回包含服务名称的数组
string[] servicesNames = new string[servicesForProcess.Count];
for (int serviceIdx = 0;serviceIdx < servicesForProcess.Count;serviceIdx++)
{
servicesNames[serviceIdx] = servicesForProcess[serviceIdx]。ServiceName;
}
return servicesNames;
}
}
EnumerateServiceHosts 是一种静态方法,它将返回 IEnumerable 以遍历所有正在运行的服务主机。
static public IEnumerable EnumerateServiceHosts ()
{
Process[] process = Process.GetProcesses () ;
foreach (进程 crtProcess 中的进程)
{
WIN32ServiceHost crtServiceHost = new WIN32ServiceHost (crtProcess) ;
if (crtServiceHost.Services != null)
{
yield 返回 crtServiceHost;
}
}
}
}
开发人员可以从任何 .NET 应用程序使用此类。 不过,在这种情况下,其他各种管理应用程序将无法使用它。 WMI.NET 具有用于向 WMI 世界公开此示例中的类的挂钩,我们将在以下段落中解释该过程。 WMI 提供了一个模型,允许利用此类对象并将其集成到企业规模管理应用程序(如系统管理服务器或 Operations Manager)中,提供远程交互,并使你能够从多个平台查看和使用此类。
程序集级别属性
使用 WMI.NET 公开程序集以供检测的第一步是在程序集级别设置 WmiConfiguration 属性。 此属性将程序集标记为实现 WMI.NET 提供程序的程序集,并允许配置提供程序实现的类的各种内容,包括将公开这些类的命名空间。 在本示例中,我们将将 WMI 命名空间定义为 root\Test ,并将托管模型设置为 NetworkService 安全上下文中的耦合提供程序模型。 请注意,所有操作都将通过模拟在调用用户的安全上下文中执行。
[assembly: WmiConfiguration (@“root\Test”, HostingModel = ManagementHostingModel.NetworkService) ]
类级别 WMI.NET 属性
若要使用 WMI.NET 检测类,类及其向 WMI.NET 公开的方法、字段和属性需要是公开的,并使用 WMI.NET 属性正确标记。 属性用于生成 WMI 将现有 C# 类用作 WMI 类所需的元数据。
检测 .NET 类
ManagementEntity 属性将 .NET 类标记为正在检测。 在部署时,WMI.NET 基础结构将在 WMI 存储库中生成具有相同名称的相应 WMI 类。 若要修改此名称,需要在 ManagementEntity 属性的参数列表中提供命名参数 Name。 Name 用作命名参数,以更改大多数属性的检测名称。 在我们的示例中,我们选择将 WMI 类命名 为WIN32_ServiceHost 而不重命名 .NET 类。
[ManagementEntity (Name = “WIN32_ServiceHost”) ]
public 类 WIN32ServiceHost
请注意,任何实体的命名可能有点棘手。 C# 区分大小写,但 WMI 不区分大小写。 因此,从 WMI.NET 基础结构的角度来看,所有名称都被视为不区分大小写。 如果属于同一类的两个字段或两个方法的名称仅因大小写而异,则程序集将无法向 WMI 注册。
控制 WMI 类架构
实例的有效负载由其属性提供。 我们需要使用 ManagementProbe、 ManagementConfiguration 或 ManagementKey 属性标记要反映在 WMI 世界中的所有属性。 ManagementProbe 是一个属性,用于将公开的属性标记为只读到 WMI 中。 在我们的示例中,Services 属性是我们要向管理世界公开的有效负载属性。 它显示了无法直接修改的进程状态,因此我们将使用 ManagementProbe 对其进行标记。 对于读/写属性,必须使用 ManagementConfiguration 属性,在这种情况下,属性本身需要同时具有 setter 和 getter。
[ManagementProbe]
public string[] Services
ID 也是 WIN32ServiceHost 有效负载的一部分,因为它标识进程本身。 唯一标识其类实例的属性是 WMI 世界中该类的键。 WMI.NET 中的所有类都需要定义键并实现它们以唯一标识实例,除非它们是抽象的、单一实例或继承自已定义键的类。 密钥使用 ManagementKey 属性进行标记。 在此示例中,进程 ID 唯一标识进程;因此,它唯一标识类的实例。
[ManagementKey]
public int ID
运行时要求
标记类及其属性集合将允许公开要向 WMI 公开的类架构,但不足以让类公开实际数据。 我们需要定义用于获取、创建或删除实例以及枚举实例的入口点。 我们将为实例枚举以及检索特定实例提供代码,该实例实际上是功能 WMI 提供程序的最低要求。
枚举实例
若要枚举实例,WMI.NET 需要一个静态公共方法,而不使用返回 IEnumerable 接口的参数。 它需要是静态的,因为它实现与整个类相关的功能。 此方法需要使用 ManagementEnumerator 属性进行标记。 EnumerateServiceHosts 已在示例中定义,满足所有要求,因此可以仅将其属性化并用于此目的。
[ManagementEnumerator]
static public IEnumerable EnumerateServiceHosts ()
此方法确保枚举中返回的每个元素都是已检测类的实例,这一点非常重要。 否则将导致运行时错误。
绑定到实例
为了能够检索在 WMI.NET) 中称为绑定的特定实例 (,我们需要一个方法,该方法根据标识实例的键的值返回实例。 我们需要一个与键具有相同数量的参数的方法,其参数的名称和类型与键相同。 返回类型应为检测的类本身。 我们将使用静态方法,为了将类的键关联到参数,我们需要为参数指定与键相同的检测名称。 在我们的示例中, ID 是 WIN32_ServiceHost 类的键,我们必须为绑定方法 ID 命名参数,或使用 ManagementName 属性向 WMI 公开名称为“ID”下的参数。 使用 ManagementBind 属性标记绑定方法或构造函数时,WMI.NET 基础结构将识别该绑定方法或构造函数。
[ManagementBind]
static public WIN32ServiceHost GetInstance ([ManagementName (“ID”) ] int processId)
{
尝试
{
Process process = Process.GetProcessById (processId) ;
WIN32ServiceHost crtServiceHost = 新的 WIN32ServiceHost (进程) ;
if (crtServiceHost.Services != null)
{
返回 crtServiceHost;
}
else
{
返回 null;
}
}
如果未找到具有给定 ID 的进程,则由 GetProcessById 引发
catch (ArgumentException)
{
返回 null;
}
}
请务必注意,如果未找到实例,方法将返回 null。 在本例中,如果根本找不到请求的进程,或者找到的进程未托管任何 Windows 服务,我们将执行此操作。
使用 WMI 注册
类已准备好执行其工作,但我们仍需将其注册到 WMI,并将其放置在磁盘上要从其加载的可访问位置。
全局程序集缓存 (GAC) 是我们希望程序集位于的位置。 这样,.NET 就可以按其完整 .NET 名称检索它。 请注意,程序集需要具有在 GAC 中注册的 .NET 强名称。 对于我们的示例,我们将使用 .NET gacutil.exe 工具将程序集存储在 GAC 中。
若要将检测程序集中的信息获取到 WMI 元数据中,需要使用 .NET 工具InstallUtil.exe调用名为 DefaultManagementInstaller 的 WMI.NET 类。 DefaultManagementInstaller 知道如何分析已检测类的整个程序集并生成相应的 WMI 元数据。 由于InstallUtil.exe需要一个用 RunInstaller 属性标记的类,因此我们将定义一个从 DefaultManagementInstaller 派生的空类,InstallUtil.exe将调用该类。
[System.ComponentModel.RunInstaller (true) ]
public 类 MyInstall:DefaultManagementInstaller
{
}
WMI 注册可以脱机或联机完成。 联机注册会将程序集的检测元数据直接存储在 WMI 存储库中。 若要直接将元数据安装到 WMI 存储库中,将使用程序集名称作为参数调用InstallUtil.exe命令。 在执行InstallUtil.exe之前,程序集必须位于 GAC 中。 对于此示例中生成的名为 WMIServiceHost.dll 的程序集,我们将使用以下命令:
C:>gacutil.exe /i WMIServiceHost.dll
C:>Installutil.exe WMIServiceHost.dll
脱机注册需要两个步骤。 第一步是生成与程序集关联的 WMI 元数据,并将其存储在 MOF 文件中。 第二步是使用 mofcomp.exe 工具或作为安装包的一部分在目标计算机上进行实际注册。 脱机注册的优点是可以根据需要本地化和修改 MOF 文件。 对于此示例,可以使用 MOF 参数生成 WMI 元数据并将其存储在名为 WMIServiceHost.mof 的文件中,如下所示:
C:>Installutil.exe /MOF=WMIServiceHost.mof WMIServiceHost.dll
与联机情况一样,程序集必须位于目标计算机上的 GAC 中。 若要验证部署,可以使用 wmic.exe 系统工具来查看此类的实例和值。
C:>wmic /NAMESPACE:\\root\test PATH win32_servicehost get /value
在开发期间,将符号部署到存储程序集的 GAC 中的同一文件夹中很有用。 这样,由于错误或崩溃而报告的任何堆栈都将包括完整的源路径和行号,以帮助识别有问题的代码片段。
通过继承扩展类
WIN32_ServiceHost与服务主机有关,它提供的信息仅限于托管 Windows 服务的进程。 扩展此信息以包含特定于进程的信息(如内存使用情况、可执行路径、会话 ID 等)会很有趣。 若要获取此信息,我们可以扩展架构并编写更多代码来检索所需信息。 编写额外代码的一个不错替代方法是利用在 root\cimv2 命名空间中操作系统上开箱即用的WIN32_Process类,并为系统中运行的任何进程提供所有这些额外信息。 此类提供有关正在运行的进程的大量信息,我们可以使用 WMI 派生来使用我们自己的类扩展它。
WMI 继承在 WMI.NET 编码模型中转换为类继承。 由于要从中派生的类是 WMI 空间中的类,而实际上并未在代码中实现,因此必须以特定方式对其进行标记。
在开始编写派生之前,需要注意 有关WIN32_Process 类的两个重要事项。 第一 个WIN32_Process 位于 root\cimv2 命名空间中,派生要求我们在同一命名空间中注册 win32_servicehost 类。 因此,我们将稍微更改 WmiConfiguration 属性语句。
[assembly: WmiConfiguration (@“root\cimv2”, HostingModel = ManagementHostingModel.NetworkService) ]
此外,超类win32_process,将 属性 Handle 定义为键。 此键的类型CIM_STRING可转换为 。NET 的 System.String。 我们必须停止使用 ID 作为键属性,并改用 Handle 属性。
若要在 WMI.NET 中定义外部类以匹配win32_process只需镜像其架构,但仅包含想要或需要使用的属性。 始终需要类层次结构中的键。 此时 ,Handle 是唯一有趣的属性,因为它是键,我们需要它来绑定到特定实例。
[ManagementEntity (External = true) ]
抽象公共类Win32_Process
{
受保护的字符串句柄;
[ManagementKey]
公共字符串句柄
{
get {
返回 this.handle;
}
}
}
在 ManagementEntity 属性上将 External 设置为 true 会阻止基础结构在部署时生成 WMI 元数据,但在需要查找派生类的键和属性时,会保留声明的信息以供在运行时使用。 请注意,控制基类的键内容非常重要,因为 WMI 子系统使用这些键来合并来自各种提供程序的信息。
若要获取 WMI 类WIN32_ServiceHost继承 WMI 类 Win32Process,我们从 .NET 世界中新建的抽象类派生 WIN32ServiceHost
[ManagementEntity (Name = “WIN32_ServiceHost”) ]
公共类 WIN32ServiceHost:Win32_Process
删除 ID 属性。
[ManagementKey]
public int ID
{
get { return this.innerProcess.Id; }
}
更改构造函数以填充基类的句柄字段中的新键
public WIN32ServiceHost (Process innerProcess)
{
this.innerProcess = innerProcess;
this.handle = innerProcess.Id.ToString () ;
}
修改 GetInstance 以使用名为 Handle 的字符串参数,只是它的前几行,因为其余行保持不变。
[ManagementBind]
静态公共 WIN32ServiceHost GetInstance (字符串句柄)
{
int processId;
如果 (!Int32.TryParse (Handle, out processId) )
{
返回 null;
}
尝试
[...]
我们需要在 GAC 中重新编译和重新部署新程序集。 我们使用 InstallUtil.exe 部署新架构。 我们可以使用略微修改的 wmic.exe 命令查询系统。
C:>wmic /NAMESPACE:\\root\cimv2 PATH win32_servicehost get /value
返回的实例将填充来自两个类的信息,win32_process和 win32_servicehost。 在输出中,服务将来自 win32_servicehost 而其他所有内容都来自 win32_process。 为了简化输出,可以指定所需的列。
C:>wmic PATH win32_servicehost get Handle,Caption,CommandLine,Services /value
当我们尝试枚举win32_process时,它变得更加有趣。 此类查询将返回所有进程,并且仅针对 win32_servicehost 实例填充“服务”字段。
C:>wmic PATH win32_process get /value
输出可能有点让人不知所措,因此只需通过在命令行) 末尾添加 > out.txt,将其转储到 (的文件中,然后在记事本中打开它以搜索 Services 属性。 为了了解所发生的情况,我们可以显示系统属性,以标识每个实例的 WMI 类。
C:>wmic PATH win32_process get Handle,CommandLine,__CLASS /value
从结果列表中选取一个win32_ServiceHost实例并显示其值。
C:>wmic path WIN32_Process.Handle=“536” get /value
可以使用 Windows 脚本、Microsoft PowerShell、托管或本机代码从任何 WMI 客户端应用程序执行类似的操作,系统将按照处理任何其他提供程序的相同方式处理此程序集。
实现方法
WMI.NET 支持静态和每个实例的方法。 在本例中,我们将添加一个方法来停止进程托管的所有服务,以便完全停止进程,而不会在服务仍在运行时将其终止。 为了使公共方法在 WMI 中可见,我们使用 ManagementTask 属性对其进行标记。
[ManagementTask]
public bool StopServices (int millisecondsWait)
{
如果 (innerProcess.Id == 0)
return false;
ServiceController[] services = ServiceController.GetServices () ;
bool oneFailed = false;
using (SCM scm = new SCM () )
{
for (int svcIndex = 0;svcIndex < 服务。长度;svcIndex++)
{
ServiceController crtService = services[svcIndex];
int processId = scm。GetProcessId (crtService.ServiceName) ;
如果 (processId == innerProcess.Id)
{
尝试
{
crtService.Stop () ;
if (millisecondsWait != 0)
{
crtService.WaitForStatus ( ServiceControllerStatus.Stopped,
new TimeSpan ( (long) millisecondsWait * 10000) ) ;
}
}
catch (System.ServiceProcess.TimeoutException)
{
oneFailed = true;
}
catch (System.ComponentModel.Win32Exception)
{
oneFailed = true;
}
catch (InvalidOperationException)
{
oneFailed = true;
}
}
}
}
return !oneFailed;
}
若要调用此方法,需要win32_servicehost 类的实例。 我们获取可用服务主机的列表,键入:
C:>wmic 路径win32_servicehost获取句柄,服务
并选择具有最良性的服务列表 (,以免使系统关闭,此外,某些服务可能也不可停止) 并使用其 Handle 属性来标识调用的实例。
C:>wmic 路径win32_servicehost。Handle=“540” CALL StopServices (0)
异常和错误报告
异常是 WMI.NET 的一个重要方面。 基础结构使用某些异常来传达信息,并将大多数异常视为未经处理。
接受的异常
WMI.NET 只处理本文中将进一步介绍的几个异常。 所有其他异常被视为编程错误,并被视为导致 WMI 提供程序主机崩溃的未经处理的异常。 WMI.NET 在 Windows 事件日志中报告未经处理的异常。
接受的异常实际上会转换为 WMI 错误代码,并发送回客户端代码,如表 1 所示。 因此,WMI.NET 提供程序的行为与任何其他本机提供程序相同。
System.OutOfMemoryException |
WBEM_E_OUT_OF_MEMORY |
System.Security.SecurityException |
WBEM_E_ACCESS_DENIED |
System.ArgumentException |
WBEM_E_INVALID_PARAMETER |
System.ArgumentOutOfRangeException |
WBEM_E_INVALID_PARAMETER |
System.InvalidOperationException |
WBEM_E_INVALID_OPERATION |
System.Management.Instrumentation.InstanceNotFoundException |
WBEM_E_NOT_FOUND |
System.Management.Instrumentation.InstrumentationException |
从内部异常,在文章中进一步介绍 |
表 1 - 将异常转换为 WMI 错误
在上面的列表中,除两个以外,还有两个是常规的 .NET Framework 异常。 可以引发列出的所有异常,以便向客户端报告这些异常所表示的特定状态。
System.Management.Instrumentation 命名空间中添加了两个新异常,进一步说明。
InstanceNotFoundException
此异常用于通知找不到请求的实例。 本文中的示例使用 GetInstance 静态方法绑定到给定实例,如果找不到该实例,则返回 null。 构造函数可用于相同的目的,但当找不到所需的实例时,它需要引发 InstanceNotFoundException 。 下面是用于替换 GetInstance 静态方法的构造函数。
[ManagementBind]
public WIN32ServiceHost (字符串句柄)
{
int processId;
如果 (!Int32.TryParse (Handle, out processId) )
{
引发新的 InstanceNotFoundException () ;
}
尝试
{
Process process = Process.GetProcessById (processId) ;
this.innerProcess = process;
this.handle = 句柄;
如果 (此。Services == null)
{
引发新的 InstanceNotFoundException () ;
}
}
如果未找到具有给定 ID 的进程,则由 GetProcessById 引发
catch (ArgumentException)
{
引发新的 InstanceNotFoundException () ;
}
}
InstrumentationException
InstrumentationException 是处理异常的包装器。 提供程序可以选择通知客户端在其端发生的错误。 为此,它将引发 InstrumentationException。 请注意,开发人员应记住异常会返回到 WMI 系统。 因此,WMI.NET 会尽力将其转换为 COM HRESULT。 若要将精确的错误代码抛回到客户端,我们需要将 InstrumentationException 的内部异常传入 一个允许 直接在基异常类中设置内部 HResult 的 Exception 类。
错误报告
上一节中未列出的任何异常最终都将成为未经处理的异常,这将导致崩溃报告为“ ('System.ExecutionEngineException') 在 wmiprvse.exe[<NNNN>]中发生未经处理的异常”,NNNN 是进程编号。 将在事件日志中报告错误和堆栈。 将符号与程序集放在同一文件夹中将显示具有文件名和行号的违规堆栈已完成。
另一个错误情况是程序集由于未部署到 GAC 而无法加载。 对于这种情况,WMI.NET 将返回提供程序加载失败 (WBEM_E_PROVIDER_LOAD_FAILURE) 。
提供程序开发过程中的一个常见问题是 WMI 架构与代码不匹配。 在未将架构部署到 WMI 的情况下部署新程序集时,或者在使用 InstallUtil.exe 部署时未捕获问题时,可能会发生这种情况,因为信息仅在运行时可用。 例如,如果在枚举期间返回了不正确的类型,则情况就是这种情况。 WMI.NET 基础结构向客户端报告作为提供程序故障 (WMI 错误WBEM_E_PROVIDER_FAILURE) ,它将在 Windows 事件日志中生成描述运行时问题的消息。
其他提示
所有属性和 WMI.NET 代码都位于 System.Management.Instrumentation 命名空间中。 若要为 WMI.NET 生成程序集,项目需要引用 System.Core.dll 和 System.Management.Infrastructure.dll ,因为它们分别包含属性和运行时代码的定义。 后面的 了解如何加载已检测的程序集,将检测的类与 WMI 存储库匹配,并相应地调用它们。
将特定类型的应用程序的所有类保留在同一程序集中。 这使得维护和部署更加容易。
本文中的所有代码都需要部署并以管理员身份运行。 在 Windows Vista 上,以管理员身份运行需要提升的安全命令提示符。 提供程序通常不需要客户端是管理员,除非它们访问特别受保护的系统数据,如示例中所示。
结论
.NET Framework 3.5 中的新管理库为开发人员提供了一个强大的工具,用于编写 WMI 提供程序。 鉴于编程模型的简单性,编写 WMI 提供程序是一项相当简单的任务,允许你实现大多数 WMI 功能。 本文中的示例演示了如何仅编写简单的 WMI 提供程序,但该库还支持开发分离提供程序以及实现单一实例、继承、抽象类、引用和关联类,使 WMI.NET 提供程序扩展 v2 成为使用 WMI 本机接口进行开发的主要替代方法。
列表 1 - SCMInterop.cs
using System;
using System.Runtime.InteropServices;
使用 System.ServiceProcess;
namespace External.PInvoke
{
[StructLayout (LayoutKind.Sequential) ]
内部结构SERVICE_STATUS_PROCESS
{
public uint dwServiceType;
public uint dwCurrentState;
public uint dwControlsAccepted;
public uint dwWin32ExitCode;
public uint dwServiceSpecificExitCode;
public uint dwCheckPoint;
public uint dwWaitHint;
public uint dwProcessId;
public uint dwServiceFlags;
public static readonly int SizeOf = Marshal.SizeOf (typeof (SERVICE_STATUS_PROCESS) ) ;
}
[Flags]
内部枚举SCM_ACCESS :uint
{
SC_MANAGER_CONNECT = 0x00001,
}
[Flags]
内部枚举SERVICE_ACCESS : uint
{
STANDARD_RIGHTS_REQUIRED = 0xF0000,
SERVICE_QUERY_CONFIG = 0x00001,
SERVICE_CHANGE_CONFIG = 0x00002,
SERVICE_QUERY_STATUS = 0x00004,
SERVICE_ENUMERATE_DEPENDENTS = 0x00008,
SERVICE_START = 0x00010,
SERVICE_STOP = 0x00020,
SERVICE_PAUSE_CONTINUE = 0x00040,
SERVICE_INTERROGATE = 0x00080,
SERVICE_USER_DEFINED_CONTROL = 0x00100,
SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
SERVICE_QUERY_CONFIG |
SERVICE_CHANGE_CONFIG |
SERVICE_QUERY_STATUS |
SERVICE_ENUMERATE_DEPENDENTS |
SERVICE_START |
SERVICE_STOP |
SERVICE_PAUSE_CONTINUE |
SERVICE_INTERROGATE |
SERVICE_USER_DEFINED_CONTROL)
}
内部枚举SC_STATUS_TYPE
{
SC_STATUS_PROCESS_INFO = 0
}
内部类 ServiceHandle:SafeHandle
{
public ServiceHandle ()
:base (IntPtr.Zero,true)
{
}
public void OpenService (SafeHandle scmHandle, string serviceName)
{
IntPtr serviceHandle = SCM。OpenService (scmHandle、serviceName SERVICE_ACCESS。SERVICE_QUERY_STATUS) ;
if (serviceHandle == IntPtr.Zero)
{
引发新的 System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error () ,
“SCM。QueryServiceStatusEx“) ;
}
SetHandle (serviceHandle) ;
}
protected override bool ReleaseHandle ()
{
返回 SCM。CloseServiceHandle (base.handle) ;
}
public override bool IsInvalid
{
get { return IsClosed || handle == IntPtr.Zero; }
}
}
内部类 SCM:SafeHandle
{
[DllImport (“advapi32.dll”, EntryPoint = “OpenSCManagerW”, CharSet = CharSet.Unicode,
SetLastError = true) ]
public static extern IntPtr OpenSCManager (string machineName,
string databaseName,
[MarshalAs (UnmanagedType.U4) ]SCM_ACCESS dwAccess) ;
[DllImport (“advapi32.dll”, EntryPoint = “OpenServiceW”, CharSet = CharSet.Unicode,
SetLastError = true) ]
public static extern IntPtr OpenService (SafeHandle hSCManager,
[MarshalAs (UnmanagedType.LPWStr) ] string lpServiceName,
[MarshalAs (UnmanagedType.U4) ]SERVICE_ACCESS dwDesiredAccess) ;
[DllImport (“advapi32.dll”, EntryPoint = “QueryServiceStatusEx”, CharSet = CharSet.Auto,
SetLastError = true) ]
public static extern bool QueryServiceStatusEx (SafeHandle hService,
SC_STATUS_TYPE InfoLevel,
ref SERVICE_STATUS_PROCESS dwServiceStatus,
int cbBufSize,
ref int) ;
[DllImport (“advapi32.dll”, EntryPoint = “CloseServiceHandle”, CharSet = CharSet.Auto,
SetLastError = true) ]
public static extern bool CloseServiceHandle (IntPtr hService) ;
public SCM ()
:base (IntPtr.Zero,true)
{
IntPtr 句柄 = OpenSCManager (null、null、SCM_ACCESS。SC_MANAGER_CONNECT) ;
基地。SetHandle (句柄) ;
}
protected override bool ReleaseHandle ()
{
返回 SCM。CloseServiceHandle (base.handle) ;
}
public override bool IsInvalid
{
get { return IsClosed || handle == IntPtr.Zero; }
}
public void QueryService (string serviceName, out SERVICE_STATUS_PROCESS statusProcess)
{
statusProcess = new SERVICE_STATUS_PROCESS () ;
int cbBytesNeeded = 0;
using (ServiceHandle serviceHandle = new ServiceHandle () )
{
serviceHandle.OpenService (此,serviceName) ;
bool scmRet = SCM。QueryServiceStatusEx (serviceHandle、
SC_STATUS_TYPE。SC_STATUS_PROCESS_INFO,
ref statusProcess、
SERVICE_STATUS_PROCESS。SizeOf、
ref cbBytesNeeded) ;
if (!scmRet)
{
引发新的 System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error () ,
“SCM。QueryServiceStatusEx“) ;
}
}
}
public int GetProcessId (string serviceName)
{
SERVICE_STATUS_PROCESS serviceStatus;
这。QueryService (serviceName, out serviceStatus) ;
return (int) serviceStatus.dwProcessId;
}
}
}
清单 2 – WIN32ServiceHost.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceProcess;
using System.Diagnostics;
using External.PInvoke;
using System.Management.Instrumentation;
[assembly: WmiConfiguration (@“root\cimv2”, HostingModel = ManagementHostingModel.NetworkService) ]
命名空间 TestWMI.Hosted
{
[System.ComponentModel.RunInstaller (true) ]
公共类 MyInstall:DefaultManagementInstaller
{
}
[ManagementEntity (External = true) ]
抽象公共类Win32_Process
{
受保护的字符串句柄;
[ManagementKey]
公共字符串句柄
{
get {
返回 this.handle;
}
}
}
[ManagementEntity (Name = “WIN32_ServiceHost”) ]
公共类 WIN32ServiceHost:Win32_Process
{
Process innerProcess;
<总结>
///
</总结>
<param name=“innerProcess”></param>
public WIN32ServiceHost (Process innerProcess)
{
this.innerProcess = innerProcess;
this.handle = innerProcess.Id.ToString () ;
}
public int ID
{
get { return this.innerProcess.Id; }
}
[ManagementProbe]
public string[] Services
{
get
{
如果 (innerProcess.Id == 0)
返回 null;
ServiceController[] services = ServiceController.GetServices () ;
List<ServiceController> servicesForProcess = new List<ServiceController> () ;
using (SCM scm = new SCM () )
{
for (int svcIndex = 0;svcIndex < 服务。长度;svcIndex++)
{
ServiceController crtService = services[svcIndex];
int processId = scm。GetProcessId (crtService.ServiceName) ;
如果 (processId == innerProcess.Id)
{
servicesForProcess.Add (services[svcIndex]) ;
}
}
}
如果 (servicesForProcess.Count == 0)
返回 null;
string[] servicesNames = new string[servicesForProcess.Count];
for (int serviceIdx = 0;serviceIdx < servicesForProcess.Count;serviceIdx++)
{
servicesNames[serviceIdx] = servicesForProcess[serviceIdx]。ServiceName;
}
return servicesNames;
}
}
[ManagementEnumerator]
static public IEnumerable EnumerateServiceHosts ()
{
Process[] process = Process.GetProcesses () ;
foreach (进程 crtProcess 中的进程)
{
WIN32ServiceHost crtServiceHost = new WIN32ServiceHost (crtProcess) ;
if (crtServiceHost.Services != null)
{
yield 返回 crtServiceHost;
}
}
}
[ManagementBind]
public WIN32ServiceHost (字符串句柄)
{
int processId;
如果 (!Int32.TryParse (Handle, out processId) )
{
引发新的 InstanceNotFoundException () ;
}
尝试
{
Process process = Process.GetProcessById (processId) ;
this.innerProcess = process;
this.handle = 句柄;
如果 (此。Services == null)
{
引发新的 InstanceNotFoundException () ;
}
}
如果未找到具有给定 ID 的进程,则由 GetProcessById 引发
catch (ArgumentException)
{
引发新的 InstanceNotFoundException () ;
}
}
静态公共 WIN32ServiceHost GetInstance (字符串句柄)
{
int processId;
如果 (!Int32.TryParse (Handle, out processId) )
{
返回 null;
}
尝试
{
Process process = Process.GetProcessById (processId) ;
WIN32ServiceHost crtServiceHost = 新的 WIN32ServiceHost (进程) ;
if (crtServiceHost.Services != null)
{
返回 crtServiceHost;
}
else
{
返回 null;
}
}
如果未找到具有给定 ID 的进程,则由 GetProcessById 引发
catch (ArgumentException)
{
返回 null;
}
}
[ManagementTask]
public bool StopServices (int 毫秒Wait)
{
如果 (innerProcess.Id == 0)
return false;
ServiceController[] services = ServiceController.GetServices () ;
bool oneFailed = false;
using (SCM scm = new SCM () )
{
for (int svcIndex = 0;svcIndex < 服务。长度;svcIndex++)
{
ServiceController crtService = services[svcIndex];
int processId = scm。GetProcessId (crtService.ServiceName) ;
if (processId == innerProcess.Id)
{
尝试
{
crtService.Stop () ;
如果 (毫秒,则为 != 0)
{
crtService.WaitForStatus ( ServiceControllerStatus.Stopped,
new TimeSpan ( (long) 毫秒Wait * 10000) ) ;
}
}
catch (System.ServiceProcess.TimeoutException)
{
oneFailed = true;
}
catch (System.ComponentModel.Win32Exception)
{
oneFailed = true;
}
catch (InvalidOperationException)
{
oneFailed = true;
}
}
}
}
return !oneFailed;
}
}
}
© 2008 年 Microsoft Corporation。 保留所有权利。