You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Windows服务安装器中动态设置InstrumentedAttribute命名空间参数?

嘿,我来帮你搞定这个动态设置WMI Instrumented命名空间的问题!

首先要明确:程序集级的InstrumentedAttribute是编译时就固定下来的,没办法在安装阶段或者运行时直接修改它的参数。所以咱们得换个思路,绕开静态属性的限制,手动控制WMI命名空间的注册和服务启动时的初始化。

下面是具体的实现步骤,结合你的ProjectInstaller代码来调整:


1. 移除静态程序集属性

先把代码顶部的<Assembly: Instrumented("Root/EnRoute/Interfaces/Spill")>这一行删掉,我们不再依赖这个静态配置。

2. 修改ProjectInstaller:确保WMI命名空间存在

在你的Install方法中,添加代码来动态创建WMI命名空间(从配置文件读取命名空间值)。这样安装服务时,会先确保目标命名空间存在:

Imports System.Configuration
Imports System.IO
Imports System.Management

Public Overrides Sub Install(stateSaver As IDictionary)
    MyBase.Install(stateSaver)
    
    ' 读取配置文件中的WMI命名空间(从传入的configName参数加载对应配置)
    Dim configNamespace As String = String.Empty
    If Context.Parameters.ContainsKey("configName") Then
        ' 构建配置文件路径
        Dim configPath As String = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{Context.Parameters.Item("configName")}.config")
        Dim configMap As New ExeConfigurationFileMap() With {.ExeConfigFilename = configPath}
        Dim configSettings As Configuration = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.None)
        ' 读取配置项,没有的话用默认值
        configNamespace = configSettings.AppSettings.Settings("WmiNamespace")?.Value ?? "Root/EnRoute/Interfaces/Spill"
    End If

    ' 逐级创建WMI命名空间(避免直接创建多级失败)
    If Not String.IsNullOrEmpty(configNamespace) Then
        Dim namespaceParts As String() = configNamespace.Split("/"c)
        Dim currentNamespace As String = namespaceParts(0) ' 从root开始
        For i As Integer = 1 To namespaceParts.Length - 1
            currentNamespace = $"{currentNamespace}/{namespaceParts(i)}"
            Try
                ' 尝试连接命名空间,检查是否存在
                Dim scope As New ManagementScope(currentNamespace)
                scope.Connect()
            Catch ex As ManagementException
                ' 命名空间不存在,创建它
                Using nsClass As New ManagementClass($"root/__Namespace", Nothing)
                    Using newNs As ManagementObject = nsClass.CreateInstance()
                        newNs("Name") = namespaceParts(i)
                        newNs.Put()
                    End Using
                End Using
            End Try
        Next
    End If

    ' 保留你原来的注册表修改逻辑
    If Context.Parameters.ContainsKey("configName") Then
        Dim system = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("System")
        Dim currentControlSet = system.OpenSubKey("CurrentControlSet")
        Dim services = currentControlSet.OpenSubKey("Services")
        Dim service = services.OpenSubKey(Me.ServiceInstaller1.ServiceName, True)
        service.SetValue("Description", Me.ServiceInstaller1.ServiceName + " Service")
        Dim config = service.CreateSubKey("Parameters")
        config.SetValue("Arguments", Command)
        Console.WriteLine(service.GetValue("ImagePath"))
        service.SetValue("ImagePath", $"{service.GetValue("ImagePath")} {Context.Parameters.Item("configName")}")
    End If
End Sub

3. 在Windows服务的OnStart方法中动态初始化WMI

接下来,在你的服务主类的OnStart方法里,读取配置文件中的命名空间,手动初始化WMI instrumentation:

Imports System.Management.Instrumentation
Imports System.Configuration
Imports System.IO

Public Class YourServiceClass
    Inherits ServiceBase

    Protected Overrides Sub OnStart(args() As String)
        ' 读取启动参数中的configName(来自ProjectInstaller设置的ImagePath)
        Dim configName As String = If(args.Length > 0, args(0), String.Empty)
        Dim namespaceName As String = "Root/EnRoute/Interfaces/Spill" ' 默认值

        If Not String.IsNullOrEmpty(configName) Then
            ' 加载指定的配置文件
            Dim configPath As String = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{configName}.config")
            Dim configMap As New ExeConfigurationFileMap() With {.ExeConfigFilename = configPath}
            Dim config As Configuration = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.None)
            namespaceName = config.AppSettings.Settings("WmiNamespace")?.Value ?? namespaceName
        End If

        ' 动态初始化WMI instrumentation,指定命名空间
        InstrumentationManager.Initialize(namespaceName, True)
        ' 注册你的WMI监测类(替换成你自己的WMI类型)
        InstrumentationManager.RegisterType(GetType(YourCustomWmiClass))

        ' 其他服务启动逻辑...
    End Sub

    Protected Overrides Sub OnStop()
        ' 清理WMI instrumentation资源
        InstrumentationManager.Shutdown()
        MyBase.OnStop()
    End Sub
End Class

4. 配置文件中添加WmiNamespace项

在你的目标配置文件(比如EnRoute.Interface.Spill.<groupname>.Service.exe.config)里,添加命名空间的配置项:

<appSettings>
    <add key="WmiNamespace" value="Root/EnRoute/Interfaces/Spill_Law" />
</appSettings>

为什么不需要自定义InstrumentedAttribute?

你之前猜测的自定义构造器其实没必要,因为程序集级属性是编译时绑定的,运行时无法修改它的参数。而上面的方法通过手动创建WMI命名空间+运行时动态初始化Instrumentation,完全绕开了静态属性的限制,实现了动态配置的需求。

内容的提问来源于stack exchange,提问作者Brett Ossman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 00:24:11