专栏首页方亮WMI技术介绍和应用——VC开发WMI应用的基本步骤

WMI技术介绍和应用——VC开发WMI应用的基本步骤

        在《WMI技术介绍和应用——WMI概述》中介绍了我们可以使用C++、.net或者支持ActiveX技术的脚本语言来使用WMI。但是各种语言对WMI的控制能力是不同的,比如脚本语言只能用来从WMI获取数据或者接收事件通知。而C++还可以编写WMI提供者和发送消息。和脚本语言相比,C++对WMI有强大的控制和定制性,当然也具有更大的复杂性。本文将主要讲述使用VC如何搭建一个使用WMI的框架。(转载请指明出于breaksoftware的csdn博客)

        为了方便使用,我将该搭建步骤封转到了一个类中

class CWMI
{
public:
    CWMI();
    ~CWMI(void);
public:
    HRESULT ExcuteFun();
protected:
    VOID SetNamespace(wstring wstrNamespace);
private:
    HRESULT InitialCom();
    HRESULT SetComSecLevels();
    HRESULT ObtainLocator2WMI(CComPtr<IWbemLocator>& pLoc);
    HRESULT Connect2WMI(CComPtr<IWbemLocator> pLoc,CComPtr<IWbemServices>& pSvc);
    HRESULT SetProxySecLevels(CComPtr<IWbemServices> pSvc);
    virtual HRESULT Excute(CComPtr<IWbemServices> pSvc) = 0;
private:
    wstring m_wstrNamespace;
};

        SetNamespace用于设置命名空间。在《WMI技术介绍和应用——WMI概述》中我们提到,WMI中存在诸如root\default、root\cimv2等WMI命令空间。因为我们要使用的WMI功能和命名空间有很大的关联,所以该类将提供这个设置命令空间的函数。

VOID CWMI::SetNamespace(wstring wstrNamespace)
{
    m_wstrNamespace = wstrNamespace;
}

        剩下的函数就承载着了使用WMI步骤的逻辑和流程。

初始化COM库

        我们还要将之前的WMI框架图列出来

        WMI是基于COM技术的。在WMI Consumers层,我们发现,C++程序是直接和WMI COM API进行交互。所以我们要先初始化COM组件库。

HRESULT CWMI::InitialCom()
{
    HRESULT hr = E_FAIL;
    do {
        hr = CoInitializeEx(0, COINIT_MULTITHREADED);
        CHECKHR(hr);

    } while (0);
    return hr;
}

设置进程COM安全信息

        因为WMI是基于COM技术的,所以我们可以使用CoInitializeSecurity设置我们应用的认证信息和扮演等级(Impersonation Levels)。如果我们不调用该函数,COM将通过读取注册表中的相关键值去决定我们应用的认证信息和扮演等级。然而,往往注册表中记录的认证信息和扮演等级是非常低的。一般来说,注册表记录的扮演等级是RPC_C_IMP_LEVEL_IDENTIFY,而绝大部分WMI提供者至少需要RPC_C_IMP_LEVEL_IMPERSONATE等级。对于认证信息,在特殊场景下,我们可能需要用其他用户身份去使用COM。但是目前,我在管理员权限下运行程序,权限基本上是够得,所以设置也很简单。

HRESULT CWMI::SetComSecLevels()
{
    // Set general COM security levels --------------------------
    // Note: If you are using Windows 2000, you must specify -
    // the default authentication credentials for a user by using
    // a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
    // parameter of CoInitializeSecurity ------------------------

    HRESULT hr = E_FAIL;
    do {
        hr = CoInitializeSecurity(
            NULL, 
            -1,                          // COM negotiates service
            NULL,                        // Authentication services
            NULL,                        // Reserved
            RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication 
            RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation  
            NULL,                        // Authentication info
            EOAC_NONE,                   // Additional capabilities 
            NULL                         // Reserved
            );
        CHECKHR(hr);

    } while (0);
    return hr;
}

创建进程内COM服务器

        我们应用中使用WMI完成相关功能,比如查询硬盘ID,实际执行查询的操作是在WMI服务中执行的,而不是我们的应用。比如,在XP上,svchost.exe服务是WMI服务的载体。我们应用通过COM告诉svchost.exe我们的请求,svchost.exe将我们请求执行后,会将结果通过COM传递给我们的应用。这样就要求我们应用和WMI服务进程有通信。在我们应用这边,就需要创建一个进程内COM服务器用于接发数据。

HRESULT CWMI::ObtainLocator2WMI(CComPtr<IWbemLocator>& pLoc)
{
    HRESULT hr = E_FAIL;
    do {
        hr = CoCreateInstance(
            CLSID_WbemLocator,             
            0, 
            CLSCTX_INPROC_SERVER, 
            IID_IWbemLocator, (LPVOID *) &pLoc);
        CHECKHR(hr);

    } while (0);
    return hr;
}

连接WMI命名空间

        这步是使用WMI的关键一步。我们使用上一步创建的IWbemLocator实例的ConnectServer函数连接本地或者远程的WMI命名空间。我们看一下函数声明

HRESULT ConnectServer(
  [in]   const BSTR strNetworkResource,
  [in]   const BSTR strUser,
  [in]   const BSTR strPassword,
  [in]   const BSTR strLocale,
  [in]   LONG lSecurityFlags,
  [in]   const BSTR strAuthority,
  [in]   IWbemContext *pCtx,
  [out]  IWbemServices **ppNamespace
);

        第一个参数strNetworkResource是命名空间的名字,比如"root\default"或者"\\.\root\default"。第二三个是用户名和密码,如果是远程计算机,则这两个参数非常有用。最后一个参数返回了IWbemServices结构的代理。我们可以通过该代理访问WMI服务。

HRESULT CWMI::Connect2WMI( CComPtr<IWbemLocator> pLoc,CComPtr<IWbemServices>& pSvc )
{
    HRESULT hr = E_FAIL;
    do {
        hr = pLoc->ConnectServer(
            CComBSTR(m_wstrNamespace.c_str()), 
            NULL, NULL, NULL, NULL, NULL, NULL, &pSvc );
        CHECKHR(hr);
    } while (0);
    return hr;
}

设置WMI连接的安全等级

        一般来说,如果没有设置正确的安全属性,COM是不允许一个进程去访问另一个进程的中的对象。我们上步获得的IWbemServices接口代理就需要访问WMI服务进程中的对象,所以我们要对该代理设置安全等级。

HRESULT CWMI::SetProxySecLevels( CComPtr<IWbemServices> pSvc )
{
    HRESULT hr = E_FAIL;
    do {
        hr = CoSetProxyBlanket(
            pSvc,                        // Indicates the proxy to set
            RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx 
            RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx 
            NULL,                        // Server principal name 
            RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx 
            RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
            NULL,                        // client identity
            EOAC_NONE                    // proxy capabilities 
            );
        CHECKHR(hr);
    } while (0);
    return hr;
}

发起WMI请求

        通过以上一系列过程,我们终于可以开始使用WMI了。在我定义的类中,我将该步骤封装成一个纯虚函数Excute,继承CWMI的类将实现具体的工作。

清理          我们在使用完以上的代理和实例后,我们要释放它们,并且最后要释放COM库。

CWMI::~CWMI(void)
{
    CoUninitialize();
}

        总体来说执行的流程用代码可以描述为

HRESULT CWMI::ExcuteFun()
{
    HRESULT hr = E_FAIL;
    CComPtr<IWbemLocator> pLoc = NULL;
    CComPtr<IWbemServices> pSvc = NULL;

    do {
        hr = InitialCom();
        CHECKHR(hr);

        hr = SetComSecLevels();
        CHECKHR(hr);

        hr = ObtainLocator2WMI(pLoc);
        CHECKHR(hr);

        hr = Connect2WMI(pLoc, pSvc);
        CHECKHR(hr);

        hr = SetProxySecLevels(pSvc);
        CHECKHR(hr);

        hr = Excute(pSvc);
        CHECKHR(hr);

    } while (0);
    return hr;
}

        下一节,我将讲解一个简单的对CWMI的封装类。

        工程源码见《WMI技术介绍和应用——WMI概述》结尾。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 如何定制一款12306抢票浏览器——处理预订页面和验证码自动识别功能

            我们先看一下预订页面的结构(转载请指明出于breaksoftware的csdn博客)

    方亮
  • 如何定制一款12306抢票浏览器——实现自动查询和预订功能

            判断是否进入订票页面,我是确定了两个标准:(转载请指明出于breaksoftware的csdn博客)

    方亮
  • WMI技术介绍和应用——执行方法

            在之前的博文中,我们主要介绍了如何使用WMI查询信息和接收事件。本文将介绍WMI的另一种用法——执行方法。(转载请指明出于breaksoftwa...

    方亮
  • GitHub 撤下被泄露的 Snapchat 源码 黑客扬言重新上传

    Snapchat 是一款主打“阅后即焚”功能的消息应用,不过近日,这款 app 的源码也在代码托管网上上玩了一回“阅后即焚”。事情的起因是,一位据说来自巴基斯坦...

    周俊辉
  • 干货|自适应大规模邻域搜索算法求解带时间窗的车辆路径规划问题(上)

    不知道大家在使用启发式算法求解车辆路径规划问题时有没有这样的困惑:设计邻域搜索算子实在是太太太太难了,邻域搜索算子必须在算子搜索范围以及算子复杂度之间达到平衡,...

    用户1621951
  • MCMC(三)MCMC采样和M-H采样

        在MCMC(二)马尔科夫链中我们讲到给定一个概率平稳分布$\pi$, 很难直接找到对应的马尔科夫链状态转移矩阵$P$。而只要解决这个问题,我们就可以找到...

    刘建平Pinard
  • WPF开发之C#中关闭进程的方式

    剑行者
  • web前端开发学习如何规划

    在这个新的互联网行业大时代里,很多年轻人以及学生都会去选择IT互联网这个行业,要想成功融入到web前端这个岗位之中去,首先你肯定要先熟悉前端到底是做什么的,它有...

    千锋哈尔滨IT培训
  • Golang语言中的多维数组传递

    在Go语言中,当多维数组直接作为函数实参进行参数传递的时候,会有很大的限制性,比如除第一维数组的其他维数需要显式给出等;此时可以使用多维切片来作为参数传递: ...

    李海彬
  • Compute the Optimal Policy & the Optimal Value 计算最佳策略和计算最佳价值

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    Steve Wang

扫码关注云+社区

领取腾讯云代金券