Wmi Program

必要なシステム類

  • Visual Studio 6 以降の Visual C++
    (自分は現在Visual Studio.Net 2003を使用)

  • 最新版のPlatform SDK と WMI SDK
    入手はInternetExplorerこのサイトにアクセスしてインストールして下さい。
    NetscapeやFireFoxなどIE以外のブラウザを使用した場合、エラーになります。
    このサイトにはPlatform SDK、WMI SDK以外のSDKも置かれています。
    容量はとられますが、入れて置いて損は無いかもしれません。

  • 最新のDirectX SDK
    ゲームプログラミングをやるなら入れておいたほうがいいです。
    無くても創る事は可能ですが、今後WindowsそのものがDirectXベースに移行するので、研究しておきましょう。
    入手はこのサイトから。
    現在の最新版は「Microsoft DirectX 9.0 SDK Update (December 2004)」(DirectX9.0c SDK)です
    日本語ドキュメントは別途インストールする必要が在るので注意し下さい!

Header

  • Wmiのheaderで宣言しているWmi用のインクルード宣言と変数です
    #include <wbemidl.h>   // WBEM(WMI)インターフェース
    #include <WbemProv.h>   // WBEM(WMI)インターフェース
    #include <WbemCli.h>   // WBEM(WMI)インターフェース
  • 変数はheader内でローカル変数として宣言しています
    IWbemLocator*   m_pIWbemLocator;
    IWbemServices*   m_pIWbemServices;
    IEnumWbemClassObject* m_pIEnumWbem;
    IWbemClassObject* m_pIWbemObject;

初期化

  • 必要な初期化部分としては、COMの初期化が必要です
    また、WMIを使用するためのヘッダーも必要ですので、ちょっと過剰かもしれませんが、 私は下記のHeaderとlibraryをインクルードしています。
    あらかじめVisualStudioの設定で、PlatformSDKとWMI SDKへのパスは設定して在ります。
    # pragma warning(disable: 4800)  // ワーニングメッセージの抑制 bool値の強制設定
    # pragma comment(lib, "wbemuuid.lib")
    #define _WIN32_WINNT 0x0400
    #define _WIN32_IE 0x0400
    #define _UNICODE
    #define UNICODE
    #ifdef _DEBUG
    #define
    _CRTDBG_MAP_ALLOC #include <windows.h> // Windows標準ヘッダー #include <windowsx.h> // Windows標準ヘッダー #include <stdio.h> // 標準入出力ヘッダー #include <stdlib.h> #include <atlbase.h> // COMインターフェイス(ATL) #include <ObjBase.h> // COM+インターフェイス(OBJ) #include <crtdbg.h> #else #include <windows.h> // Windows標準ヘッダー #include <windowsx.h> // Windows標準ヘッダー #include <stdio.h> // 標準入出力ヘッダー #include <stdlib.h> #include <atlbase.h> // COMインターフェイス(ATL) #include <ObjBase.h> // COM+インターフェイス(OBJ) #endif

  • 上で少し触れていますが、WMIはCOMベースのシステムなので、あらかじめCOM+をmulti-threadで初期化しあります。
    COMはWMI以外にもDirectXなどでも使用するので、プログラムのMAIN関数の最初に初期化を入れ込んでおくと便利です。
    COMは1度初期化すれば、そのまま使用できるので、何度も初期化しないで下さい。
    COMの開放も、プログラムの最後に行えば問題ありません。
     
  • さて、WMIの初期化ですが、ComSecurity?の設定を行う必要があります。
    次にWMIロケーターを作成します。
    ローケーターの作成時に適切なClassIDを入れないと使用出来ません。
    下記のようなコードになります。
    ///////////////////////////////////////////////////////////////
    // WMIの開始
    ///////////////////////////////////////////////////////////////
    bool CWmiLib::Init(void)
    {
       HRESULT   hr;
       DWORD   dwLen = MAX_COMPUTERNAME_LENGTH + 1;
       TCHAR szServer[MAX_PATH];
       char cTemp[MAX_PATH];
    
       // COMライブラリを使用できるように初期化
       // COMライブラリの初期化は1度だけ行うので、最初に行い個別には行わない
       // メインのルーチンで行えば後は必要ないので、ここに見本で記載
    /*   hr = CoInitializeEx(0, COINIT_MULTITHREADED);
       if (FAILED(hr))
       {
          // エラーだったら終了
          return false;
       }
    */
       g_CompStatus = NULL;
    
       g_CompStatus = new COMPSTATUS;
       if(g_CompStatus == NULL)
       {
          // メモリの確保に失敗
          return false;
       }
    
       ZeroMemory(g_CompStatus, sizeof(COMPSTATUS));
    
       m_pIWbemLocator  = NULL;
       m_pIWbemServices = NULL;
       m_pIEnumWbem = NULL;
    
       ZeroMemory(m_szCompName,sizeof(m_szCompName));
       m_bstrServer = NULL;
       m_ulCount = 0;
    
       // セキュリティー設定 -- WMI
       hr = CoInitializeSecurity( NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT,
                                  RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0);
       if (FAILED(hr))
       {
          return false;
       }
    
       // WMIロケーターの作成
       // Admin(CLSID_WbemAdministrativeLocator)権限ロケーターを作成
       // Windows2000/WindowsXP Proはこのロケーターで無ければ取得不能
       hr = CoCreateInstance(CLSID_WbemAdministrativeLocator, NULL, CLSCTX_INPROC_SERVER,
                             IID_IWbemLocator, (LPVOID *) &m_pIWbemLocator);
       if (FAILED(hr))
       {
          return false;
       }
    
       ZeroMemory(m_szCompName, sizeof(m_szCompName));
       GetComputerName( m_szCompName, &dwLen );
    
       lstrcpy(szServer, _T("\\\\.\\root\\cimv2"));
    
       m_bstrServer = SysAllocString(szServer);
       if (m_bstrServer == NULL)
       {
          Clear();
          return false;
       }
    
       // ロケーターをサービスに接続
       hr = m_pIWbemLocator->ConnectServer((BSTR)m_bstrServer,NULL,NULL, 0L,0L,
                                               NULL, NULL, &m_pIWbemServices);
       if (FAILED(hr))
       {
          InstanceErrStr(hr,cTemp);
          Clear();
          return false;
       }
    
       // セキュリティ設定
       hr = CoSetProxyBlanket(m_pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
                              NULL, RPC_C_AUTHN_LEVEL_CALL, 
                              RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
       if (FAILED(hr))
       {
          InstanceErrStr(hr,cTemp);
          Clear();
          return false;
       }
    
       // コンピュータネームを保持
       WideCharToMultiByte(CP_ACP , 0 , m_szCompName,
                           -1 , (char*)g_CompStatus->lpCompName ,
                           lstrlenW(m_szCompName), NULL , NULL);
    
       return true;
    }
    
    

終了処理

  • 終了部分もあわせて、記載しておきます。
    きちんと終了しないと、メモリーリークの元なので注意!
    ///////////////////////////////////////////////////////////////
    // WMIの終了
    ///////////////////////////////////////////////////////////////
    void CWmiLib::Clear(void)
    {
       if(m_bstrServer != NULL)
       {
          SysFreeString((BSTR)m_bstrServer);
       }
    
       if(m_pIWbemObject != NULL)
       {
          m_pIWbemObject->Release();
          m_pIWbemObject = NULL;
       }
    
       if(m_pIEnumWbem != NULL)
       {
          m_pIEnumWbem->Release();
          m_pIEnumWbem = NULL;
       }
    
       if(m_pIWbemServices != NULL)
       {
          m_pIWbemServices->Release();
          m_pIWbemServices = NULL;
       }
    
       if(m_pIWbemLocator != NULL)
       {
          m_pIWbemLocator->Release();
          m_pIWbemLocator = NULL;
       }
    }
    

Wmi Command?

  • 初期化が終了すれば、WMIでWindowsの情報を取得出来ます。
    情報の取得はWMI ServiceからEnumする事で取り出せます。
    Enumには「Win32_*」を使用して各種情報を取得します。
    Win32_*には「Win32_BIOS」や「Win32_VideoController?」等が在ります。
    Sampleとして、「Win32_VideoController?」でのコードを掲載しておきます。
    他のWin32_*や、取得できる情報の詳細に関してはWMI SDKのドキュメントを読んで下さい。
    ///////////////////////////////////////////////////////////////
    // VideoController情報の取得
    ///////////////////////////////////////////////////////////////
    bool CWmiLib::GetVideoControllerInfo(VIDEODEVICE *m_CompStatus)
    {
      HRESULT  hr;
      ULONG i = 0;
    	char  cSize[MAX_PATH];      // 容量
      _int64    iSize = 0;
      char cTemp[MAX_PATH];
      bool  bVideo = true;
    
      // Win32_VideoController情報の取得を開始
      // SQL文風に取得することも可能
      // hr = m_pIWbemServices->ExecQuery(L"WQL", L"SELECT * Win32_VideoController", 
                              WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
                              iWbemContext, &m_pIEnumWbem); 
      hr = m_pIWbemServices->CreateInstanceEnum(L"Win32_VideoController",
                                  WBEM_FLAG_SHALLOW|WBEM_FLAG_FORWARD_ONLY,
                                  NULL, &m_pIEnumWbem);
      if ( hr != WBEM_S_NO_ERROR)
      {
        // エラーになった場合 Win32_DisplayConfiguration で取得
        hr = m_pIWbemServices->CreateInstanceEnum(L"Win32_DisplayConfiguration",
                                    WBEM_FLAG_SHALLOW|WBEM_FLAG_FORWARD_ONLY,
                                    NULL, &m_pIEnumWbem);
        if ( hr != WBEM_S_NO_ERROR)
        {
          InstanceErrStr(hr,cTemp);
          OBJClear();
          return false;
        }
        else
        {
         bVideo = false;
        }
      }
    
      // OBJECTに入れる
      while(m_pIEnumWbem->Next(WBEM_INFINITE, 1, &m_pIWbemObject, &m_ulCount) == WBEM_S_NO_ERROR)
      {
       m_CompStatus[i].bVga = true;
    
       if(bVideo)
       {
         GetStringValue(m_pIWbemObject, L"AdapterCompatibility", 
                   (char*)m_CompStatus[i].cAdapterCompatibility, MAX_PATH);
    
         GetStringValue(m_pIWbemObject, L"AdapterDACType", 
                   (char*)m_CompStatus[i].cAdapterDACType, MAX_PATH);
    
         GetIntValue(m_pIWbemObject, L"AdapterRAM", (long*)&iSize);
         ZeroMemory(cSize , sizeof(cSize));
         if(iSize >= 1048576)
         {
           iSize = iSize / 1048576;
           _i64toa(iSize , (char*)cSize , 10);
           strcat(cSize, " MB");
           lstrcpy((LPWSTR)m_CompStatus[i].cAdapterRAM, (LPWSTR)cSize);
         }
         else
         {
           _i64toa(iSize , (char*)cSize , 10);
           strcat(cSize, " KB");
           lstrcpy((LPWSTR)m_CompStatus[i].cAdapterRAM, (LPWSTR)cSize);
         }
    
         // インストールされているドライバの名前とバージョンを取得
         // ただしWMIで取得可能なバージョンは実際のバージョンとは異なる場合がある
         GetStringValue(m_pIWbemObject, L"InstalledDisplayDrivers", 
                   (char*)m_CompStatus[i].cDriverName, MAX_PATH);
    
         GetStringValue(m_pIWbemObject, L"DriverVersion", 
                   (char*)m_CompStatus[i].cDriverVersion, MAX_PATH);
         //  VariantClear(&var);
         //  m_pIWbemObject->Get(L"Caption", 0, &var, NULL, NULL);
         //  VariantClear(&var);
         //  m_pIWbemObject->Get(L"DriverDate", 0, &var, NULL, NULL);
    
         GetStringValue(m_pIWbemObject, L"Name", (char*)m_CompStatus[i].cName, MAX_PATH);
    
         GetStringValue(m_pIWbemObject, L"VideoProcessor", 
                   (char*)m_CompStatus[i].cVideoProcessor, MAX_PATH);
       }
       else
       {
         lstrcpy((LPWSTR)m_CompStatus[i].cAdapterCompatibility, (LPWSTR)"not found");
         lstrcpy((LPWSTR)m_CompStatus[i].cAdapterDACType, (LPWSTR)"not found");
         lstrcpy((LPWSTR)m_CompStatus[i].cAdapterRAM, (LPWSTR)"not found");
         lstrcpy((LPWSTR)m_CompStatus[i].cDriverName, (LPWSTR)"not found");
         lstrcpy((LPWSTR)m_CompStatus[i].cDriverVersion, (LPWSTR)"not found");
    
         GetStringValue(m_pIWbemObject, L"DeviceName", (char*)m_CompStatus[i].cName, MAX_PATH);
    
         lstrcpy((LPWSTR)m_CompStatus[i].cVideoProcessor, (LPWSTR)"not found");
       }
       i++;
      }
    
      g_CompstatusCount.iVideoDevice = i;
    
      OBJClear();
      return true;
    }
    
  • ↑のような形で、ビデオカードの情報を取得出来ます。
    取得できる情報はもう少し在りますが、この辺りの情報が取得できれば十分だと思います。

Wmiコマンド解説?

  • m_CompStatus?」は取得した情報を格納するための自作構造体です。
  • 「m_pIWbemServices?->CreateInstanceEnum?
    WMI ServiceをWin32_VideoController?で EnumしてWin32_*を取得開始する。
  • 「m_pIEnumWbem?->Next」
    WMI objectを取得して、そのobjectを元に情報を取得します。
  • VariantClear?(&var);」は「->Get」で取得した情報を確保したら、必ず行って下さい。
    これを行わないと次の情報が取得できなかったり、メモリリークの元です。
    このプログラムでは「GetStringValue?」など関数にして情報取得を一元化しています。
  • 他の情報を続けて取得する場合ですが、WMI ObjectとWMI Enumだけを開放すれば、
    可能なので、WMI ServiceとWMI Locatorは開放する必要は在りません。
    WMI情報の取得は時間が結構かかるので、出来るだけ簡略化できるところは簡略に済ませましょう。