WMI.NET Provider Extension 2.0 を使用した結合 WMI プロバイダーの作成

WMI.NET Provider Extension 2.0 を使用した結合 WMI プロバイダーの作成

ガブリエル ギジラ

Microsoft Corporation

2008 年 1 月

 

概要:.NET Framework 3.5 に付属 WMI.NET Provider Extension 2.0 を使用して、結合 WMI プロバイダーを作成する方法について詳しく説明します

内容

はじめに

単純な .NET クラス

アセンブリ レベル属性

クラス レベルの WMI.NET 属性

ランタイム要件

WMI を使用した登録

継承によるクラスの拡張

メソッドの実装

例外とエラー報告

その他のヒント

まとめ

リスト 1 – SCMInterop.cs

リスト 2 – WIN32ServiceHost.cs

 

はじめに

Windows Management Instrumentation (WMI) は、Windows アプリケーションと Windows アプリケーションを管理するために広く使用されているインフラストラクチャです。 システム管理者や管理アプリケーションの間で非常に拡張可能で人気があるにもかかわらず、多くの開発者は、実装する必要があるネイティブ インターフェイスの複雑さにより、WMI プロバイダーの作成について考える必要があります。

.NET Frameworkの初期バージョンには、WMI プロバイダーを実装するための一連のオブジェクトとパターンが付属していましたが、これらはアプリケーション管理に限定されていましたが、メソッドを定義することはできませんでした。また、インスタンスのキーが自動的に生成されました。 WMI.NET プロバイダー拡張機能 v2 (WMI.NET) は、Orcas (.NET Framework 3.5) の新しいインフラストラクチャであり、WMI プロバイダー機能の完全なセットを実装できます。 この新しいインフラストラクチャは、以前のバージョンの WMI.NET プロバイダー モデルと共存しますが、はるかに強力で拡張可能です。

この記事の焦点は、WMI.NET で最も重要な機能の 1 つである WMI 結合プロバイダーを記述する方法です。 分離されたプロバイダーの記述方法に大きな違いはありません。そのため、この記事では、WMI.NET を使用して任意の種類の WMI プロバイダーを記述しようとする読者に良いスタートを切ることができます。 この記事では、単純な .NET クラスから始まる結合 WMI プロバイダーを作成し、いくつかの追加機能で強化する方法について説明します。 目標は、Windows サービスをホストするプロセスを列挙し、このような各プロセスで Windows サービスを列挙し、この機能を WMI に統合できることです。

単純な .NET クラス

まず、Windows サービスをホストするプロセスをモデル化する C# クラスを作成します。 各インスタンスには、関連付けられたプロセスでホストされているサービスの一覧が表示されます。 クラスには、システムで実行されているサービス ホストに関連付けられているすべてのインスタンスを返す静的メソッドがあります。

    パブリック クラス WIN32ServiceHost

    {

クラスは Process クラスのラッパーです。 innerProcess フィールドには、Process オブジェクトへの参照が格納されます。

        innerProcess を処理する。

クラスには、プロセス オブジェクトをパラメーターとして受け入れる 1 つのコンストラクターがあります。 

        public WIN32ServiceHost(Process innerProcess)

        {

            this.innerProcess = innerProcess;

        }

プロセス ID のアクセサーが含まれています。

        public int ID

        {

            get { return this.innerProcess.Id; }

        }

Services プロパティは、ホストされているサービスの名前を持つ配列を返します。 プロセスでサービスが実行されていない場合、このプロパティは null です。

        public string[] Services

        {

            get

            {

アイドル プロセスでは、サービスはホストされません。

                if (innerProcess.Id == 0)

                    null を返します。

システム上のすべての Windows サービスの一覧を取得する

                ServiceController[] services = ServiceController.GetServices();

                List<ServiceController> servicesForProcess = 新しい List<ServiceController>();

                using (SCM scm = new SCM())

                {

                    for (int svcIndex = 0;svcIndex < services)。長さ;svcIndex++)

                    {

                        ServiceController crtService = services[svcIndex];

                        int processId = scm。GetProcessId(crtService.ServiceName);

サービスが実行されているプロセスの ID を、現在のプロセスの ID と比較します。

                        if (processId == innerProcess.Id)

                        {

                            servicesForProcess.Add(services[svcIndex]);

                        }

                    }

                }

 

                if (servicesForProcess.Count == 0)

                    null を返します。

サービスの名前を使用して配列を準備、設定、および返す

                string[] servicesNames = new string[servicesForProcess.Count];

 

                for (int serviceIdx = 0; serviceIdx < servicesForProcess.Count; serviceIdx++)

                {

                    servicesNames[serviceIdx] = servicesForProcess[serviceIdx].Servicename;

                }

                return servicesNames;

            }

        }

EnumerateServiceHosts は、実行中のすべてのサービス ホストを通過するために IEnumerable を返す静的メソッドです。

        静的パブリック IEnumerable EnumerateServiceHosts()

        {

            Process[] processes = Process.GetProcesses();

            foreach (プロセスでの crtProcess の処理)

            {

                WIN32ServiceHost crtServiceHost = 新しい WIN32ServiceHost(crtProcess);

                if (crtServiceHost.Services != null)

                {

                    yield return crtServiceHost;

                }

            }

        }

    }

開発者は、任意の .NET アプリケーションからこのクラスを使用できます。 ただし、この場合、他のさまざまな管理アプリケーションで使用する方法はありません。 WMI.NET には、この例の クラスを WMI ワールドに公開するためのフックがあり、次の段落でプロセスについて説明します。 WMI は、このようなオブジェクトを活用し、それらを Systems Management Server または Operations Manager としてエンタープライズ スケール管理アプリケーションに統合し、リモート操作を提供し、複数のプラットフォームからこのクラスを表示して使用できるようにするモデルを提供します。

アセンブリ レベル属性

WMI.NET を使用してインストルメンテーション用のアセンブリを公開する最初の手順は、アセンブリ レベルで WmiConfiguration 属性を設定することです。 この属性は、アセンブリを WMI.NET プロバイダーを実装するアセンブリとしてマークし、公開される名前空間など、プロバイダーによって実装されるクラスに関するさまざまなことを構成できます。 この例では、WMI 名前空間を root\Test に定義し、NetworkService セキュリティ コンテキストでホスティング モデルを結合プロバイダー モデルに設定します。 すべての操作は、偽装によって呼び出し元ユーザーのセキュリティ コンテキストで実行されることに注意してください。

[assembly: WmiConfiguration(@"root\Test", HostingModel = ManagementHostingModel.NetworkService)]

クラス レベルの WMI.NET 属性

WMI.NET を使用してクラスをインストルメント化するには、クラス、そのメソッド、フィールド、およびプロパティ WMI.NET 公開され、WMI.NET 属性で適切にマークされている必要があります。 属性は、WMI クラスとして既存の C# クラスを使用するために WMI で必要なメタデータを生成するために使用されます。

.NET クラスのインストルメント化

ManagementEntity 属性は、.NET クラスをインストルメント化対象としてマークします。 デプロイ時に、WMI.NET インフラストラクチャによって、WMI リポジトリに同じ名前の対応する WMI クラスが生成されます。 この名前を変更するには、ManagementEntity 属性の引数リストに名前付きパラメーター Name を指定する必要があります。 Name は、ほとんどの属性のインストルメンテーション名を変更するために名前付きパラメーターとして使用されます。 この例では、.NET クラスの名前を変更せずに WMI クラスにWIN32_ServiceHost 名前を付けます。

    [ManagementEntity(Name = "WIN32_ServiceHost")]

    パブリック クラス WIN32ServiceHost

エンティティの名前付けは少し難しい場合があることに注意してください。  C# では大文字と小文字は区別されますが、WMI は大文字と小文字は区別されません。 そのため、すべての名前は、WMI.NET インフラストラクチャの観点から大文字と小文字を区別しないものとして扱われます。 同じクラスのメンバーである 2 つのフィールドまたは 2 つのメソッドの名前が大文字と小文字のみで異なる場合、アセンブリは WMI への登録に失敗します。

WMI クラス スキーマの制御

インスタンスのペイロードは、そのプロパティによって指定されます。 ManagementProbeManagementConfiguration、または ManagementKey 属性を使用して、WMI ワールドに反映されるすべてのプロパティをマークする必要があります。 ManagementProbe は、WMI に読み取り専用として公開されるプロパティをマークする属性です。 このサンプルでは、Services プロパティは、管理ワールドに公開するペイロード プロパティです。 直接変更できないプロセスの状態が表示されるため、 ManagementProbe でマークします。 読み取り/書き込みプロパティの場合、 ManagementConfiguration 属性を使用する必要があります。この場合、プロパティ自体にセッターとゲッターの両方が必要です。

        [ManagementProbe]

        public string[] Services

ID は、プロセス自体を識別する WIN32ServiceHost のペイロードの一部でもあります。 クラスのインスタンスを一意に識別するプロパティは、WMI ワールド内のそのクラスのキーです。 WMI.NET のすべてのクラスは、既にキーを定義しているクラスから抽象、シングルトン、または継承しない限り、キーを定義し、それらを実装してインスタンスを一意に識別する必要があります。 キーは ManagementKey 属性でマークされます。 このサンプルでは、プロセス ID によってプロセスが一意に識別されます。そのため、クラスのインスタンスを一意に識別します。

        [ManagementKey]

        public int ID

ランタイム要件

クラスとそのプロパティのコレクションをマークすると、クラス スキーマを WMI に公開できますが、クラスで実際のデータを公開しても十分ではありません。 インスタンスの取得、作成、削除、およびインスタンスの列挙のエントリ ポイントを定義する必要があります。 インスタンス列挙のコードと、機能的な WMI プロバイダーにとって実質的に最小である特定のインスタンスを取得するコードを提供します。 

インスタンスの列挙

インスタンスを列挙するために、WMI.NET は IEnumerable インターフェイスを返すパラメーターを持たない静的パブリック メソッドを想定しています。 クラス全体に関連する機能を実装するため、静的である必要があります。 このメソッドは 、ManagementEnumerator 属性でマークする必要があります。 このサンプルで既に定義されている EnumerateServiceHosts は、属性を設定してこの目的で使用できるように、すべての要件を満たしています。

        [ManagementEnumerator]

        静的パブリック IEnumerable EnumerateServiceHosts()

このメソッドでは、列挙で返される各要素がインストルメント化されたクラスのインスタンスであることを確認することが本当に重要です。 そうしないと、ランタイム エラーが発生します。

インスタンスへのバインド

特定のインスタンス (WMI.NET ではバインディングと呼ばれます) を取得できるようにするには、それを識別するキーの値に基づいてインスタンスを返すメソッドが必要です。 キーと同じ数のパラメーターを持ち、パラメーターの名前と型がキーと同じであるメソッドが必要です。 戻り値の型は、インストルメント化されたクラス自体である必要があります。 静的メソッドを使用します。クラスのキーをパラメーターに関連付けるには、キーと同じインストルメント化された名前をパラメーターに指定する必要があります。  このサンプルでは、 IDWIN32_ServiceHost クラスのキーであり、バインド メソッド ID のパラメーターに名前を付けるか、 ManagementName 属性を使用して"ID" という名前で WMI にパラメーターを公開する必要があります。 WMI.NET インフラストラクチャは、 ManagementBind 属性でマークされると、バインド メソッドまたはコンストラクターを認識します。

        [ManagementBind]

        静的パブリック WIN32ServiceHost GetInstance([ManagementName("ID")] int processId)

        {

            試す

            {

                Process process = Process.GetProcessById(processId);

                WIN32ServiceHost crtServiceHost = 新しい WIN32ServiceHost(process);

                if (crtServiceHost.Services != null)

                {

                    crtServiceHost を返します。

                }

                else

                {

                    null を返します。

                }

            }

            指定された ID を持つプロセスが見つからない場合は、GetProcessById によってスローされます

            catch (ArgumentException)

            {

                null を返します。

            }

        }

インスタンスが見つからない場合、メソッドは null を返します。 この場合、要求されたプロセスがまったく見つからない場合、または見つかったプロセスが Windows サービスをホストしていない場合は、これを行います。

WMI を使用した登録

クラスは作業を行う準備ができていますが、WMI に登録し、読み込み元のディスク上のアクセス可能な場所に配置する必要があります。

グローバル アセンブリ キャッシュ (GAC) は、アセンブリを配置する場所です。 そうすることで、.NET は完全な .NET 名で取得できます。 アセンブリを GAC に登録するには、.NET の厳密な名前が必要であることに注意してください。 この例では、.NET gacutil.exe ツールを使用してアセンブリを GAC に格納します。

インストルメント化されたアセンブリから WMI メタデータに情報を取得するには、.NET ツール InstallUtil.exeを使用して DefaultManagementInstaller という名前の WMI.NET クラスを呼び出す必要があります。 DefaultManagementInstaller は 、インストルメント化されたクラスのアセンブリ全体を解析し、対応する WMI メタデータを生成する方法を認識しています。 InstallUtil.exe RunInstaller 属性でマークされたクラスが必要な場合は、DefaultManagementInstaller から派生した空のクラスInstallUtil.exe定義します。

    [System.ComponentModel.RunInstaller(true)]

    public クラス MyInstall : DefaultManagementInstaller

    {

    }

WMI への登録は、オフラインまたはオンラインで行うことができます。 オンライン登録では、アセンブリのインストルメンテーション メタデータが WMI リポジトリに直接格納されます。 WMI リポジトリにメタデータを直接インストールするために、InstallUtil.exe コマンドがアセンブリ名をパラメーターとして呼び出されます。 アセンブリは、InstallUtil.exeを実行する前に GAC に存在する必要があります。 この例で生成された WMIServiceHost.dllという名前 のアセンブリでは、次のコマンドを使用します。

C:> /i WMIServiceHost.dllをgacutil.exeする

C:>Installutil.exe WMIServiceHost.dll

オフライン登録には 2 つの手順が必要です。 最初の手順では、アセンブリに関連付けられている WMI メタデータを生成し、MOF ファイルに格納します。 2 番目の手順は、 mofcomp.exe ツールを使用するか、インストール パッケージの一部としてターゲット マシンに実際に登録することです。 オフライン登録の利点は、MOF ファイルを必要に応じてローカライズして変更できることです。 この例では、次のように MOF パラメーターを使用して 、WMIServiceHost.mof という名前のファイルに WMI メタデータを生成して格納できます。

C:> /MOF=WMIServiceHost.mof WMIServiceHost.dllをInstallutil.exeします

オンラインの場合と同様に、アセンブリはターゲット コンピューター上の GAC に存在する必要があります。 デプロイを検証するために、 wmic.exe システム ツールを使用して、このクラスのインスタンスと値を確認できます。

C:>wmic /NAMESPACE:\\root\test PATH win32_servicehost get /value

開発中は、アセンブリが格納されている GAC 内の同じフォルダーにシンボルを展開すると便利です。 このようにして、エラーまたはクラッシュの結果として報告されたスタックには、問題のあるコードを特定するのに役立つ完全なソース パスと行番号が含まれます。

継承によるクラスの拡張

WIN32_ServiceHostはサービス ホストに関するものです。提供される情報は、Windows サービスをホストするプロセスに限定されます。 この情報を拡張して、メモリ使用量、実行可能パス、セッション ID などのプロセス固有の情報を含めるのが興味深いでしょう。 この情報を取得するために、スキーマを拡張し、必要な量の情報を取得するためのコードをさらに記述します。 余分なコードを記述する代わりに、root\cimv2 名前空間のオペレーティング システムに既に存在する WIN32_Process クラスを利用し、システムで実行されているすべてのプロセスに対してこの追加情報をすべて提供します。 このクラスは実行中のプロセスに関する広範な情報を提供し、WMI 派生を使用して独自のクラスで拡張できます。

WMI 継承は、WMI.NET コーディング モデルでクラス継承に変換されます。 派生元のクラスは、実際にコードに実装していない WMI 空間からのクラスであるため、特定の方法でマークする必要があります。

派生の記述を開始する前に、 WIN32_Process クラスに関する 2 つの重要な点に注意する必要があります。 最初 のWIN32_Processroot\cimv2 名前空間にあり、派生では、win32_servicehost クラスを同じ名前空間に登録する必要があります。 そのため、 WmiConfiguration 属性ステートメントを少し変更します。

[assembly: WmiConfiguration(@"root\cimv2", HostingModel = ManagementHostingModel.NetworkService)]

さらに、スーパークラスwin32_processには、 プロパティ Handle がキーとして定義されています。 このキーは CIM_STRING 型で、 に変換されます。NET の System.String。 キー プロパティとして ID の使用を停止し、代わりに プロパティ Handle を使用する必要があります。

win32_processに一致するように WMI.NET で外部クラスを定義するには、そのスキーマをミラーしますが、使用する必要があるプロパティのみを含めます。 クラス階層のキーは常に必要です。 現時点では 、Handle はキーであるため、唯一の興味深いプロパティであり、特定のインスタンスへのバインドに必要になります。

    [ManagementEntity(External = true)]

    抽象パブリック クラス Win32_Process

    {

        保護された文字列ハンドル。

 

        [ManagementKey]

        public string Handle

        {

            get {

                this.handle を返します。

            }

        }

    }

ManagementEntity 属性で External を true に設定すると、デプロイ時にインフラストラクチャで WMI メタデータが生成されなくなりますが、派生クラスのキーとプロパティを検索する必要がある場合は、実行時に使用するために宣言された情報が保持されます。 キーは WMI サブシステムによってさまざまなプロバイダーからの情報をマージするために使用されるため、基本クラスのキーの内容を制御することは非常に重要であることに注意してください。

WMI クラスを取得WIN32_ServiceHost WMI クラス Win32Process を継承するには、.NET ワールドで新しく作成された抽象クラスから WIN32ServiceHost を派生させます。

    [ManagementEntity(Name = "WIN32_ServiceHost")]

    public クラス WIN32ServiceHost: Win32_Process

   

ID プロパティを削除します。

        [ManagementKey]

        public int ID

        {

            get { return this.innerProcess.Id; }

        }

コンストラクターを変更して、基底クラスのハンドル フィールドに新しいキーを設定します

        public WIN32ServiceHost(Process innerProcess)

        {

            this.innerProcess = innerProcess;

            this.handle = innerProcess.Id.ToString();

        }

GetInstance を変更して 、Handle という名前の文字列引数を操作します。残りは同じままであるため、その最初の数行だけです。

        [ManagementBind]

        静的パブリック WIN32ServiceHost GetInstance(string Handle)

        {

            int processId;

            if (!Int32.TryParse(Handle, out processId))

            {

                null を返します。

            }

 

            試す

            [...]

GAC で新しいアセンブリを再コンパイルして再デプロイする必要があります。 InstallUtil.exeを使用して新しいスキーマをデプロイします。 少し変更された wmic.exe コマンドを使用してシステムにクエリを実行できるよりも。

C:>wmic /NAMESPACE:\\root\cimv2 PATH win32_servicehost get /value

返されるインスタンスには、win32_processとwin32_servicehostの両方のクラスからの情報 設定されます。 出力では、サービスは win32_servicehost から取得され、他のすべてが win32_processから取得されます。 出力を簡略化するために、必要な列を指定できます。

C:>wmic PATH win32_servicehostハンドル、キャプション、CommandLine、Services /value を取得する

win32_processを列挙しようとすると、さらに興味深くなります。 このようなクエリでは、すべてのプロセスが返され、win32_servicehostのインスタンスのためだけに [サービス] フィールドが設定されます。

C:>wmic PATH win32_process get /value

出力は少し圧倒される可能性があるため、(コマンド ラインの末尾にout.txtを追加 > して) ファイルにダンプし、メモ帳で開いて Services プロパティを検索します。 何が起こっているのかを理解するために、各インスタンスの WMI クラスを識別するシステム プロパティを示すことができます。

C:>wmic PATH win32_process Get Handle,CommandLine,__CLASS /value

結果の一覧から、win32_ServiceHost インスタンスを選択し、その値を表示します。

C:>wmic path WIN32_Process.Handle="536" get /value

Windows スクリプト、Microsoft PowerShell、マネージド コード、またはネイティブ コードを使用して、任意の WMI クライアント アプリケーションから同様の操作を実行できます。システムは、このアセンブリを他のプロバイダーと同じように扱います。

メソッドの実装

WMI.NET では、静的メソッドとインスタンス単位の両方のメソッドがサポートされます。 ここでは、プロセスによってホストされているすべてのサービスを停止するメソッドを追加し、サービスの実行中にプロセスを強制終了することなく、プロセスをクリーンに停止できるようにします。 パブリック メソッドを WMI で表示するには、 ManagementTask 属性でマークします。

        [ManagementTask]

        public bool StopServices(int millisecondsWait)

        {

            if (innerProcess.Id == 0)

                false を返します。

            ServiceController[] services = ServiceController.GetServices();

 

            bool oneFailed = false;

            using (SCM scm = new SCM())

            {

                for (int svcIndex = 0;svcIndex < services。長さ;svcIndex++)

                {

                    ServiceController crtService = services[svcIndex];

                    int processId = scm。GetProcessId(crtService.ServiceName);

                    if (processId == innerProcess.Id)

                    {

                        試す

                        {

                            crtService.Stop();

                            if (millisecondsWait != 0)

                            {

                                crtService.WaitForStatus( ServiceControllerStatus.Stopped,

                                                            new TimeSpan((long)millisecondsWait * 10000));

                            }

                        }

                        catch (System.ServiceProcess.TimeoutException)

                        {

                            oneFailed = true;

                        }

                        catch (System.ComponentModel.Win32Exception)

                        {

                            oneFailed = true;

                        }

                        catch (InvalidOperationException)

                        {

                            oneFailed = true;

                        }

                    }

                }

            }

            return !oneFailed;

        }

 このメソッドを呼び出すには、win32_servicehost クラスのインスタンスが必要です。 次のように入力すると、使用可能なサービス ホストの一覧が表示されます。

C:>wmic パス win32_servicehostハンドルの取得,サービス

そして、サービスの最も無害なリストを持つものを選択し(システムを停止しないように、また、一部のサービスも停止できない可能性があります)、その Handle プロパティを使用して呼び出しのインスタンスを識別します。

C:>wmic パス win32_servicehost。Handle="540" CALL StopServices(0)

例外とエラー報告

例外は、WMI.NET の重要な側面です。 インフラストラクチャでは、いくつかの例外を使用して情報を通信し、ほとんどの例外を未処理として扱います。

受け入れられた例外

WMI.NET では、この記事で詳しく説明する少数の例外のみを処理します。 その他のすべての例外はプログラミング エラーと見なされ、未処理の例外として扱われ、WMI プロバイダー ホストがクラッシュします。 WMI.NET は、Windows イベント ログに未処理の例外を報告します。

受け入れられた例外は、実際には WMI エラー コードに変換され、表 1 に示すようにクライアント コードに返送されます。 したがって、WMI.NET プロバイダーは、他のネイティブ プロバイダーとして動作します。

System.OutOfMemoryException

WBEM_E_OUT_OF_MEMORY

System.Security.SecurityException

WBEM_E_ACCESS_DENIED

System.ArgumentException

WBEM_E_INVALID_PARAMETER

System.ArgumentOutOfRangeException

WBEM_E_INVALID_PARAMETER

System.InvalidOperationException

WBEM_E_INVALID_OPERATION

System.Management.Instrumentation.InstanceNotFoundException

WBEM_E_NOT_FOUND

System.Management.Instrumentation.InstrumentationException

内部例外から、この記事で詳しく説明します

表 1 – WMI エラーへの例外の変換

上記の一覧では、2 つを除くすべてが一般的な .NET Framework 例外です。 一覧表示されているすべての例外をスローして、これらの例外が表す特定の状態についてクライアントに報告できます。

さらに説明するように、 System.Management.Instrumentation 名前空間に 2 つの新しい例外が追加されました。

InstanceNotFoundException

この例外は、要求されたインスタンスが見つからないことを通知するために機能します。 この記事のサンプルでは、特定のインスタンスにバインドするために GetInstance 静的メソッドを使用し、インスタンスが見つからない場合は null を返します。 コンストラクターは同じ目的で使用できますが、必要なインスタンスが見つからない場合は InstanceNotFoundException をスローする必要があります。 GetInstance 静的メソッドを置き換えるコンストラクターを次に示します。

        [ManagementBind]

        public WIN32ServiceHost(string Handle)

        {

            int processId;

            if (!Int32.TryParse(Handle, out processId))

            {

                新しい InstanceNotFoundException()をスローします。

            }

 

            試す

            {

                Process process = Process.GetProcessById(processId);

                this.innerProcess = process;

                this.handle = Handle;

                if (this.Services == null)

                {

                    新しい InstanceNotFoundException()をスローします。

                }

            }

            指定された ID を持つプロセスが見つからない場合は、GetProcessById によってスローされます

            catch (ArgumentException)

            {

                新しい InstanceNotFoundException()をスローします。

            }

        }

InstrumentationException

InstrumentationException は、処理された例外のラッパーです。 プロバイダーは、クライアント側で発生したエラーをクライアントに通知することを選択できます。 これを行うには、 InstrumentationException がスローされます。 開発者は、例外が WMI システムに戻っていることを念頭に置く必要があることに注意してください。 したがって、WMI.NET は、それを COM HRESULT に変換するために最善を尽くします。 正確なエラー コードをクライアントにスローするには、基本例外クラスで内部 HResult を直接設定できる Exception クラスを InstrumentationException の内部例外として渡す必要があります。

エラー報告

前のセクションに記載されていない例外は、ハンドルされない例外として終わり、"ハンドルされない例外 ('System.ExecutionEngineException') が wmiprvse.exe [<NNNN>]" で発生しました。NNNN はプロセス番号として報告されます。 エラーとスタックはイベントログで報告されます。 アセンブリと同じフォルダーにシンボルがあると、問題のあるスタックが完全に表示され、ファイル名と行番号が表示されます。

もう 1 つのエラー ケースは、アセンブリが GAC に展開されていないために読み込めなかった場合です。 この場合、WMI.NET はプロバイダーの読み込みエラー (WBEM_E_PROVIDER_LOAD_FAILURE) を返します。

プロバイダーの開発中に頻繁に問題になるのは、WMI スキーマとコードの不一致です。 これは、WMI にスキーマを展開せずに新しいアセンブリを展開するとき、または情報が実行時にのみ使用できるため、InstallUtil.exeを使用してデプロイするときに問題がキャッチされない場合に発生する可能性があります。 たとえば、列挙型の間に正しくない型が返された場合などです。 WMI.NET インフラストラクチャは、プロバイダーエラー (WMI エラー WBEM_E_PROVIDER_FAILURE) としてクライアントに報告し、ランタイムの問題を説明するメッセージを Windows イベント ログに生成します。

その他のヒント

すべての属性と WMI.NET コードは、 System.Management.Instrumentation 名前空間にあります。 WMI.NET のアセンブリをビルドするには、属性の定義とランタイム コードがそれぞれ含まれている ので 、プロジェクトは System.Core.dllとSystem.Management.Infrastructure.dll への参照を持っている必要があります。 後で、インストルメント化されたアセンブリを読み込み、インストルメント化されたクラスを WMI リポジトリと照合し、それに応じて呼び出す方法を認識します。

特定の種類のアプリケーションのすべてのクラスを同じアセンブリに保持します。 これにより、メンテナンスとデプロイがはるかに簡単になります。 

記事内のすべてのコードを展開し、管理者として実行する必要があります。 Windows Vista では、管理者として実行するには、管理者特権のセキュリティ コマンド プロンプトが必要です。 プロバイダーは、通常、この例のように特別にセキュリティで保護されたシステム データにアクセスしない限り、クライアントを管理者にする必要はありません。 

まとめ

.NET Framework 3.5 の新しい管理ライブラリにより、開発者は WMI プロバイダーを作成するための強力なツールが提供されます。 プログラミング モデルのシンプルさを考えると、WMI プロバイダーの記述は、ほとんどの WMI 機能を実装できるかなり簡単なタスクになります。 この記事のサンプルでは、単純な WMI プロバイダーのみを記述する方法を示していますが、このライブラリでは、分離されたプロバイダーの開発と、シングルトン、継承、抽象クラス、参照、および関連付けクラスの実装のサポートも提供され、WMI.NET Provider Extension v2 は WMI ネイティブ インターフェイスを使用した開発に代わる重要な代替手段となります。

リスト 1 – SCMInterop.cs

using System;

System.Runtime.InteropServices を使用する。

System.ServiceProcess を使用する。

 

 

namespace External.PInvoke

{

    [StructLayout(LayoutKind.Sequential)]

    内部構造体SERVICE_STATUS_PROCESS

    {

        public uint dwServiceType;

        public uint dwCurrentState;

        public uint dwControlsAccepted;

        public uint dwWin32ExitCode;

        public uint dwServiceSpecificExitCode;

        public uint dwCheckPoint;

        public uint dwWaitHint;

        public uint dwProcessId;

        public uint dwServiceFlags;

        public static readonly int SizeOf = Marshal.SizeOf(typeof(SERVICE_STATUS_PROCESS));

    }

 

    [フラグ]

    内部列挙型SCM_ACCESS : uint

    {

        SC_MANAGER_CONNECT = 0x00001、

    }

 

    [フラグ]

    内部列挙型SERVICE_ACCESS : uint

    {

        STANDARD_RIGHTS_REQUIRED = 0xF0000、

        SERVICE_QUERY_CONFIG = 0x00001、

        SERVICE_CHANGE_CONFIG = 0x00002、

        SERVICE_QUERY_STATUS = 0x00004、

        SERVICE_ENUMERATE_DEPENDENTS = 0x00008、

        SERVICE_START = 0x00010、

        SERVICE_STOP = 0x00020、

        SERVICE_PAUSE_CONTINUE = 0x00040、

        SERVICE_INTERROGATE = 0x00080、

        SERVICE_USER_DEFINED_CONTROL = 0x00100、

        SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |

                          SERVICE_QUERY_CONFIG |

                          SERVICE_CHANGE_CONFIG |

                          SERVICE_QUERY_STATUS |

                          SERVICE_ENUMERATE_DEPENDENTS |

                          SERVICE_START |

                          SERVICE_STOP |

                          SERVICE_PAUSE_CONTINUE |

                          SERVICE_INTERROGATE |

                          SERVICE_USER_DEFINED_CONTROL)

    }

 

    内部列挙型SC_STATUS_TYPE

    {

        SC_STATUS_PROCESS_INFO = 0

    }

 

    内部クラス ServiceHandle : SafeHandle

    {

        public ServiceHandle()

            : base(IntPtr.Zero, true)

        {

        }

 

        public void OpenService(SafeHandle scmHandle, string serviceName)

        {

            IntPtr serviceHandle = SCM。OpenService(scmHandle, serviceName, SERVICE_ACCESS.SERVICE_QUERY_STATUS);

 

            if (serviceHandle == IntPtr.Zero)

            {

                新しい System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error() をスローします。

                                                                "SCM。QueryServiceStatusEx");

            }

            SetHandle(serviceHandle);

        }

 

        protected override bool ReleaseHandle()

        {

            は SCM を返します。CloseServiceHandle(base.handle);

        }

 

        public override bool IsInvalid

        {

            get { return IsClosed || handle == IntPtr.Zero; }

        }

    }

 

    内部クラス SCM : SafeHandle

    {

        [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", CharSet = CharSet.Unicode,

                                                                     SetLastError = true)]

        public static extern IntPtr OpenSCManager(string machineName,

                                                  string databaseName,

                                    [MarshalAs(UnmanagedType.U4)]SCM_ACCESS dwAccess);

 

        [DllImport("advapi32.dll", EntryPoint = "OpenServiceW", CharSet = CharSet.Unicode,

                                                                     SetLastError = true)]

        public static extern IntPtr OpenService(SafeHandle hSCManager,

                                    [MarshalAs(UnmanagedType.LPWStr)] 文字列 lpServiceName,

                                    [MarshalAs(UnmanagedType.U4)]SERVICE_ACCESS dwDesiredAccess);

 

        [DllImport("advapi32.dll", EntryPoint = "QueryServiceStatusEx", CharSet = CharSet.Auto,

                                                                     SetLastError = true)]

        public static extern bool QueryServiceStatusEx(SafeHandle hService,

                                                      InfoLevel SC_STATUS_TYPE、

                                                      ref SERVICE_STATUS_PROCESS dwServiceStatus,

                                                      int cbBufSize,

                                                      ref int pcbBytesNeeded);

 

        [DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle", CharSet = CharSet.Auto,

                                                                     SetLastError = true)]

        public static extern bool CloseServiceHandle(IntPtr hService);

 

        public SCM()

            : base(IntPtr.Zero, true)

        {

            IntPtr handle = OpenSCManager(null, null, SCM_ACCESS.SC_MANAGER_CONNECT);

            ベース。SetHandle(handle);

        }

 

        protected override bool ReleaseHandle()

        {

            は SCM を返します。CloseServiceHandle(base.handle);

        }

 

        public override bool IsInvalid

        {

            get { return IsClosed || handle == IntPtr.Zero; }

        }

 

        public void QueryService(string serviceName, out SERVICE_STATUS_PROCESS statusProcess)

        {

            statusProcess = new SERVICE_STATUS_PROCESS();

            int cbBytesNeeded = 0;

 

            using (ServiceHandle serviceHandle = new ServiceHandle())

            {

                serviceHandle.OpenService(this, serviceName);

 

                bool scmRet = SCM。QueryServiceStatusEx(serviceHandle,

                                                        SC_STATUS_TYPE。SC_STATUS_PROCESS_INFO、

                                                        ref statusProcess,

                                                        SERVICE_STATUS_PROCESS。Sizeof

                                                        ref cbBytesNeeded);

                if (!scmRet)

                {

                    新しい System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error() をスローします。

                                                                    "SCM。QueryServiceStatusEx");

                }

            }

        }

 

        public int GetProcessId(string serviceName)

        {

            SERVICE_STATUS_PROCESS serviceStatus;

            これ。QueryService(serviceName, out serviceStatus);

            return (int)serviceStatus.dwProcessId;

        }

    }

}

リスト 2 – WIN32ServiceHost.cs

using System;

System.Collections を使用する。

using System.Collections.Generic;

System.Linq を使用する。

using System.Text;

 

System.ServiceProcess を使用する。

System.Diagnostics を使用する。

External.PInvoke を使用する。

System.Management.Instrumentation を使用する。

 

[assembly: WmiConfiguration(@"root\cimv2", HostingModel = ManagementHostingModel.NetworkService)]

 

 

namespace TestWMI.Hosted

{

    [System.ComponentModel.RunInstaller(true)]

    public クラス MyInstall : DefaultManagementInstaller

    {

    }

 

 

    [ManagementEntity(External = true)]

    抽象パブリック クラス Win32_Process

    {

        保護された文字列ハンドル。

 

        [ManagementKey]

        public string Handle

        {

            get {

                this.handle を返します。

            }

        }

    }

 

    [ManagementEntity(Name = "WIN32_ServiceHost")]

    public クラス WIN32ServiceHost: Win32_Process

    {

        innerProcess を処理する。

 

        <概要>

        ///

        </概要>

        <param name="innerProcess"></param>

        public WIN32ServiceHost(Process innerProcess)

        {

            this.innerProcess = innerProcess;

            this.handle = innerProcess.Id.ToString();

        }

 

        public int ID

        {

            get { return this.innerProcess.Id; }

        }

 

        [ManagementProbe]

        public string[] Services

        {

            get

            {

                if (innerProcess.Id == 0)

                    null を返します。

                ServiceController[] services = ServiceController.GetServices();

                List<ServiceController> servicesForProcess = new List<ServiceController>();

                using (SCM scm = new SCM())

                {

                    for (int svcIndex = 0;svcIndex < services。長さ;svcIndex++)

                    {

                        ServiceController crtService = services[svcIndex];

                        int processId = scm。GetProcessId(crtService.ServiceName);

                        if (processId == innerProcess.Id)

                        {

                            servicesForProcess.Add(services[svcIndex]);

                        }

                    }

                }

 

                if (servicesForProcess.Count == 0)

                    null を返します。

                string[] servicesNames = new string[servicesForProcess.Count];

 

                for (int serviceIdx = 0; serviceIdx < servicesForProcess.Count; serviceIdx++)

                {

                    servicesNames[serviceIdx] = servicesForProcess[serviceIdx].Servicename;

                }

                return servicesNames;

            }

        }

 

        [ManagementEnumerator]

        静的パブリック IEnumerable EnumerateServiceHosts()

        {

            Process[] process = Process.GetProcesses();

            foreach (プロセスでの crtProcess の処理)

            {

                WIN32ServiceHost crtServiceHost = 新しい WIN32ServiceHost(crtProcess);

                if (crtServiceHost.Services != null)

                {

                    yield return crtServiceHost;

                }

            }

        }

 

        [ManagementBind]

        public WIN32ServiceHost(string Handle)

        {

            int processId;

            if (!Int32.TryParse(Handle, out processId))

            {

                新しい InstanceNotFoundException()をスローします。

            }

 

            試す

            {

                Process process = Process.GetProcessById(processId);

                this.innerProcess = process;

                this.handle = Handle;

                if (this.Services == null)

                {

                    新しい InstanceNotFoundException()をスローします。

                }

            }

            指定された ID を持つプロセスが見つからない場合は、GetProcessById によってスローされます

            catch (ArgumentException)

            {

                新しい InstanceNotFoundException()をスローします。

            }

        }

 

        静的パブリック WIN32ServiceHost GetInstance(string Handle)

        {

            int processId;

            if (!Int32.TryParse(Handle, out processId))

            {

                null を返します。

            }

 

            試す

            {

                Process process = Process.GetProcessById(processId);

                WIN32ServiceHost crtServiceHost = 新しい WIN32ServiceHost(process);

                if (crtServiceHost.Services != null)

                {

                    crtServiceHost を返します。

                }

                else

                {

                    null を返します。

                }

            }

            指定された ID を持つプロセスが見つからない場合は、GetProcessById によってスローされます

            catch (ArgumentException)

            {

                null を返します。

            }

        }

 

        [ManagementTask]

        public bool StopServices(int millisecondsWait)

        {

            if (innerProcess.Id == 0)

                false を返します。

            ServiceController[] services = ServiceController.GetServices();

 

            bool oneFailed = false;

            using (SCM scm = new SCM())

            {

                for (int svcIndex = 0;svcIndex < services。長さ;svcIndex++)

                {

                    ServiceController crtService = services[svcIndex];

                    int processId = scm。GetProcessId(crtService.ServiceName);

                    if (processId == innerProcess.Id)

                    {

                        試す

                        {

                            crtService.Stop();

                            if (millisecondsWait != 0)

                            {

                                crtService.WaitForStatus( ServiceControllerStatus.Stopped,

                                                            new TimeSpan((long)millisecondsWait * 10000));

                            }

                        }

                        catch (System.ServiceProcess.TimeoutException)

                        {

                            oneFailed = true;

                        }

                        catch (System.ComponentModel.Win32Exception)

                        {

                            oneFailed = true;

                        }

                        catch (InvalidOperationException)

                        {

                            oneFailed = true;

                        }

                    }

                }

            }

            return !oneFailed;

        }

    }

}

 

 

 

© 2008 Microsoft Corporation. All rights reserved.