WMI使用说明(翻译)

类别:VC语言 点击:0 评论:0 推荐:

原文:http://www.codeproject.com/useritems/wmi.asp

第一次翻译,只想带个头,翻译的不好,希望各位不要笑话我

相信大家对WMI一定也都有听说过吧,国内的很多网站也多有介绍过
这里是codeproject上的一篇,我觉得很全面在这里翻译一下希望大
家评价一下,讨论一下自己编写代码的心得,OK不多说了,翻译了......

先介绍一下相关知识:
什么是WMI呢Windows 管理规范 (Windows Management Instrumentation ),它的
主要功能包括:访问本地主机的一些信息和服务,可以远程管理计算机(当然你必
须拥有足够的权限)也就是说可以象重起,关机,关闭进程,创建进程等等!

有了初步了解下面我门开始一些初步的工作吧:
在这个WMI程序中,我将实现四个基本功能:
1.Explorer 类似与windows里的资源管理器
2.SystemInfo 查看你的硬件信息和OS信息
3.Services  当前正在工作的信息
4.Processes 当前执行的进程
(这里是可以管理远程和本地OS的)

好了,我们要开始实现访问我们的OS了,不过在这之前我们必须要引入System.Management
这个命名空间

下面让我们了解一下控件的状态事件

我们必须保证是实时的所以必须在这里包涵UpdateStatus(string e)该事件(这是一个自定义的)
这里主要是希望在点击每个控件时刷新状态栏,可以让用户知道程序在做什么!

代码事例:
//控件代码
//状态事件的代理
public delegate void Status(string e);
public event Status UpdateStatus;
//这里更新状态栏
UpdateStatus("Hello world.");

//这里是在主程序里的代码
//写具体的事件代码
private void refreshStatusBar(string stringStatus)
{
 //update status bar
 statusBarStatus.Text = stringStatus;
}


下面是具体代码:

Explorer  Control

这里首先介绍一下WMI的 Win32_LogicalDisk类(参考Platform SDK: Windows Management Instrumentation),通过它我们可以查看到本地驱动器
的一些详细情况,我们还需要用到System.Management中的两个类ManagementObjectSearcher
和ManagementOjbectCollection它们的作用主要是ManagementObjectSearcher将查询到了
ManagementOjbectCollection该对象的集合中去(这里可以获取的驱动器信息包括 驱动器的名称
,类型,描述信息等)当然你也可以只查看驱动器的部分信息可以在ManagementObjectSearcher类
的构造函数中这样写ManagementObjectSearcher query = new ManagementObjectSearcher("SELECT * From Win32_LogicalDisk "); (参考Platform SDK: Windows Management Instrumentation)
代码如下:
//get drive collection
 ManagementObjectSearcher query = new ManagementObjectSearcher("SELECT * From Win32_LogicalDisk ");
 ManagementObjectCollection queryCollection = query.Get();

 //loop throught each object to get drive information
 foreach ( ManagementObject mo in queryCollection)
 {
  switch (int.Parse( mo["DriveType"].ToString()))
  {
   case Removable: //removable drives
    imageIndex = 5;
    selectIndex = 5;
    break;
   case LocalDisk: //Local drives
    imageIndex = 6;
    selectIndex = 6;
    break;
   case CD: //CD rom drives
    imageIndex = 7;
    selectIndex = 7;
    break;
   case Network: //Network drives
    imageIndex = 8;
    selectIndex = 8;
    break;
   default: //defalut to folder
    imageIndex = 2;
    selectIndex = 3;
    break;
  }
  
  //get drive name
  Console.WriteLine("Drive: " + mo["Name"].ToString());
 }

SystemInfo Control

该控件主要用来查看本地或远程主机的OS和硬件信息,这里需要用到另外两个
对象ConnectionOptions和ManagementScope,ConnectionOptions主要是设置
WMI连接信息的,如用户名和密码,这里我们主要用到它的两个属性UserName和
Password; ManagementScope对象表示WMI的规范,通过该规范可以访问服务器
和命名空间的路径已及使用ConnectionOptions中的设置
请看如下代码:
//Connect to the remote computer
ConnectionOptions co = new ConnectionOptions();

co.Username = textUserID.Text;
co.Password = textPassword.Text;

//Point to machine
System.Management.ManagementScope ms = new System.Management.ManagementScope("\\\\" +
     stringHostName + "\\root\\cimv2", co);     

现在我们结合刚才的知识来得到我们要的信息,但我们要得到那些我们想要信息呢?
那样的话我们就必须要用到ObjectQuery对象,通过它我们可以得到我们想要的查询
信息.把ObjectQuery对象和ManagementScope对象放入ManagementObjectSearcher对象
中这样就可以通过我们设置好的规范和我们设置好的查询得到我们想要的结果,当然
还必须要掉用ManagementObjiectSearcher的Get()方法,它会返回一个ManagementObject
对象的集合,然后可以通过结合操作来访问到每一个我们要的信息.

代码如下:
//Query system for Operating System information
oq = new System.Management.ObjectQuery("SELECT * FROM Win32_OperatingSystem");
query = new ManagementObjectSearcher(ms,oq);

queryCollection = query.Get();
foreach ( ManagementObject mo in queryCollection)
{
 //create child node for operating system
 createChildNode(nodeCollection, "Operating System: " + mo["Caption"]);
 createChildNode(nodeCollection, "Version: " + mo["Version"]);
 createChildNode(nodeCollection, "Manufacturer : " + mo["Manufacturer"]);
 createChildNode(nodeCollection, "Computer Name : " +mo["csname"]);
 createChildNode(nodeCollection, "Windows Directory : " + mo["WindowsDirectory"]);
}
  
要是你只是希望查看到本地主机的信息,你就没必要去创建 ConnectionOption, ManagementScope,ObjectQuery 对象,你仅仅只需要把ManagementObjectSearcher
对象的结果在ManagementObjectCollection集合里去调用Get()方法既可.

代码如下:
ManagementObjectSearcher query = new ManagementObjectSearcher("SELECT * From Win32_OperatingSystem");
ManagementObjectCollection queryCollection = query.Get();

这里只是介绍了OS信息的方法,其它的如Bios,Memory.Network Connection等信息的查看
只需要把查询字改改就可以了!(可参考Platform SDK: Windows Management Instrumentation)

Service Control控件介绍:
该控件要用到一个新的查询字"SELECT * FROM Win32_Service",通过它我们就可以
得到系统中有那些服务存在.为了方便启动和终止一项服务我们可以在ListView中
动态的创建一个弹出式菜单,当用鼠标左击ListView中的Item的时候,可以用来启动
或终止一项服务.可以这样来指定你要的服务"SELECT * FROM Win32_Service WHERE
 Name = 'ServiceName'",这个时候我们要调用ManagementObject.InvokeMethod()
来指定是终止还是启动一个服务.InvokeMethod()的第一个参数是一个ManagementBaseObject
的对象.它用作更具体的管理对象类的基类.我通过一个ManagementOperationObserver对象
管理异步操作和处理异步收到的管理信息和事件。可以由completionHandlerObj.ReturnObject
(为自定义类的属性)
属性返回值来判断是否成功.

代码如下:
/// <summary>
/// List view mouse down event to built context menu dynamically
/// </summary>
///
///
private void listViewServices_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
 System.Windows.Forms.ListView listViewObject = (System.Windows.Forms.ListView) sender;
 ContextMenu mnuContextMenu = new ContextMenu();
 MenuItem menuItem = new MenuItem();
 ManagementObjectCollection queryCollection;

 //check if right button
 if (e.Button == System.Windows.Forms.MouseButtons.Right)
 {
  //get service name
  ServiceName = listViewObject.GetItemAt(e.X, e.Y).Text;
  //set list view item
  ServiceItem = listViewObject.GetItemAt(e.X,e.Y);

  //create popup menu
  listViewObject.ContextMenu = mnuContextMenu;
    try
  {
   //get specific service object
   queryCollection = getServiceCollection("SELECT * FROM Win32_Service Where Name = '" +
     ServiceName + "'");
   foreach ( ManagementObject mo in queryCollection)
   {
    //create menu depending on service state
    if (mo["Started"].Equals(true))
    {
     menuItem.Text = "Stop";
     //set action property
     ServiceAction = "StopService";
    }
    else
    {
     menuItem.Text = "Start";
     ServiceAction = "StartService";
    }
    mnuContextMenu.MenuItems.Add(menuItem);

    // Add functionality to the menu items using the Click event.
    menuItem.Click  += new System.EventHandler(this.menuItem_Click);
   }
  }
  catch (Exception e1)
  {
   MessageBox.Show("Error: " + e1);
  }
 }
}

/// <summary>
/// List view context menu click event to invoke start/stop service
/// </summary>
///
///
private void menuItem_Click(object sender, System.EventArgs e)
{  
 ManagementObjectCollection queryCollection;
 ListViewItem lvItem;

 //Set up a handler for the asynchronous callback
 ManagementOperationObserver observer = new ManagementOperationObserver();
 completionHandler.MyHandler completionHandlerObj = new completionHandler.MyHandler();
 observer.ObjectReady += new ObjectReadyEventHandler(completionHandlerObj.Done);

 //get specific service object
 queryCollection = getServiceCollection("Select * from Win32_Service Where Name ='" +
  ServiceName + "'");
 
 //Status
 updateStatus("Starting/Stopping service...");
 
 foreach ( ManagementObject mo in queryCollection) 
 {
  //start or stop  service
  mo.InvokeMethod(observer, ServiceAction, null);
 }
 //wait until invoke method is complete  or 5 sec timeout
 
 int intCount = 0; 
 while
 (!completionHandlerObj.IsComplete)
 {
  if
  (intCount >  10)
  {
   MessageBox.Show("Terminate process timed out.", "Terminate Process Status");
   break;
  }
  //wait 1/2 sec.
  System.Threading.Thread.Sleep(500);
 
  //increment counter
  intCount++;
 }

 //see if call was successful.
 if (completionHandlerObj.ReturnObject.Properties["returnValue"].Value.ToString() == "0")
 {
  //succeeded
  lvItem = ServiceItem;

  if (ServiceAction == "StartService")
   lvItem.SubItems[2].Text = "Started";
  else
   lvItem.SubItems[2].Text = "Stop";
 }
 else
 {
  //error message
  string stringAction;

  if (ServiceAction == "StartService")
   stringAction = "start";
  else
   stringAction = "stop";

  MessageBox.Show("Failed to " + stringAction + " service " + ServiceName + ".",
   "Start/Stop Service Failure");
 }

 //clean-up objects
 ServiceName = "";
 ServiceAction = "";
 ServiceItem = null;

 //Status
 updateStatus("Ready");
 this.Update();
}


//----------------------------------
// Completion Handler
//----------------------------------
using System;
using System.Management;

namespace completionHandler
{
 /// <summary>
 /// MyHandler class handle notification when InvokeMethod call is complete
 /// </summary>
 public class MyHandler
 {
  private bool isComplete = false;
  private ManagementBaseObject returnObject;
            
  /// <summary>
  /// Trigger Done event when InvokeMethod is complete
  /// </summary>
  public void Done(object sender, ObjectReadyEventArgs e)
  {
   isComplete = true;
   returnObject = e.NewObject;
  }


        /// <summary>
        /// Get property IsComplete
        /// </summary>
     public bool IsComplete
  {
   get
   {
    return isComplete;
   }
  }

  /// <summary>
  /// Property allows accessing the result object in the main function
  /// </summary>
  public ManagementBaseObject ReturnObject
  {
   get
   {
    return returnObject;
   }
  }


 }
}

ProcessesControl控件介绍:
该控件主要用来显示系统中正在运行的进程,如:用户进程.CPU利用率,
内存的使用状况.我们可以通过GetOwner(User,Domain)方法来得知进程
的所有者是谁.User和Domain是入口参数,可是问题是我们如何从InvokeMethod
中得到这入口参数呢?这里我们需要实现InvokeMethod.一下讨论两种情况
1.我们不需要异步操作,我们仅仅只需要一个string[]数组就可以完成
2.当我们需要异步操作的时候也只需要一个completionHandlerObj.ReturnObject
属性来收集对象.

代码如下:
//-------------------------------------------------
//Get process owner info without the observer object
//--------------------------------------------------
//Createan array containing all arguments for the method

string[] methodArgs = {"", ""};

//Get process owner info
mo.InvokeMethod("GetOwner", methodArgs);

//methodArgs[0] - contain process user
//methodArgs[1] = contain process domain

//-----------------------------------------------
//Getprocess owner info with the observer object
//-----------------------------------------------
mo.InvokeMethod(observer,"GetOwner", null);

while (!completionHandlerObj.IsComplete)
{
 System.Threading.Thread.Sleep(500);
}

if (completionHandlerObj.ReturnObject["returnValue"].ToString() == "0")
 structProcess.stringUserName = completionHandlerObj.ReturnObject.Properties["User"].Value.ToString();
else
 structProcess.stringUserName = "";
  

下面讨论如何终结进程:

终结一个指定的进程很类似与上面提到的启动或终止一项服务.
首先当然是用ManagementObject对象来指定你要的进程,然后调用
InvokeMethod(observer,"Terminate",null)来终止一个进程.

代码如下:
//Set up a handler for the asynchronous callback
ManagementOperationObserver observer = new ManagementOperationObserver();
completionHandler.MyHandler completionHandlerObj = new completionHandler.MyHandler();
observer.ObjectReady  += new ObjectReadyEventHandler(completionHandlerObj.Done);

//Get ManagementObject for process
queryCollection = getProcessCollection("Select * from Win32_Process Where ProcessID = '" + ProcessID + "'");

//Status
updateStatus("Invoking terminate process");

foreach ( ManagementObject mo in queryCollection)
{
 //start or stop service
 mo.InvokeMethod(observer, "Terminate", null);
}

//wait until invoke method is complete or 5 sec timeout
int intCount = 0;
while (!completionHandlerObj.IsComplete)
{
 if (intCount == 10)
 {
  MessageBox.Show("Terminate process timed out.", "Terminate Process Status");
  break;
 }
 //wait 1/2 sec.
 System.Threading.Thread.Sleep(500);
 
 //increment counter
 intCount++;
}

if (intCount != 10)
{
 //InvokeMethod did not time out
 if (completionHandlerObj.ReturnObject.Properties["returnValue"].Value.ToString() == "0")
 {
  lvItem = ProcessItem;
  lvItem.Remove();
 }
 else
 {
  MessageBox.Show("Error terminating process.", "Terminate Process");
 }
}

创建进程:

创建一个新的进程我们需要调用ManagementClass类的InvokeMethod方法来完成
我们可以通过ManagementClass processClass = New ManagementClass(ms,path,null);
这条语句来实现一个ManagementClass对象.ms是一个ManagementScope类的实例;
,path是一个ManagementPath的实例.ManagementScope来设置Management的范围.
ManagementPath用来提供一个包装,用于分析和生成 WMI 对象的路径.("Win32_Process")
在这之前我们还需要调用ManagementClass.InvokeMethod(observer, methodName, inParameters).
我们可以通过一个对象数组来一次传递四个参数.inParameters实际上就是一个
Create Method in Class Win32_Process(参考Platform SDK: Windows Management Instrumentation)
uint32 Create(string CommandLine,
     string CurrentDirectory,
     Win32_ProcessStartup ProcessStartupInformation,
     uint32* ProcessId);
Parameters
CommandLine - [in] Command line to execute. The system adds a null character to the command line, trimming the string if necessary, to indicate which file was actually used.

CurrentDirectory -  [in] Current drive and directory for the child process. The string requires that the current directory resolves to a known path. A user can specify an absolute path or a path relative to the current working directory. If this parameter is NULL, the new process will have the same path as the calling process. This option is provided primarily for shells that must start an application and specify the application's initial drive and working directory.

ProcessStartupInformation  - [in] The startup configuration of a Windows process. For more information see Win32_ProcessStartup.

ProcessId - [out] Global process identifier that can be used to identify a process. The value is valid from the time the process is created until the time the process is terminated

例如代码:
//Create an array containing all arguments for the method
object[] methodArgs = {stringCommandLine, null, null, 0};

//Execute the method
processClass.InvokeMethod (observer, "Create", methodArgs);
  

下面的代码是用来创建并插入一个新的进程.我们可以写一个CreateProcess
函数通过参数stringCommandLine来传递一个你希望创建的进程.如你可以
这样写 CreateProcess("Calc.exe"), 这时候你就可以创建一个计算器
进程了,下面是个例子.

代码如下;

/// <summary>
/// Invoke method 'Create' on local or remote machine
/// </summary>
///
private void CreateProcess(string stringCommandLine)
{  
 //Set up a handler for the asynchronous callback
 ManagementOperationObserver observer = new ManagementOperationObserver();
 completionHandler.MyHandler completionHandlerObj = new completionHandler.MyHandler();
 observer.ObjectReady  += new ObjectReadyEventHandler(completionHandlerObj.Done);

 string stringMachineName = "";

 //Connect to the remote computer
 ConnectionOptions co = new ConnectionOptions();

 if (radioMachine.Checked == true)
 {
  stringMachineName = "localhost";
 }
 else
 {
  stringMachineName = textIP.Text;
 }

 if (stringMachineName.Trim().Length == 0)
 {
  MessageBox.Show("Must enter machine IP address or name.");
  return;
 }

 //get user and password
 if (textUserID.Text.Trim().Length   > 0)
 {
  co.Username = textUserID.Text;
  co.Password = textPassword.Text;
 }

 //Point to machine
 System.Management.ManagementScope ms = new System.Management.ManagementScope("\\\\" +
  stringMachineName + "\\root\\cimv2", co);     
 //get process path
 ManagementPath path = new ManagementPath( "Win32_Process");

 //Get the object on which the method will be invoked
 ManagementClass processClass = new ManagementClass(ms,path,null);

 //Status
 updateStatus("Create process " + stringCommandLine + ".");
 
 //Create an array containing all arguments for the method
 object[] methodArgs = {stringCommandLine, null, null, 0};

 //Execute the method
 processClass.InvokeMethod (observer, "Create", methodArgs);

 //wait until invoke method is complete or 5 sec timeout
 int intCount = 0;
 while (!completionHandlerObj.IsComplete)
 {
  if (intCount > 10)
  {
   MessageBox.Show("Create process timed out.", "Terminate Process Status");
   break;
  }
  //wait 1/2 sec.
  System.Threading.Thread.Sleep(500);
  
  //increment counter
  intCount++;
 }

 if (intCount != 10)
 {
  //InvokeMethod did not time out
  //check for error
  if (completionHandlerObj.ReturnObject.Properties["returnValue"].Value.ToString() == "0")
  {
   //refresh process list
   this.Refresh();
  }
  else
  {
   MessageBox.Show("Error creating new process.", "Create New Process");
  }
 }

 //Status
 updateStatus("Ready");
 this.Update();
}


总结:
这里只是做了一个使用WMI的例子.我们大体可以通过这个简单的例子
了解一下WMI究竟可以做那些事情.我想,我在代码中的注释可以很方便
让大家了解WMI.

下面这个列表可以让你明白WMI能处理那些问题:

*控制你的软硬件
*监管事件
*执行一个基于事件的描述
*通过事件来发送E-mail

参考资料:
[url]http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/wmi_start_page.asp[/url]
[url]http://www.microsoft.com/taiwan/technet/scriptcenter/sampscr.htm[/url]

本文地址:http://com.8s8s.com/it/it2247.htm