[WMI : C++] WMI プロバイダ以外 CoCreateInstance には CLSID_WbemAdministrativeLocator クラス識別子を指定してはいけない

今日は WMI コンポーネント使用時の CoCreateInstance() で指定するクラス識別子の違いについてのお話しをご紹介させていただきます。

【概要】
WMI を使用するプログラムを C++ で作成する際に、まず IWbemLocator インターフェースを取得して、そこから WMI の実行ターゲットコンピュータ(ローカルまたはリモート)の IWbemServices インターフェース (= WMI サービス) を取得する処理を行います。
このまず最初の IWbemLocator インターフェースを取得する際に CoCreateInstance() の第一引数 REFCLSID には、プログラムの目的によって以下のように指定するクラス識別子を使い分ける必要があります。

・WMI プロバイダ自体(あるいはフィジカル コンシューマー)を作成している場合
CLSID_WbemAdministrativeLocator クラス識別子

・それ以外の一般的な WMI クライアント アプリケーションの場合 (端的にいえば WMI の処理を実施するだけのアプリケーション)⇒ CLSID_WbemLocator クラス識別子

【詳細】
-a. CoCreateInstance() の使用法について (1) WMI プロバイダ作成の場合 - “CLSID_WbemAdministrativeLocator“

例えば、以下の MSDN ライブラリ " IWbemServices Interface " の項の例では、CLSID_WbemAdministrativeLocator クラス識別子を指定してロケータを取得していますが、非常に判りづらいのですが、以下に小さく書いてあるようにこれは WMI プロバイダを作成する場合の呼び出し例となります。

IWbemLocator *pIWbemLocator = NULL;

HRESULT hRes = CoCreateInstance (             CLSID_WbemAdministrativeLocator,             NULL ,             CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER ,             IID_IUnknown ,             ( void ** ) &pIWbemLocator             ) ;

IWbemServices Interface
https://msdn.microsoft.com/en-us/library/aa392093(VS.85).aspx

image

(2) WMI を使うアプリケーション作成の場合 - “CLSID_WbemLocator“ 通常は、WMI のプロバイダ自体を実装されるよりも、何らかの値を取得したり、システムの設定を操作したりするだけの目的で WMI を使う用途が多いと思います。そうした場合は、以下のように CLSID_WbemLocator を指定してください。

IWbemLocator *pIWbemLocator = 0;

HRESULT hRes = CoCreateInstance(             CLSID_WbemLocator,                         0,             CLSCTX_INPROC_SERVER,             IID_IWbemLocator, (LPVOID *) &pIWbemLocator);

Example: Creating a WMI Application
https://msdn.microsoft.com/en-us/library/aa390418(VS.85).aspx

-b. 通常の WMI アプリケーションに CLSID_WbemAdministrativeLocator を指定した場合の影響
WMI の基本設計上、CLSID_WbemAdministrativeLocator が使われるのは WMI プロバイダ(あるいはフィジカル コンシューマー) のみが想定されるシナリオであり、WMI アプリケーションに CLSID_WbemAdministrativeLocator を指定した場合、OS 上の想定動作保障外となることに注意が必要です。 結果として期待通りの動作となる場合もありますが、基本的に想定外の動作の結果であり、システム コンディションや WMI の動作状況、リポジトリの状態などにより突然動作しなくなるといった状況に陥る可能性も否定できません。
特に、Windows 2000 上では問題が顕著に現れる可能性があります。

この点について直感的に理解できるレベルのドキュメントは存在しませんが、以下の MSDN の記述にあるように 「WMI のプロバイダの場合」あるいは「フィジカル コンシューマー」などの場合は CLSID_ WbemAdministrativeLocator クラス識別子を指定するように、というような内容があります。

Implementing a Physical Consumer
<https://msdn.microsoft.com/en-us/library/aa390871(VS.85).aspx>

IWbemProviderInit::Initialize Method
<https://msdn.microsoft.com/en-us/library/aa391858(VS.85).aspx>

-c. WMI 動作基盤の違い - Windows 2000 以前と XP 以降
実は、WMI の動作基盤は Windows 2000 以前と XP 以降で大きく設計が変わっています。

(1) Windows 2000 の場合
WMI 動作基盤は、Windows 2000 以前 (2000 含む) では Winmgmt.exe という名前のプロセスでした。
このプロセスはサービス プロセスで、WMI の動作基盤(コア)部分と、WMI プロバイダ双方をホストしていました。この状況は残念ながら堅牢とはいえませんでした。
なぜならば、1 つのサービスで動作する以上、WMI コアと WMI プロバイダ双方から処理を実行した場合、ブロッキング状態が引き起こされる可能性があったからです。そこで、Windows 2000 の場合、WMI はインプロセス プロバイダがこの CLSID_WBemAdministrativeLocator クラス識別子を使用するようにし、その他の一般的な WMI の処理と区別するようにすることでブロッキングを防ぐような機構を採用していました。
これにより、WMI プロバイダと他の WMI クライアント呼び出しのコネクションを区別し、クライアント接続を確立させるように動作させています。

しかし、クライアント アプリケーションで CLSID_WBemAdministrativeLocator インターフェースが指定された場合、この動作の前提が崩れてしまいます。コネクションの区別をする際、本来 WMI プロバイダとして振舞うべきではない一般 WMI アプリケーションがプロバイダのコネクションを使って動作してしまう可能性があるからです。
こうした事情により、WMI プロバイダ(あるいはフィジカル コンシューマー)とその他 WMI アプリケーション全般で、ロケータを取得する際に指定するクラス識別子を分けていただく必要があるのです。

(2) Windows XP の場合
Windows XP 以降では、コア WMI 自体が別のサービスに分離されました。Windows XP の場合、WMI プロバイダは Wmiprvse.exe プロセスに読み込まれます。WMIPrvSE.exe = WMI Provider Host です。

image
プロセス分離により、WMI の動作基盤の安全性は向上いたしましたものの、Windows 2000 と同様、WMI プロバイダが CLSID_WBemAdministrativeLocator を使いクライアントは CLSID_WBemLocator を使用するという前提で OS の設計がなされていることは Windows 2000 と同様 Windows XP 以降も変わりはありません。

-d. "正常動作している" 場合の判断について
ブロッキング状態などの動作時の問題は、システム コンディションやシステムで使用されるさまざまな WMI の動作状況などによって発生頻度などは変わりますので結果的に想定の動作となる場合もありますが、想定外の実装のアプリケーションを動作させている限り潜在的な問題が存在することになりますので、注意が必要です。

(WMI Programming Support Team - June 17, 2010)