UI 拡張機能の作成について

公開日: 2008 年 5 月 21 日 (作業者: SinghGurpreet (英語))

更新日: 2008 年 5 月 21 日 (作業者: SinghGurpreet (英語))

はじめに

IIS 7 管理ツールは、モジュールのプラグインによって拡張可能なプラットフォームです。開発者は独自の UI 機能を作成することができ、作成した UI 機能は inetmgr によって組み込みの機能と同様に扱われます。

このチュートリアルでは、UI モジュールを一から作成する方法について説明します。また、IIS チームが採用しているモジュール作成のベスト プラクティスも紹介します。

このタスクでは、2 つのアセンブリを作成します。このプロセスは、2 つの手順に要約できます。

  • クライアント アセンブリの作成
  • サーバー アセンブリの作成

新しい inetmgr は、ユーザー インターフェイス ロジックとサーバー設定を操作するコードとを明確に分離した、Web サービスに似たアーキテクチャを使用するよう設計されています。これは、主に ModuleService と ModuleServiceProxy という 2 つのクラスによって実現されます。ModuleService 派生クラスは、ModuleProvider の ServiceType プロパティによって ModuleProvider に関連付けられます。

空のソリューションの作成

   [スタート] ボタン、[プログラム] の順にクリックし、[Microsoft Visual Studio 2008] または [Microsoft Visual C# 2008 Express Edition] をクリックして実行します。[ファイル] メニューの [新規作成] - [プロジェクト] をクリックし、[その他のプロジェクトの種類][Visual Studio ソリューション] をクリックします。右側で [空のソリューション] をクリックし、ソリューションの名前として「ExtensibilityDemo」と入力します。  

Ff454099.pict1(ja-jp,TechNet.10).jpg

クライアント アセンブリの作成

クライアント アセンブリを作成するタスクでは、次の作業を行います。

  • クライアント アセンブリのプロジェクトのセットアップ
  • ModuleServiceProxy の作成
  • ModuleListPage の作成と ModuleServiceProxy の使用
  • モジュール クラスの作成

クライアント アセンブリのプロジェクトのセットアップ

ソリューション エクスプローラーで、ソリューションを右クリックし、[追加]、[新しいプロジェクト] の順にクリックして次のウィンドウを開きます。[Visual C#] をクリックし、右側のウィンドウで [クラス ライブラリ] をクリックし、プロジェクトの名前として「Client」と入力します。

Ff454099.image003_441(ja-jp,TechNet.10).png

既定で追加されている Class1.cs ファイルは使用しないので、ソリューション エクスプローラーで、コンテキスト メニューの [削除] オプションを使用して削除します。

[プロジェクト] メニューの [参照の追加] をクリックし、[参照] タブで \Windows\system32\inetsrv ディレクトリを検索して、Microsoft.Web.Management.dll への参照を追加します。このディレクトリには、inetmgr でモジュールを作成するために必要なすべての拡張機能クラスが含まれています。System.Windows.Forms の一部のクラスを参照するので、System.Windows.Forms への参照も含めます。

ベスト プラクティス: Client プロジェクトからは Microsoft.Web.Administration.dll を参照しないでください。

ライブラリを InetMgr 内で使用するための要件の 1 つとして、ライブラリを GAC に登録する必要があります。このため、DLL には必ず厳密な名前を付ける必要があります。Visual Studio では、新しい名前を簡単に作成できます。アセンブリに署名するには、プロジェクトのプロパティを開いて [署名] タブをクリックし、[アセンブリの署名] チェック ボックスをオンにします。[厳密な名前のキー ファイルを選択してください] ドロップダウンボックスから "<新規作成...>" を選択し、表示されたダイアログ ボックスで、キーの名前として「ExtensibilityDemoKey」と入力し、[キー ファイルをパスワードで保護する] チェック ボックスをオフにします。

Ff454099.image006_441(ja-jp,TechNet.10).gif

Ff454099.image007_441(ja-jp,TechNet.10).png

[OK] をクリックしてアセンブリに署名します。また、アセンブリを GAC に登録する必要があるので、コンパイルのたびに自動的にアセンブリが GAC に追加されるように、ビルド後のイベントをいくつか追加します。これによって、新しい機能を追加する際に、簡単にデバッグや変更を行うことができます。これには、[ビルド イベント] タブをクリックし、[ビルド後に実行するコマンド ライン] に次のコマンド ラインを追加します。

gacutil.exe /if '$(TargetPath)' 

Ff454099.image009_441(ja-jp,TechNet.10).png

最後に、プロジェクトのプロパティを閉じて、[ファイル] メニューの [すべてを保存] をクリックします。これで、[ビルド] メニューの [ソリューションのビルド] をクリックしてプロジェクトをコンパイルできます。実行すると、自動的に DLL がビルドされ、GAC に追加されます。

注: vsvars32.bat を設定するコマンドがエラー コード 9009 で失敗する場合は、[ビルド後に実行するコマンド ライン] に追加したコマンドを、gacutil.exe の完全パスを追加したコマンドに置き換えることができます。たとえば、次のように入力します。

'C:\Program Files\Microsoft Visual Studio 9\SDK\v2.0\Bin\gacutil.exe' /if '$(TargetPath)'

また、環境によっては gacutil.exe のパスは以下のようになります。

'C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\gacutil.exe' /if '$(TargetPath)'

エラーが回避されない場合は、コンパイル正常終了後にコマンドプロンプトを管理者として起動し、以下のコマンドを実行することで代替できます。

'C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\gacutil.exe' /if '生成されたアセンブリ (dll) までのフルパス'

ModuleServiceProxy の作成

 ModuleServiceProxy は、ModuleService クラスのメソッドを呼び出すプロセスを簡素化するクラスです。このクラスは、Web 管理サービス (wmsvc) を使用してリモート コンピューターを管理したり、ローカル サーバーを管理したりする際のロジックを抽象化するので重要です。

このサンプルでは、'appSettings' の内容を読み書きします。[プロジェクト] メニューの [新しい項目の追加] をクリックします。[コード] をクリックし、ファイル名として「DemoModuleServiceProxy.cs」と入力します。

Ff454099.image011_441(ja-jp,TechNet.10).png

コードを次のような内容に変更します。

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Web.Management.Client;
using Microsoft.Web.Management.Server;

namespace Client {
    internal class DemoModuleServiceProxy : ModuleServiceProxy {
        public ArrayList GetAppSettings() {
            return (ArrayList )Invoke("GetAppSettings");
        }
    }
} 

このように、プロキシは、ModuleServiceProxy を派生したクラスを作成し、サーバー側で ModuleService のメソッドを呼び出すだけで作成できます。

ModuleListPage の作成

[プロジェクト] メニューの [新しい項目の追加] をクリックします。[コード] をクリックし、ファイル名として「DemoModuleListPage.cs」と入力します。

コードを次のような内容に変更します。

using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using Microsoft.Web.Management.Client.Win32;
using Microsoft.Web.Management.Server;

namespace Client {
    internal class DemoModuleListPage : ModuleListPage {
        private ColumnHeader _keyColumnHeader;
        private ColumnHeader _valueColumnHeader;
        private DemoModuleServiceProxy _serviceProxy;

        private DemoModuleServiceProxy ServiceProxy {
            get {
                if (_serviceProxy == null) {
                    _serviceProxy = (DemoModuleServiceProxy)CreateProxy(typeof(DemoModuleServiceProxy));
                }
                return _serviceProxy;
            }
        }

        protected override void InitializeListPage() {
            _keyColumnHeader = new ColumnHeader();
            _keyColumnHeader.Text = "Key";
            _keyColumnHeader.Width = 200;

            _valueColumnHeader = new ColumnHeader();
            _valueColumnHeader.Text = "Value";
            _valueColumnHeader.Width = 200;

            ListView.Columns.Add(_keyColumnHeader);
            ListView.Columns.Add(_valueColumnHeader);
        }

        protected override void OnActivated(bool initialActivation) {
            base.OnActivated(initialActivation);
            if (initialActivation) {
                GetAppSettings();
            }
        }

        private void GetAppSettings() {
            StartAsyncTask("Getting Settings", new DoWorkEventHandler(GetSettings),
                                              new RunWorkerCompletedEventHandler(GetSettingsCompleted));
        }

        private void GetSettings(object sender, DoWorkEventArgs e) {
            e.Result = ServiceProxy.GetAppSettings();
        }

        private void GetSettingsCompleted(object sender, RunWorkerCompletedEventArgs e) {
            ListView.BeginUpdate();
            try {
                ArrayList settingsList = (ArrayList)e.Result;
                foreach (PropertyBag bag in settingsList) {
                    ListViewItem item = new ListViewItem();
                    item.Text = (string)bag[0];
                    item.SubItems.Add((string)bag[1]);

                    ListView.Items.Add(item);
                }
            }
            finally {
                ListView.EndUpdate();
            }
        }
    }
}

このデモでは、ModuleListPage クラスを拡張しています。InitializeListPage メソッドをオーバーライドして列を初期化し、リストビューに追加します。また、OnActivated メソッドもオーバーライドします。このメソッドは、ページにアクセスしたときに呼び出されます。

Module クラスの作成

これは、存在するすべてのオブジェクトへのクライアント側のエントリ ポイントです。主なメソッドとして、Initialize、GetService、Dispose の 3 つがあります。このクラスでは、ページの登録、ページへのカテゴリの割り当てなど、すべての処理が行われます。

[プロジェクト] メニューの [新しい項目の追加] をクリックします。[コード] をクリックし、ファイル名として「DemoModule.cs」と入力します。

コードを次のような内容に変更します。

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Web.Management.Client;

namespace Client {
    internal class DemoModule : Module {
        protected override void Initialize(IServiceProvider serviceProvider, Microsoft.Web.Management.Server.ModuleInfo moduleInfo) {
            base.Initialize(serviceProvider, moduleInfo);

            IControlPanel controlPanel = (IControlPanel)GetService(typeof(IControlPanel));
            controlPanel.RegisterPage(new ModulePageInfo(this, typeof(DemoModuleListPage), "DemoPage"));
        }
    }
}

DemoModule は Initialize メソッドをオーバーライドします。最初に、DemoModule は基本クラスの Initialize を呼び出します。次に、GetService によって ControlPanel サービスにアクセスします。最後に、ControlPanel の RegisterPage によって DemoModule 自体を登録します。

サーバー アセンブリの作成

サーバー アセンブリを作成するには、2 つのメイン クラスを作成します。

  • モジュール プロバイダー
  • モジュール サービス

また、サーバーは、\Windows\System32\inetsrv ディレクトリにある Microsoft.Web.Management.dll と Microsoft.Web.Management.Administration.dll という 2 つのメイン アセンブリも参照する必要があります。最初のアセンブリは、inetmgr の UI の作成に使用するすべてのフレームワーク クラスで構成されます。2 番目のアセンブリは構成の読み取り/書き込み用クラスで構成されます。

ソリューション エクスプローラーでソリューションを右クリックし、[追加]、[新しいプロジェクト] の順にクリックして次のウィンドウを開きます。[Visual C#] をクリックし、右側のウィンドウで [クラス ライブラリ] をクリックし、プロジェクトの名前として「Server」と入力します。

Ff454099.image013_441(ja-jp,TechNet.10).png

既定で追加されている Class1.cs ファイルは使用しないので、ソリューション エクスプローラーで、コンテキスト メニューの [削除] オプションを使用して削除します。

[プロジェクト] メニューの [参照の追加] をクリックし、[参照] タブで \Windows\system32\inetsrv ディレクトリを検索して、Microsoft.Web.Management.dllMicrosoft.Web.Management.Administration.dll への参照を追加します。このディレクトリには、inetmgr でモジュールを作成するために必要なすべての拡張機能クラスが含まれています。

ライブラリを InetMgr 内で使用するための要件の 1 つとして、ライブラリを GAC に登録する必要があります。このため、DLL には必ず厳密な名前を付ける必要があります。Visual Studio では、新しい名前を簡単に作成できます。アセンブリに署名するには、プロジェクトのプロパティを開いて [署名] タブをクリックし、[アセンブリの署名] チェック ボックスをオンにします。ドロップダウン リストで [<参照...>] をクリックします。表示されたダイアログ ボックスで、Client フォルダーに移動し、ExtensibilityDemoKey をクリックして [開く] をクリックします。プロジェクトのプロパティを保存します。

Ff454099.image005_441(ja-jp,TechNet.10).png 

[OK] をクリックしてアセンブリに署名します。また、アセンブリを GAC に登録する必要があるので、コンパイルのたびに自動的にアセンブリが GAC に追加されるように、ビルド後のイベントをいくつか追加します。これによって、新しい機能を追加する際に、簡単にデバッグや変更を行うことができます。これには、[ビルド イベント] タブをクリックし、[ビルド後に実行するコマンド ライン] に次のコマンド ラインを追加します。

gacutil.exe /if '$(TargetPath)'

 

Ff454099.image009_441(ja-jp,TechNet.10).png

最後に、プロジェクトのプロパティを閉じて、[ファイル] メニューの [すべてを保存] をクリックします。これで、[ビルド] メニューの [ソリューションのビルド] をクリックしてプロジェクトをコンパイルできます。実行すると、自動的に DLL がビルドされ、GAC に追加されます。

注: vsvars32.bat を設定するコマンドがエラー コード 9009 で失敗する場合は、[ビルド後に実行するコマンド ライン] に追加したコマンドを、gacutil.exe の完全パスを追加したコマンドに置き換えることができます。たとえば、次のように入力します。

'C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe' /if '$(TargetPath)'

また、環境によっては gacutil.exe のパスは以下のようになります。

'C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\gacutil.exe' /if '$(TargetPath)'

エラーが回避されない場合は、コンパイル正常終了後にコマンドプロンプトを管理者として起動し、以下のコマンドを実行することで代替できます。

'C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\gacutil.exe' /if '生成されたアセンブリ (dll) までのフルパス'

モジュール サービスの作成

モジュール サービスの作成vice を作成します。これは、Microsoft.Web.Administration.dll を利用した、構成ファイルを読み書きするためのクラスです。

[プロジェクト] メニューの [新しい項目の追加] をクリックします。[新しい項目の追加] ダイアログ ボックスで [コード ファイル] テンプレートをクリックし、ファイルの名前として「DemoModuleService.cs」と入力します。

コードを次のような内容に変更します。

using System.Collections;
using Microsoft.Web.Administration;
using Microsoft.Web.Management.Server;

namespace Server {
    internal class DemoModuleService : ModuleService {

        [ModuleServiceMethod(PassThrough = true)]
        public ArrayList GetAppSettings() {

            ArrayList settingsList = new ArrayList();

            ConfigurationSection appSettingsSection =
                ManagementUnit.Configuration.GetSection(@"appSettings");
            ConfigurationElementCollection settingsCollection = appSettingsSection.GetCollection();

            foreach (ConfigurationElement element in settingsCollection) {
                PropertyBag bag = new PropertyBag();
                bag[0] = element.GetAttributeValue("key");
                bag[1] = element.GetAttributeValue("value");
                settingsList.Add(bag);
            }

            return settingsList;
        }
    }
}

AssemblyRef クラスの作成

ここでは、AssemblyRef クラスを作成します。このクラスは、モジュール クラスをインスタンス化するために、モジュール プロバイダーによって参照されます。

[プロジェクト] メニューの [新しい項目の追加] をクリックします。[新しい項目の追加] ダイアログ ボックスで [コード ファイル] テンプレートをクリックし、ファイルの名前として「DemoModuleProvider.cs」と入力します。

using System.Reflection;

internal static class AssemblyRef {

    private static string client;

    internal static string Client {
        get {
            if (client == null) {
                AssemblyName assemblyName = typeof(AssemblyRef).Assembly.GetName();
                string assemblyFullName = assemblyName.FullName;
                client = assemblyFullName.Replace(assemblyName.Name, "Client");
            }

            return client;
        }
    }
}

モジュール プロバイダーの作成

ここでは、モジュール プロバイダーを作成します。モジュール プロバイダーは、Inetmgr におけるモジュール登録の主要なエントリ ポイントです。これらのモジュール プロバイダーのリストは Administration.config に記述されます。

[プロジェクト] メニューの [新しい項目の追加] をクリックします。[新しい項目の追加] ダイアログ ボックスで [コード ファイル] テンプレートをクリックし、ファイルの名前として「DemoModuleProvider.cs」と入力します。

Ff454099.image015_441(ja-jp,TechNet.10).png

コードを次のような内容に変更します。

using System;
using Microsoft.Web.Management.Server;

namespace Server {
    internal class DemoModuleProvider : ModuleProvider {
        public override ModuleDefinition GetModuleDefinition(IManagementContext context) {
            return new ModuleDefinition(Name, "Client.DemoModule," + AssemblyRef.Client);
        }

        public override Type ServiceType {
            get {
                return typeof(DemoModuleService);
            }
        }

        public override bool SupportsScope(ManagementScope scope) {
            return true;
        }
    }
} 

Administration.Config でのエントリの追加

ここでは、administration.config の moduleProviders リストにエントリを追加します。アセンブリの完全な名前は、\Windows\Assembly フォルダーで確認できます。Client アセンブリを探します。見つかったら、右クリックして [プロパティ] をクリックします。

Ff454099.image017_441(ja-jp,TechNet.10).png

アセンブリの完全な名前は、このウィンドウで確認できます。たとえば、この例では、アセンブリの完全な名前は、Server, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e788138af6131538 です。administration.config の moduleProviders に追加するエントリは、次のようになります。

<moduleProviders>
<..
<add name=
"DemoModule" 
type=
"Server.DemoModuleProvider,Server, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e788138af6131538"
/>
../> 
<moduleProviders>

注: このエントリを追加することによって、このモジュールはサーバー接続についてのみ登録されます。

ソリューションをビルドして inetmgr を実行すると、次のような画面が表示されます。

Ff454099.image019_441(ja-jp,TechNet.10).png

Ff454099.image021_441(ja-jp,TechNet.10).png 

まとめ

inetmgr の拡張機能モデルを使用して、独自のカスタム ページを作成する方法について説明しました。今後の記事では、taskItem の追加や構成ファイルへの書き込みの方法についても説明する予定です。