在DotNet服务中集成WMI 服务提供程序
2007年10月17日
by James Moore
James展示了如何添加简单的WMI提供程序,这样我们就可以监视、修改和通过网络远程访问它。
如果你编写一个想在远程能够监视和配置的程序(例如服务),可能想让你的程序和Windows管理指令(WMI)顺利集成。WMI是一种Web Based Enterprise Management工业标准的微软实现。它允许企业用户通过多种设备访问、更改和管理信息。在微软的DotNet Framework的类库的system.management命名空间中。
在本篇文章中,我们首先编写一个通过TCPl连接访问并返回通过Telenet连接输入的任何字符的Windows 服务程序。然后,我们通过添加WMI代码让该服务能够发布i其返回的字符的数量。也就是说,我们把该服务转换为一个WMI提供者。
尽管这是一个相当简单示例,它展示了开发具有WMI功能的应用程序的关键步骤。
1、 创建和安装服务
在Visual Studio中创建一个TestService的Windows服务程序。在解决方案中打开Service1.cs文件。
添加一个类型为Thread的变量m_engineThread
Thread m_engineThread;
我们在服务启动时开始一个新的监听线程
protectedoverridevoid OnStart(string[] args)
{
m_engineThread = newThread(newThreadStart(ThreadMain));
m_engineThread.Start();
}
确保服务停止时,该监听线程也停止。
protectedoverridevoid OnStop()
{
try
{
m_engineThread.Abort();
}
catch (Exception) { ;}
}
ThreadMain代码相当简单,它只是设置一个TCPListener并接受连接。然后输出通过TCP连接输入的任何内容。在单独的一行输入”.”结束。
publicvoid ThreadMain()
{
// Setup the TCP Listener to bind to 127.0.0.1:50009
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
TcpListener tlistener = newTcpListener(localAddr, 50009);
try
{
// Start listening
tlistener.Start();
String data = null;
// Enter processing loop
while (true)
{
// Block until we get a connection
TcpClient client = tlistener.AcceptTcpClient();
data = null;
// Get a stream object and
// then create a StreamReader for convience
NetworkStream stream = client.GetStream();
StreamReader sr = newStreamReader(stream);
// Read a line from the client at a time.
while ((data = sr.ReadLine()) != null)
{
if (data == ".")
{
break;
}
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
stream.Write(msg, 0, msg.Length);
stream.WriteByte((byte)'"r');
stream.WriteByte((byte)'"n');
}
// Shutdown and end connection
client.Close();
}
}
catch (SocketException e)
{
;
}
finally
{
// Stop listening for new clients.
tlistener.Stop();
}
}
最后,我们让这个服务自动安装。要实现自动安装,在工程中添加Sytem.Configuration.Install.dll引用,并在工程中添加一个MyInstaller的类,该类从Installer中继承并标记RunInstallerAttribute属性。
[System.ComponentModel.RunInstaller(true)]
publicclassMyInstaller : Installer
{
…..
在MyInstaller构造方法中,需要添加如下代码来安装服务:
public MyInstaller()
{
ServiceProcessInstaller procInstaller = new
ServiceProcessInstaller();
ServiceInstaller sInstaller = newServiceInstaller();
procInstaller.Account = ServiceAccount.LocalSystem;
sInstaller.StartType = ServiceStartMode.Automatic;
sInstaller.ServiceName = "Simple-Talk Test Service";
Installers.Add(sInstaller);
Installers.Add(procInstaller);
}
所有这些,确保服务安装正确,并出现在控制面板的服务管理中。
2、 启动和停止服务
让我们运行以下。按F6编译该程序。然后运行InstallUtil.exe来安装生成的二进制文件。如:
C:"Simple-Talk>InstallUtil.exe TestService.exe
你会看安装工具的大量的命令行安装情况提示。一旦安装完成,在开始-运行中输入services.msc运行服务管理。找到Simple-TalkTestService服务并启动它。
既然已经启动服务,在启动-运行中输入Telnet 127.0.0.1 5009,打开一个Telnet窗口,输入你想要回显得东西后按回车。
结束连接,在单独一行中输入一个”.”回车。
我们通过服务管理结束该服务。
3、添加WMI支持
现在,我们要向TestService服务添加WMI支持。作为示例,我们将自从服务启动以来,其所回显的字符总数发布出来。
要添加对WMI支持,添加对System.Management.dll引用。并在工程中添加一个EchoInfoClass类,并标记InstrumentationClass属性,并添加参数InstrumentationType.Instance. 然后,添加一个公共int 类型变量CharsEchoed。
[InstrumentationClass(InstrumentationType.Instance)]
publicclassEchoInfoClass
{
publicint CharsEchoed;
}
InstrumentationClass属性指定该类提供WMI数据。这种WMI数据即可以是一个类的实例也可以是一个WMI事件通知使用的一个类。本例中,我们提供一个类的实例。下一步,为了能够提供WMI支持,我们需要修改之前编写的安装类,将我们的WMI对象注册到WMI类库中。
为了按钻,我们先运行InstallUtil.exe /u Testservice.exe 来卸载该服务。
现在,我们修改安装类来将WMI对象正确注册到WMI系统中。幸运的是,DotNet Framework 架构将这一切做得非常简单。在FrameWork中有一DefaultManagementProjectInstaller类为具有InstrumentationClass属性的类提供默认的安装代码. 要使用该特性,只要将MyInstaller类从DefaultManagementProjectInstaller继承,而不是从Installer.继承即可。
[System.ComponentModel.RunInstaller(true)]
publicclassMyInstaller :
DefaultManagementProjectInstaller
{
…
我们需要在服务启动时创建并注册这个服务实例。这样,首先定义一个服务类的变量:
EchoInfoClass m_informationClass;
然后,在OnStart代码中添加如下代码:
protected override void OnStart(string[] args)
{
m_informationClass = newEchoInfoClass();
m_informationClass.CharsEchoed = 0;
Instrumentation.Publish(m_informationClass);
m_engineThread = new Thread(new ThreadStart(ThreadMain));
m_engineThread.Start();
}
这样,就创建了类的实例并将其注册到WMI FrameWork中并可以通过WMI进行访问了。完成后就像正常类进行访问就可以。
我们就告诉WMI FrameWork 我们自定义的类情况,并将该类的一个实例发布了出去(在OnStart方法中)。现在,我们需要更新通过WMI发布的类的信息。通过将每次回显数据给客户端时修改 m_informationClass.CharsEchoed字段来实现.在ThreadMain:添加如下方法来实现:
while ((data = sr.ReadLine()) != null)
{
if (data == ".")
{
break;
}
byte[] msg =
System.Text.Encoding.ASCII.GetBytes(data);
stream.Write(msg, 0, msg.Length);
stream.WriteByte((byte)'"r');
stream.WriteByte((byte)'"n');
m_informationClass.CharsEchoed += msg.Length;
}
4、测试WMI Provider
现在我们可以编译运行一下看看运行情况。按F6编译程序并再次运行InstallUtil。
C:"Simple-Talk>InstallUtil.exe TestService.exe
然后,启动服务进行试验。
C:"Simple-Talk>net start “Simple-talk test service”
C:"Simple-Talk>telnet 127.0.0.1 50009
Telnet 命令打开一个屏幕等待录入。录入”simple-talk”并按回车,屏幕回显”simple-talk”。
所以,服务返回11个字符。我们希望WMI Provider工作正常并且记录该数据。微软提供一个wbemTest工具提供对WMI信息浏览。尽管该程序已经很老了,但是现在可以使用它来进行测试。
C:"Simple-Talk>wbemtest
单击Connect按钮,选择默认设置,然后单击OK按钮:
单击Query…按钮并输入如下信息:
WQL 返回我们请求的实例而不是行,显示给我们如下信息:
注意: WQL非常类似 SQL –事实上,它是SQL子集,让我们以非常类似与访问RDBMS的方式访问管理信息。WQL 通常返回实例而不是行。
双击类的实例:
CharsEchoed属性显示为其已经发回数为11。
5、总结
WMI是windows和其他平台进行跨企业网的机器和程序管理的基础架构。尽管我们的程序相当简单。但它给你足够的信息进行编写服务或网站进行WMI集成来进行远程监视和管理。
作者简介:James Moore