IIS マネージャーで IIS 7 用のモジュール プロパティを拡張する方法
公開日: 2007 年 11 月 24 日 (作業者: saad (英語))
更新日: 2008 年 3 月 7 日 (作業者: saad (英語))
概要
IIS 7 管理ツールは、開発者が独自の機能をプラグインしたり、カスタム設定を管理したりするために使用できる拡張可能なプラットフォームです。追加機能はすべてこのツールに統合され、IIS 7 や ASP.NET の機能と共に提供されます。
このドキュメントでは、Microsoft.Web.Management.Client.Win32 名前空間の ModulePropertiesPage 基本クラスから派生したページを作成することによって、カスタム セクションの構成設定にアクセスする方法および構成設定を操作する方法について説明します。このページでは、プロパティ グリッドを使用して設定を表示および変更します。このドキュメントの背景情報については、「単純な UI モジュールの作成」、「IIS 7 管理ツール拡張機能への構成機能の追加 (英語)」、「Microsoft.Web.Administration API を使用した IIS スキーマの拡張およびカスタム セクションへのアクセス」を参照してください。
はじめに
このドキュメントでは、IIS 7 管理ツール (InetMgr) を拡張して、このツールにプラグインできる機能を開発する方法について説明します。InetMgr は、ModuleProvider と ModuleService をサーバー側の主要コンポーネントとし、Module と ModuleServiceProxy をクライアント側の主要コンポーネントとする分散クライアント/サーバー アーキテクチャとして設計されています。
Microsoft.Web.Management 名前空間には、ユーザーが設定の表示や操作を行うダイアログ ページ、リスト ページ、プロパティ ページなどの開発に使用できる基本クラスがいくつか用意されています。このドキュメントでは、プロパティ グリッドを使用して管理設定の表示や操作を行う、ModulePropertiesPage から派生したページの作成について主に説明します。IIS マネージャー ツールの既存のプロパティ ページには、たとえば ASP 設定や CGI 設定などを操作するページがあります。
このドキュメントの内容は次のとおりです。
- IIS 7 スキーマを拡張して、カスタム構成セクションを作成する方法の概要。
- サーバーの設定の操作に使用する ModuleProvider と ModuleService のセットアップ。この例では、構成ファイル内のプロパティを操作します。
- Module のセットアップとプロパティ ページの登録。これによって、クライアント UI が表示されます。
- ModulePropertiesPage 基本クラスから派生したプロパティ ページの実際の開発。
IIS 7 スキーマの拡張
スキーマを拡張するには、単純な .xml ファイルをコンピューター上の %SystemRoot%\system32\inetsrv\config\schema ディレクトリに追加します。このディレクトリには IIS_schema.xml というファイルがあります。このファイルは、独自のスキーマをセットアップするための見本として利用できます。CUSTOM_schema.xml という名前のファイルを作成し、下に示すような内容に設定します。この例では、カスタム セクション (myCustomSection) にブール型、文字列型、整数型の 3 つのプロパティがあります。これらのプロパティの既定値が、次のスキーマで指定されています。
<configSchema>
<sectionSchema name="system.webServer/myCustomSection">
<attribute name="booleanProperty" type="bool" defaultValue="false" />
<attribute name="stringProperty" type="string" />
<attribute name="integerProperty" type="int" defaultValue="100" />
</sectionSchema>
</configSchema>
このセクションを applicationHost.config に登録します。次に示すような 1 行を applicationHost.config に追加します。
注: 追加するセクションのパスは system.webServer/myCustomSection なので、IIS 7 の既存のセクション グループの 1 つである system.webServer グループにセクションを登録します。
<configSections>
<sectionGroup name="system.webServer">
<section name="myCustomSection" />
...
</sectionGroup>
<sectionGroup name="system.applicationHost">
...
...
...
</sectionGroup>
</configSections>
ModuleProvider と ModuleService の作成
ModuleProvider クラスから派生した CustomModuleProvider は、管理ツールに拡張機能モジュールを登録するためのエントリ ポイントを提供します。CustomModuleProvider クラスでは、機能の提供範囲 (サーバー、サイト、またはアプリケーション)、この機能と関連付けられる ModuleService、この機能のクライアント側の Module を指定します。
public class CustomModuleProvider : ModuleProvider {
public override Type ServiceType {
get {
return typeof(CustomModuleService);
}
}
public override ModuleDefinition GetModuleDefinition(IManagementContext context) {
ModuleDefinition moduleDefinition =
new ModuleDefinition(Name, typeof(CustomModule).AssemblyQualifiedName);
return moduleDefinition;
}
public override bool SupportsScope(ManagementScope scope) {
return (scope == ManagementScope.Application) ||
(scope == ManagementScope.Site) ||
(scope == ManagementScope.Server);
}
}
CustomModuleProvider は、%SystemRoot%\system32\inetsrv\config にある administration.config に登録します。ModuleProvider の完全修飾型名を指定してください。administration.config は、InetMgr が使用する特殊ファイルです。InetMgr は、起動時にこのファイルを使用して、表示する機能を判別します。
<moduleProviders>
...
...
<add name="Custom" type="CustomModule.CustomModuleProvider, CustomModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=fb21e927ce4cfa1e" />
</moduleProviders>
<location path=".">
<modules>
...
...
<add name="Custom" />
</modules>
</location>
注: このエントリを <moduleProviders> のリストだけに含めると、UI 機能はサーバー レベルでのみ表示されます。サイトやアプリケーションでも機能を表示するように指定しているので、このエントリを <modules> のリストにも追加して、UI 機能がサイト レベルやアプリケーション レベルでも有効であることを InetMgr に知らせます。これにより、ユーザーはコードを修正せずに、柔軟に機能の表示場所を変更できます。
CustomModuleService は ModuleService 基本クラスから派生したクラスで、実際のサーバー管理を行います。この例では、構成セクションやそのプロパティを読み取るメソッドや、ユーザーが設定した新しいプロパティでセクションの内容を更新するメソッドが含まれています。
public class CustomModuleService : ModuleService {
[ModuleServiceMethod]
public PropertyBag GetCustomSettings() {
PropertyBag bag = new PropertyBag();
ConfigurationSection section =
ManagementUnit.Configuration.GetSection("system.webServer/myCustomSection");
bag[CustomGlobals.booleanProperty] =
section.GetAttributeValue("booleanProperty");
bag[CustomGlobals.stringProperty] =
section.GetAttributeValue("stringProperty");
bag[CustomGlobals.integerProperty] =
section.GetAttributeValue("integerProperty");
return bag;
}
[ModuleServiceMethod]
public void UpdateCustomSettings(PropertyBag updatedSettings) {
if (updatedSettings == null) {
throw new ArgumentNullException("updatedSettings");
}
ConfigurationSection section =
ManagementUnit.Configuration.GetSection("system.webServer/myCustomSection");
foreach (int key in updatedSettings.ModifiedKeys) {
switch (key) {
case CustomGlobals.booleanProperty:
section.SetAttributeValue("booleanProperty",
updatedSettings[CustomGlobals.booleanProperty]);
break;
case CustomGlobals.stringProperty:
section.SetAttributeValue("stringProperty",
updatedSettings[CustomGlobals.stringProperty]);
break;
case CustomGlobals.integerProperty:
section.SetAttributeValue("integerProperty",
updatedSettings[CustomGlobals.integerProperty]);
break;
}
}
ManagementUnit.Update();
}
}
GetCustomSettings はプロパティ バッグ (パラメーターを格納する汎用ディクショナリ。シリアル化が可能) を使用してカスタム構成セクションの値を返します。UpdateCustomSettings はこれらの設定の値を更新します。ManagementUnit.Update() は構成ファイルを保存します。
updatedSettings プロパティ バッグの ModifiedKeys プロパティは、ユーザーが UI を使用して値を変更したキーを格納します (これにより、構成ファイル内の既存の設定と値が異なるプロパティのみ更新されます)。プロパティ バッグで変更を追跡し、値が変更されたプロパティのキーを ModifiedKeys に格納する方法については、後ほど説明します。クライアント プロキシから呼び出されたメソッドは、ModuleServiceMethod 属性を使用してマークされます。
CustomGlobals クラスは、プロパティ バッグ内のプロパティのインデックスである整数キーを格納します。
internal sealed class CustomGlobals {
public const int booleanProperty = 0;
public const int stringProperty = 1;
public const int integerProperty = 2;
}
Module と ModuleServiceProxy の作成
CustomModule は Module クラスから派生したクラスで、クライアント側のコードのエントリ ポイントを提供します。CustomModule の Initialize メソッドは、CustomPage を登録し、設定を操作するためのプロパティ グリッドを表示します。この例では、CustomPage を Server カテゴリ (ツールのホーム ページに表示される機能グループ) に登録しています。
class CustomModule : Module {
protected override void Initialize(IServiceProvider serviceProvider,
ModuleInfo moduleInfo) {
base.Initialize(serviceProvider, moduleInfo);
// Register the control panel category and page with the control panel
IControlPanel controlPanel =
(IControlPanel)serviceProvider.GetService(typeof(IControlPanel));
Debug.Assert(controlPanel != null, "Couldn't get IControlPanel");
ModulePageInfo customPage =
new ModulePageInfo(this, typeof(CustomPage),
"Custom Section Settings", "Edits the custom settings");
controlPanel.RegisterPage(ControlPanelCategoryInfo.Server, customPage);
}
}
ModuleServiceProxy から派生した CustomModuleProxy はメソッド スタブと呼ばれます。メソッド スタブは、サーバー側のメソッドをクライアント側で呼び出すために使用するコンポーネントです。プロキシ メソッドは、[ModuleServiceMethod] 属性でマークされた ModuleService のメソッドに対応するメソッドです。
class CustomModuleProxy : ModuleServiceProxy {
public CustomModuleProxy() {
}
public PropertyBag GetCustomSettings() {
return (PropertyBag)Invoke("GetCustomSettings");
}
public void UpdateCustomSettings(PropertyBag updatedSettings) {
Invoke("UpdateCustomSettings", updatedSettings);
}
}
プロパティ グリッド ページの作成
他のコンポーネントのセットアップが完了したので、次に ModulePropertiesPage を派生したクラスを作成し、プロパティ グリッドをセットアップします。このグリッドを使って構成設定の表示や変更ができるようにします。
ページ上に表示したいすべてのプロパティを公開するクラスを定義します。
class CustomPropertiesInfo : PropertyGridObject {
private PropertyBag _bag;
public CustomPropertiesInfo(ModulePropertiesPage page, PropertyBag bag)
: base(page) {
Initialize(bag);
}
[DefaultValue(false)]
public bool BooleanProperty {
get {
object o = _bag[CustomGlobals.booleanProperty];
if (o == null) {
return false;
}
return (bool)o;
}
set {
_bag[CustomGlobals.booleanProperty] = value;
}
}
public string StringProperty {
get {
object o = _bag[CustomGlobals.stringProperty];
if (o == null) {
return String.Empty;
}
return (string)o;
}
set {
_bag[CustomGlobals.stringProperty] = value;
}
}
[DefaultValue(100)]
public int IntegerProperty {
get {
object o = _bag[CustomGlobals.integerProperty];
if (o == null) {
return 100;
}
return (int)o;
}
set {
_bag[CustomGlobals.integerProperty] = value;
}
}
internal void Initialize(PropertyBag bag) {
_bag = bag;
}
}
このクラスは PropertyGridObject 基本クラスから派生します。この基本クラスは、ICustomTypeDescriptor インターフェイスを実装するので、このオブジェクトの動的な型情報を提供できます (たとえば、実行時に特定のプロパティに特別な属性を指定できます)。ただし、この例では、この基本クラスに用意されている機能は使用しません。
ModulePropertiesPage から派生したクラスをセットアップします。
clas CustomPage : ModulePropertiesPage {
}
サーバーから設定を取得するために使用する、ModulePropertiesPage 基本クラスの GetProperties メソッドをオーバーライドします。このメソッドは、以下の 2 つの場合に呼び出されます。
- 最初にページにアクセスしたとき
- ページ表示時に、最新の情報に更新するボタンをクリックしたとき
protected override PropertyBag GetProperties() {
returnServiceProxy.GetCustomSettings();
}
このメソッドはバックグラウンド スレッドで非同期的に実行されるので、このメソッド内で UI 要素にアクセスしないことをお勧めします。このメソッドは、サーバーの最新の設定が格納されたプロパティ バッグを返します。
ProcessProperties メソッドをオーバーライドします。このメソッドは、PropertyBag 引数で指定されている現在の設定内容を設定および表示するために使用します。
このメソッドは、プロパティ グリッドを最新の設定に更新する場合に呼び出します。プロパティ グリッドの更新は、次のような場合に行われます。
- サーバーから設定を初めて取得するとき (ページ アクセス時)
- サーバーが更新されたとき (ユーザーが [変更の適用] をクリックしたとき)
- UI に加えられた変更が取り消され、元の設定が復元されたとき (ユーザーが [変更の取り消し] をクリックしたとき)
protected override void ProcessProperties(PropertyBag properties) {
_bag = properties;
_clone = _bag.Clone();
CustomPropertiesInfo info = (CustomPropertiesInfo)TargetObject;
if (info == null) {
info = new CustomPropertiesInfo(this, _clone);
TargetObject = info;
}
else {
info.Initialize(_clone);
}
ClearDirty();
}
UpdateProperties メソッドを実装します。このメソッドは、サービスを呼び出してサーバーの設定を更新し、更新時に発生したすべてのエラーを表示します。このメソッドは最新の設定が格納されたプロパティ バッグを返します。これによって、サーバーの最新の値セットを取得したことを基本クラスに通知します。
このプロパティ バッグのコピーを作成し、このコピーに、ユーザーが加えた変更をすべて格納します。ユーザーが [変更の取り消し] をクリックした場合に備えて、構成ファイルの元の設定を保持しておきます。プロパティ バッグは、基本クラスで保持します。ユーザーの操作は、必ずプロパティ バッグのコピーに対して実行されるようにしてください。また、プロパティ バッグのコピーに対して加えられた変更内容を追跡する必要があります。プロパティ バッグのコピーは、値が変更されたプロパティのキーを ModifiedProperties に格納します (サーバー側では、これを使用して構成に変更を加えています)。
ClearDirty メソッドは、ページが変更されていないこと、つまり、ページは「ダーティ」ではないため、[変更の適用] や [変更の取り消し] のリンクを無効とすべきであることを示します。
protected override PropertyBag UpdateProperties(out bool updateSuccessful) {
updateSuccessful = false;
try {
ServiceProxy.UpdateCustomSettings(_clone);
_bag = _clone;
updateSuccessful = true;
}
catch (Exception ex) {
OnException(ex);
}
return _bag;
}
特殊な方法でエラーを処理する OnException メソッドをオーバーライドします。この例では、単純にメッセージ ボックスにエラーを表示します。
protected override void OnException(Exception ex) {
while (ex.InnerException != null) {
ex = ex.InnerException;
}
ShowError(ex, false);
}
このクラス全体のフレームワークを示すスニペットを以下に示します。
class CustomPage : ModulePropertiesPage {
private CustomModuleProxy _serviceProxy;
private PropertyBag _bag;
private PropertyBag _clone;
protected override bool CanApplyChanges {
get {
return HasChanges;
}
}
protected override bool CanRefresh {
get {
return true;
}
}
private CustomModuleProxy ServiceProxy {
get {
if (_serviceProxy == null) {
_serviceProxy =
(CustomModuleProxy)CreateProxy(typeof(CustomModuleProxy));
}
return _serviceProxy;
}
}
protected override PropertyBag GetProperties() {
}
protected override void ProcessProperties(PropertyBag properties) {
}
protected override void OnException(Exception ex) {
}
protected override PropertyBag UpdateProperties(out bool updateSuccessful) {
}
}