はじめに
インターネットは数多くのネットワークアプリケーションが接続されることで成り立っていますが、このインターネットの世界で、Webサイトを訪れる数百万人のユーザーの要求に応えるべくWebアプリケーションをスケーラブルにしたいと思うのは当然のことです。COM+は、スケーラブルで、拡張性があり、保守可能な分散アプリケーションを作成するためのWindows DNA 2000アーキテクチャの中核要素です。ただし、.NETでは、COM+コンポーネントのことを「サービスコンポーネント」と呼んでいます。.NET戦略のたび重なる変更により、サービスコンポーネントの配置モデルは、従来のCOM+コンポーネントの配置方法とは異なっています。この記事では、.NET Frameworkに用意されている各種ツールを使ってサービスコンポーネントを配置する方法を学習します。また、配置モデルがサービスコンポーネントのタイプによってどう変わるかについても説明します。最後に、Windowsインストーラプロジェクトを使ってサービスコンポーネントの配置を自動化する方法を学習します。
Windows 2000のコンポーネントサービスは、エンタープライズアプリケーションのための機能を豊富に備えており、Web対応の分散アプリケーション開発に必要なすべての基本機能を提供します。COM+サービスはもともとCOMコンポーネントで使うために設計されたものですが、COM+ランタイムが提供するサービスは、オブジェクトプーリング、データベース接続プーリング、リソース共有、ロールベースセキュリティ、分散トランザクション処理の監視など、有益なものが多いので、.NETコンポーネントでも利用できれば便利です。
.NET Frameworkには、COM+サービスをアプリケーションに提供するために必要なすべてのクラスを含んだSystem.EnterpriseServicesという名前空間があります。COM+の概要については、TechNetの「Introduction to COM and COM+」を参照してください。
サービスコンポーネントのさまざまな配置方法の説明に入る前に、System.EnterpriseServices名前空間にある最も重要なクラスの1つ、ServicedComponentクラスについて説明しましょう。
ServicedComponent
COM+サービスを利用する.NETオブジェクトを作成するには、System.EnterpriseServices.ServicedComponentクラスからオブジェクトを派生する必要があります。このクラスは、COM+ランタイムとシームレスにやり取りするためのメソッドをオブジェクトに提供し、これによってオブジェクトはCOM+サービスを利用できるようになります。サービスコンポーネントの配置を説明するに当たって、サーバーアプリケーションとライブラリアプリケーションの違いを理解しておく必要があります。基本的に、COM+ライブラリアプリケーションは、そのライブラリアプリケーションを作成するクライアントのプロセスで実行されるのに対し、COM+サーバーアプリケーションは、COM+実行可能ファイル(dllhost.exe)のプロセス空間で実行されます。
サービスコンポーネントのさまざまな配置方法
COMコンポーネントをコンパイルすると、そのコンポーネントをCOM+にインストールするために必要な全情報を含んだタイプライブラリが作成されます。一方、.NETコンポーネントをコンパイルすると、マニフェスト、MSIL、メタデータなどを含んだアセンブリが作成されます。アセンブリは、タイプライブラリとはまったく異なるものです。そのため、アセンブリからタイプライブラリを作成し、アセンブリをCOM+にシームレスにインストールできるようする必要があります。
Windows 2000のコンポーネントサービスに.NETコンポーネントを配置するには、次の3とおりの方法があります。
- Regsvcsコマンドラインユーティリティを使う
Regsvcsコマンドラインユーティリティは、アセンブリの名前を入力として受け取り、そのアセンブリを読み取ってタイプライブラリを作成した後、オブジェクトをCOM+に登録します。
- .NET Frameworkに用意されている自動登録メカニズムを使う
実行時に、共通言語ランライム(CLR)はアセンブリに基づいてタイプライブラリを自動的に作成し、COM+はそのタイプライブラリにある情報を使ってオブジェクトをインストールします。この方法は「レイジー登録」(lazy registration)と呼ばれ、ライブラリアプリケーションでしか利用できません。
- System.EnterpriseServices名前空間の一部であるRegistrationHelperクラスを使う
RegistrationHelperクラスのInstallAssemblyメソッドを使って、コードからCOM+に.NETオブジェクトを登録することができます。
アセンブリは、COM+に配置するだけでなく、次のいずれかの場所にも配置しなければなりません。
- クライアントアプリケーションのプライベートbinディレクトリ
クライアントが[Add Reference]ダイアログボックスを使ってサービスコンポーネントアセンブリを参照する場合は、ここがアセンブリの既定の配置場所になります。
- 共有の場所(GAC)
コンピュータ上のすべてのクライアントがアセンブリを利用できるようになります。
ライブラリアプリケーションにサービスコンポーネントを配置する場合は1つ目の配置方法(プライベート配置)を検討し、サーバーアプリケーションにサービスコンポーネントを配置する場合は2つ目の配置方法(GACに配置)を検討します。
サービスコンポーネントを使う前に、アセンブリに厳密に署名するためのキーファイルが必要です。キーファイルを生成するには、まず-kスイッチを使ってsn.exe(厳密名ユーティリティ)を実行します。この結果、公開/秘密キーのペアを含むファイルが作成されます。次に、公開/秘密キーのペアでアセンブリに署名し、「Assemblyinfo.cs」ファイルに<assembly: AssemblyKeyFile("....LibraryApp.snk")>という行を追加します。
では、COM+ライブラリアプリケーションにサービスコンポーネントを配置する方法の説明から始めましょう。
ライブラリアプリケーションにサービスコンポーネントを配置する
まず、Visual Studio .NETで[File]メニューの[New Project]を選択して新しいプロジェクトを作成します。次の画面に示すように、[New Project]ダイアログボックスで、プロジェクトの種類としてVisual C#クラスライブラリを選択し、プロジェクトの名前として「LibraryApp」と入力します。
プロジェクトを作成できたら、既定クラス(Class1)の名前を「SampleLibrary」に変更し、次のように修正します。COM+に用意されているサービスを利用するために、SampleLibraryクラスをServicedComponentクラスから派生させるようにします。
public class SampleLibrary : ServicedComponent
{
public SampleLibrary()
{
}
public string WriteMessage()
{
return "This message is from a library application";
}
}
ここでは、文字列を返すWriteMessageという非常に簡単なメソッドを用意しています。
次に、snユーティリティを使ってキーファイルを生成します。キーファイルを生成するには、Visual Studio .NETコマンドプロンプトで次のコマンドを入力します。
キーファイルを生成できたら、そのキーファイルをプロジェクトに追加します。さらに、「AssemblyInfo.cs」ファイルに次の行を追加します。
[assembly: AssemblyKeyFile("....LibraryApp.snk")]
[assembly: ApplicationName("LibraryApp")]
[assembly: ApplicationActivation(ActivationOption.Library)]
[assembly: Description("This is a library application")]
このコードについて詳しく説明しましょう。
- ApplicationName
COM+アプリケーションを作成する場合に使う名前を指定します。
- ApplicationActivation
サービスコンポーネントをアクティブ化する方法(インプロセスまたはアウトオブプロセス)を指定します。ここでは、値ActivationOption.Libraryを指定して、サービスプロセスをクライアントのプロセス空間でアクティブ化することを指定しています。
- Description
アセンブリの説明を入力します。
サービスコンポーネントをコンパイルしたら、COM+にアセンブリを配置する必要があります。配置するには、コマンドラインから次のようにサービスインストールユーティリティ「regsvcs.exe」を実行します。
このコマンドラインユーティリティを実行すると、基本的に次の処理が行われます。
- まず、CLRアセンブリがCOMコンポーネントとして登録されます(アセンブリ登録ユーティリティ「regasm.exe」を実行した場合と同じです)。
- COMタイプライブラリが生成され(タイプライブラリエクスポータ「tlbexp.exe」を実行した場合と同じです)、そのタイプライブラリを使って、アセンブリ内の設定済みクラスがCOM+カタログに配置されます。
- 次に、既定で、アセンブリの
ApplicationName属性とApplicationActivation属性に指定されているターゲットアプリケーションが作成されます。
- 最後に、.NETのReflection APIを使って、アセンブリ内の設定済みクラスのメタデータが取り出され、その情報に従って、各クラスに適切な宣言属性が設定されるようにCOM+カタログが更新されます。設定済みクラスで宣言属性が具体的に指定されていない場合は、既定値が使用されます。
regsvcsユーティリティを実行したら、コンポーネントサービスエクスプローラを起動して、コンポーネントがCOM+に正しくインストールされていることを確認します。
この画面からお分かりのように、「AssemblyInfo.cs」ファイルでの指定どおりにLibraryAppという新しいライブラリアプリケーションが作成され、LibraryAppライブラリアプリケーションの内側にLibraryAppコンポーネントがインストールされています。このサービスコンポーネントをテストするため、簡単なVisual C# Windowsアプリケーションを作成してメソッドを呼び出してみましょう。
サービスコンポーネントのクライアントアプリケーション
クライアントアプリケーションの名前を「LibraryAppClient」とし、既定フォーム「Form1.cs」にコマンドボタンを追加します。次に、[Add Reference]オプションを使って、LibraryAppアセンブリの参照を追加します。
さらに、ファイルの先頭に次のステートメントを追加して、必要な名前空間の参照をインポートします。
コマンドボタンのクリックイベントに、次のコード行を書きます。
private void btnInvoke_Click(object sender, System.EventArgs e)
{
SampleLibrary obj = new SampleLibrary();
MessageBox.Show(obj.WriteMessage());
}
アプリケーションを実行してコマンドボタンをクリックすると、表示されているサービスコンポーネントから返されたメッセージが表示されます。
COM+でライブラリアプリケーションにサービスコンポーネントを配置する方法の説明はこれで終わりにし、次は、サーバーアプリケーションの一部としてサービスコンポーネントを配置する方法を解説します。
サーバーアプリケーションにサービスコンポーネントを配置する
既に説明したとおり、サーバーアプリケーションに配置したサービスコンポーネントは、常にCOM+実行可能ファイル「dllhost.exe」のアドレス空間でアクティブ化されます。さらに、サービスコンポーネントは(COM+サーバーアプリケーションに配置する場合は)GACにも配置する必要があります。これは、COM+が、クライアントが実際に実行されているディレクトリに関係なく、アセンブリをロードできるようにするためです。この例では、次のように、「ServerApp」という新しいVisual C#クラスライブラリアプリケーションを作成します。
プロジェクトを作成したら、既定クラスの名前を「SampleServer」に変更し、次のコード行を追加します。
public class SampleServer : ServicedComponent
{
public SampleServer()
{
}
public string WriteMessage()
{
return "This message is from a server application";
}
}
前述の例と同じように、キーファイル「ServerApp.snk」を作成してプロジェクトに追加します。次に、「AssemblyInfo.cs」ファイルに次の行を追加します。
[assembly: ApplicationName("ServerApp")]
[assembly: Description("This is a server application")]
[assembly: ApplicationActivation(ActivationOption.Server)]
[assembly: AssemblyKeyFile("....ServerApp.snk")]
ここでは、ApplicationActivation属性をActivationOption.Serverとして、このアセンブリをCOM+の実行可能プロセスでアクティブ化することを指定しています。これで、必要なオプションをすべて指定できたので、コンポーネントのコンパイルに進み、アセンブリを生成します。アセンブリを生成できたら、「gacutil.exe」ユーティリティを使ってGACにアセンブリをインストールします。アセンブリをインストールするには、アセンブリの名前とコマンドスイッチiを引数としてgacutilユーティリティに渡します。
GACにアセンブリをインストールできたら、regsvcsユーティリティを実行してCOM+にコンポーネントを登録します。
これで、アセンブリを配置できました。クライアントアプリケーションを作成し、配置したアセンブリにあるクラスのメソッドをテストしてみましょう。
サービスコンポーネントのクライアントアプリケーション
まず、「ServerAppClient」という新しいVisual C# Windowsアプリケーションを作成し、「Form1.cs」ファイルに次のコード行を追加します。
using ServerApp;
private void cmdServerApp_Click(object sender, System.EventArgs e)
{
SampleServer obj = new SampleServer();
MessageBox.Show(obj.WriteMessage());
}
アプリケーションを実行してコマンドボタンをクリックすると、サービスコンポーネントから返されたメッセージが表示されます。
System.EnterpriseServices.RegistrationHelperクラスを使って配置を自動化する
これらの手動の配置方法は、サイズが小さいアプリケーションにしか利用できません。例えば、ターゲットサーバーにインストールするために複雑なインストールプログラムを必要とする、エンタープライズアプリケーション内でサービスコンポーネントを使用する場合は、そのサービスコンポーネントの配置を、インストールプログラムの一部として行う必要があります。また、アセンブリをGACにインストールする処理をインストールプログラムの一部として行うことが必要になる場合もあります。このようなインストールを実行するスクリプトやバッチファイルを作成することもできますが、もっと簡単な方法があります。Visual Studio .NET のセットアップ/配置プロジェクトを使用すると、コンポーネント登録用のコードを呼び出す機能や、インストール時またはアンインストール時にその他の必要な処理を実行する機能を備えたセットアッププロジェクトを簡単に作成できます。このためには、インストール時またはアンインストール時に実行するカスタム動作を作成する必要があります。カスタム動作を作成するために必要な作業は、System.Configuration.Install.Installerを継承するクラスをアセンブリ内で作成し、配置プロジェクトにカスタムのインストール動作を追加することだけです。今回の例では、先ほど作成したLibraryAppサービスコンポーネントを使うことにします。
LibraryAppサービスコンポーネントアセンブリにインストーラクラスを追加するには、次のようにします。
- LibraryAppプロジェクトを右クリックします。
- [Add / New Item...]を選択し、[Add New Item]ダイアログボックスを表示します。
- [Categories]ペインで[Code]を選択します。
- [Templates]ペインで[Installer Class]を選択し、クラスの名前としてCustomActionInstallerを指定します。
クラスを作成できたら、そのクラスにInstallメソッドとUninstallメソッドを追加します。
public override void Install(
System.Collections.IDictionary stateSaver)
{
try
{
string appID = null;
string typeLib = null;
// Get the location of the current assembly
string assembly = GetType().Assembly.Location;
// Install the application
RegistrationHelper regHelper = new RegistrationHelper ();
regHelper.InstallAssembly (assembly, ref appID, ref typeLib,
InstallationFlags.FindOrCreateTargetApplication);
// Save the state - you will need this for the uninstall
stateSaver.Add ("AppID", appID);
stateSaver.Add ("Assembly", assembly);
}
catch(Exception ex)
{
#if DEBUG
Debug.WriteLine (ex);
#endif
StreamWriter writer = File.AppendText ("InstallError.log");
writer.WriteLine ("Uninstall Error: {0}", ex.Message);
// If the installer catches the exception it will display
// an error message. Show a friendly error message
throw new ApplicationException
(
"Error installing the middle tier", ex);
}
}
public override void Uninstall(
System.Collections.IDictionary savedState)
{
try
{
// Get the state created when the app was installed
string appID = (string)savedState["AppID"];
string assembly = (string)savedState["Assembly"];
// Uninstall the application
RegistrationHelper regHelper = new RegistrationHelper ();
regHelper.UninstallAssembly (assembly, appID);
}
catch ( Exception ex)
{
// Don’t allow unhandled exceptions during uninstall
#if DEBUG
Debug.WriteLine (ex);
#endif
StreamWriter sw = File.AppendText ("InstallError.log");
sw.WriteLine ("Uninstall Error: {0}", ex.Message);
}
}
このコード行について詳しく説明しましょう。まず、現在使用しているアセンブリの場所の参照を取得します。
string assembly = GetType().Assembly.Location;
ここで、RegistrationHelperクラスのインスタンスを作成します。
RegistrationHelper regHelper = new RegistrationHelper ();
その後、InstallAssemblyメソッドを呼び出してアセンブリをインストールし、appID変数とtypeLib変数をbyref引数として渡します。アセンブリをインストールしたら、新たに作成したアプリケーションのIDの参照と、ローカル変数に代入されているタイプライブラリを取得します。
regHelper.InstallAssembly (assembly, ref appID, ref typeLib,
InstallationFlags.FindOrCreateTargetApplication);
次に、用意されているコレクションオブジェクトにAppIDとアセンブリの場所を保存し、後で取得できるようにしておきます。永続化した情報はアプリケーションのアンインストールフェーズで使います。
stateSaver.Add ("AppID", appID);
stateSaver.Add ("Assembly", assembly);
}
このコード行の実行時に例外が発生した場合は、その例外をキャッチし、例外情報をログファイル「InstallError.log」に書き込みます。
catch(Exception ex)
{
#if DEBUG
Debug.WriteLine (ex);
#endif
StreamWriter writer = File.AppendText ("InstallError.log");
writer.WriteLine ("Uninstall Error: {0}", ex.Message);
// If the installer catches the exception it will display
// an error message. Show a friendly error message
throw new ApplicationException
(
"Error installing the middle tier", ex);
}
}
Uninstallメソッドでは、まずコレクションオブジェクトからAppIDとアセンブリ情報を取得します。
// Get the state created when the app was installed
string appID = (string)savedState["AppID"];
string assembly = (string)savedState["Assembly"];
次に、RegistrationHelperクラスのUninstallAssemblyメソッドを呼び出してアセンブリをアンインストールします。
RegistrationHelper regHelper = new RegistrationHelper ();
regHelper.UninstallAssembly (assembly, appID);
}
ここで再び、例外ブロックで、例外情報を外部のログファイル「InstallError.log」に書き込みます。
catch ( Exception ex)
{
// Don’t allow unhandled exceptions during uninstall
#if DEBUG
Debug.WriteLine (ex);
#endif
StreamWriter sw = File.AppendText ("InstallError.log");
sw.WriteLine ("Uninstall Error: {0}", ex.Message);
}
}
インストーラクラスを作成できたら、CustomInstallerクラスのInstallメソッドとUninstallメソッドが自動的に呼び出されるように、配置プロジェクトにカスタム動作を追加します。その前に、既存のLibraryAppソリューションに新しい配置プロジェクトを追加する必要があります。このため、次のように「LibraryAppSetup」という新しいプロジェクトを作成します。
次に、配置プロジェクトにLibraryAppプロジェクトの出力を追加する必要があります。このためには、LibraryAppSetupプロジェクトを右クリックし、[Add]をポイントして[Project Output]を選択します。[Add Project Output Group]ダイアログボックスの[project]ドロップダウンボックスから[LibraryApp]を選択し、出力リストから[Primary Output]を選択します。次に、インストーラがCustomActionInstallerクラスのInstallメソッドとUninstallメソッドを呼び出すように、配置プロジェクトにカスタム動作を追加する必要があります。
カスタム動作を追加する
カスタム動作はインストールのさまざまなフェーズで呼び出すことができます。インストールが終了した後またはロールバックされた後は、コミットフェーズとロールバックフェーズが発生します。コミットフェーズとロールバックフェーズの際に重要な作業を実行してはなりません。Installメソッドはインストールフェーズで呼び出し、Uninstallメソッドはアンインストールフェーズで呼び出します。
カスタム動作を追加するには、次のようにします。
- LibraryAppSetupプロジェクトを右クリックし、[View]をポイントして[File System]を選択します。
- [Install]フェーズを右クリックし、[Add Custom Action...]を選択します。
- ターゲットコンピュータのファイルシステムをナビゲートし、カスタム動作クラスが入っているLibraryAppアセンブリを探します。
GACにアセンブリを配置する
インストールの一部として、GACにサービスコンポーネントを配置することもできます。これは、COM+サーバーアプリケーションにサービスコンポーネントを配置する場合に非常に便利です。GACにサービスコンポーネントをインストールするには、次の操作を実行します。
- LibraryAppSetupプロジェクトを右クリックし、[View]をポイントして[File System]を選択します。
- 既定では、フォルダのリストにグローバルアセンブリキャッシュフォルダは表示されません。リストにグローバルアセンブリキャッシュフォルダを追加するには、[File System on Target Machin]を右クリックし、[Add Special Folder]をポイントして[Global Assembly Folder]を選択します。
- グローバルアセンブリフォルダを右クリックし、[Add]をポイントして[Assembly]を選択し、GACフォルダにアセンブリを追加します。
これで、インストーラを作成して「LibraryAppSetup.msi」ファイルを実行すると、アセンブリがコンポーネントサービスに登録されているだけでなく、GACにも配置されていることが分かります。
XCOPY配置とサービスコンポーネント
.NET Frameworkの目標の1つは、XCOPY配置をサポートすることによってシステムのインストールを簡略化することです。XCOPYとは、ファイルやディレクトリを別の場所にコピーするためのMS-DOSコマンドラインユーティリティのことです。XCOPY配置の目的は、コードを実行することなくアプリケーションをリモートサーバーにインストールできるようにすることです。XCOPY配置をサポートするために、CLR/COM+の統合アーキテクチャでは、インストール時に「regsvcs.exe」の実行を無視し、サービスコンポーネントが初めて使用されるときに自動登録を行うような仕組みが用意されています。これを「レイジー登録」と言います。レイジー登録は、ライブラリアプリケーションにサービスコンポーネントを配置する場合に役立ちます。ただし、XCOPY配置にも注意しなければならない欠点があります。
- COM+システムアプリケーションで定義されている管理者ロールに含まれるユーザーしかCOM+カタログを更新できません。既定の管理者ロールにはローカルの管理者しか含まれていません。そのため、サービスコンポーネントを正しく登録するには、最初にサービスコンポーネントを使うコードに管理者特権を付与する必要があります。そうしないと、登録は失敗します。
- GACにサービスコンポーネントを配置する場合は適切なディレクトリにファイルをコピーすれば簡単に配置できますが、サーバーアプリケーションにもアセンブリを配置する場合には別の作業が必要になります。特に、メタデータでは自動的に設定されない、COM+サーバーアプリケーションのセキュリティプリンシパルの設定が必要になります。COM+ライブラリアプリケーションに設定済みクラスを配置する場合は、アセンブリがクライアントのプライベートディレクトリに配置されている限り問題ありません。
GACにサービスコンポーネントをインストールすることが必要な場合
次の条件に当てはまる場合は、GACにサービスコンポーネントをインストールすることが必要です。
- アプリケーションがSOAP(クライアントとサーバーの両方)または.NET Remotingを使っている場合。
- マネージドコードからサービスコンポーネントにアクセスし、かつアセンブリの格納ディレクトリが実行可能ファイルとは異なる場合。つまり、マネージドコードからアセンブリを探す場合、アセンブリは、アプリケーションディレクトリ内にないときはGAC内になければなりません。
- 同じコンピュータ上のすべてのクライアントアプリケーションが同じアセンブリを共有する場合。
- サービスコンポーネントがCOM+サーバーアプリケーションに配置されている場合。COM+ライブラリアプリケーションに配置されているサービスコンポーネントは、ディレクトリが別々でない限り、GACに置く必要はありません。現在ログオンしているユーザーは管理者グループに含まれていなければなりません。
まとめ
この記事では、.NET Frameworkに用意されているさまざまなクラスやツールを使ってサービスコンポーネントを配置する方法を説明しました。また、Windowsインストーラテクノロジを利用すると、サービスコンポーネントの配置を自動化するWindowsインストーラプロジェクトを簡単に作成できることも説明しました。
最後に、この記事を読んでくださった方に感謝します。また、この記事があなたにとって有益であることを願っています。