連載
|
![]() |
|
|
|
●ダウンロードした.DLLファイルのロードと、そこに含まれる機能の実行
ダウンロードした.DLLファイルに含まれる機能(例えばクラスなど)を利用することは、まったく難しくない。というのも、その機能を通常と同じようにそのまま呼び出せばよいだけだからだ。このようなことが可能なのは、「.NET FrameworkのCLRでは、あるクラスを利用しようとしているメソッドが呼び出されるときに、初めてJITコンパイラがそのクラスのアセンブリをロードしようとする」という仕組みのおかげである。
ただしアセンブリをロードするときに、そのファイルが見つからない場合、FileNotFoundException例外(System.IO名前空間)が発生してしまう。この問題を避けるために、JITコンパイラが動くのを、ファイル(アセンブリ)がダウンロードされた後まで遅延させなければならない。これを行うには、アセンブリの機能の呼び出し(つまりクラスのインスタンス生成や静的メソッドの使用など)を、ほかのメソッドの中にカプセル化し(つまり1つのメソッドの中にまとめて)、さらにそのメソッドの呼び出しがインライン展開されないようにしておいた方がよい。
インライン展開されないようにするには、MethodImplOptions列挙体のNoInlining値をパラメータに指定したMethodImpl属性(=MethodImplAttributeクラス)を、そのメソッドに付加すればよい(この列挙体と属性はSystem.Runtime.CompilerServices名前空間に所属する)。
要するに、次のようなコードを書けばよいわけである。なおこのコードは、[オンデマンド配置]ボタンがクリックされたときのイベント・ハンドラである。
|
|
| ダウンロードされたアセンブリ(.DLLファイル)に含まれるクラスを呼び出すコード例 | |
| ダウンロードされた後でアセンブリが正しくCLR上にロードされるように、そのアセンブリに含まれるクラスを使用するコードはメソッド内にまとめて記述し、さらにそのメソッドには「MethodImplOptions.NoInlining」をパラメータに指定したMethodImpl属性を付加する。 |
上記のコードでは、オンデマンド配置されるアセンブリの機能をUseSampleClassLibraryメソッドの中にカプセル化している。さらに、そのUseSampleClassLibraryメソッドにはインライン展開されないように属性を付加している。
●ロード失敗時のアセンブリの再ロード
通常は上記のコードで問題ないはずだが、何らかの理由でダウンロードしたアセンブリのロードに失敗した場合、その失敗の情報をCLRがキャッシュしてしまうらしく、アプリケーションを再起動しないと、ダウンロードしたアセンブリ(.DLLファイル)のクラスにアクセスできずにエラーが発生する。
これを回避するために、CLRがアセンブリのロードに失敗したときに発行されるAssemblyResolveイベントをハンドルして、そこで独自にアセンブリをロードする(Assembly.LoadFileメソッド)ような仕組みを用意しておいた方が安全だ。
AssemblyResolveイベントはアプリケーション・ドメインで発生するイベントであり、現在のアプリケーション・ドメインは、AppDomainクラス(System名前空間)のCurrentDomainプロパティより取得できる。
本稿で提供する「ClickOnce」プロジェクトのクラス・ライブラリ内でClickOnceAssemblyクラス(DigitaAdvantage.ClickOnce名前空間)では、このAssemblyResolveイベントの処理も実装している。ここでは、このクラスを活用して先ほど示したコードを拡張したものを示しておこう。
|
|
| AssemblyResolveイベントを活用して動的にアセンブリをロードするコード |
このコードでは、先ほどのUseSampleClassLibraryメソッドを呼び出す前に、LoadSampleClassLibraryメソッドを呼び出している。
このLoadSampleClassLibraryメソッドは、オンデマンド配置されたアセンブリ(のクラス)が正しくロードできるかどうかを検証するためのものだ。正しくロードできた場合にはTrue、そうでない場合はFalseを戻り値として返す。この戻り値がTrueのときにだけ、UseSampleClassLibraryメソッドを呼び出すというロジックになっている。
肝心のLoadSampleClassLibraryメソッドの内部では、ClickOnceAssemblyクラスをインスタンス化して、そのLoadDeployedFileメソッドを呼び出している。このLoadDeployedFileメソッドは、アセンブリを安全にロードしてくれ(厳密には先ほど説明したAssemblyResolveイベントを利用してアセンブリをロードする)、正常にロードできた場合はTrueを、何らかのエラーが発生した場合はFalseを戻り値として返す。
LoadDeployedFileメソッドの第1パラメータには.DLLファイルが格納されているパスを指定し(通常は.EXEファイルと同じ場所にダウンロードされるため、「Application.StartupPath」を指定すればよい)、第2パラメータに.DLLファイルの名前を指定する(「.dll」という拡張子部分は除く)*1。第3パラメータには、CheckLibraryClassDelegateデリゲート(DigitaAdvantage.ClickOnce名前空間)を指定する。このデリゲートの構文はパラメータなしで、戻り値はvoid型(=なし)である。
| *1 ここで.DLLファイルのパスと名前を取得しているのは、AssemblyResolveイベントのハンドラでAssembly.LoadFileメソッドを呼び出す際にそれらの値を使用するからである。 |
デリゲート経由で呼ばれるメソッドは、アセンブリ(.DLLファイル)をロードするために使われる。従ってそのデリゲート・メソッド(本稿ではasm_CheckLibraryClassDelegateメソッド)内では、アセンブリに含まれるクラスをインスタンス化するなどすればよい(本稿の例ではSampleClassLibraryCheckClassという空のクラスをインスタンス化している)*2。
asm_CheckLibraryClassDelegateメソッドが呼び出される際、CLRのJITコンパイラが働き、アセンブリをロードしようとする。万が一、そのロードに失敗すると、アプリケーション・ドメインでAssemblyResolveイベントが発生する。そのイベントをClickOnceAssemblyクラスがハンドルして、LoadDeployedFileメソッドのパラメータに指定されたパスの.DLLファイルをアセンブリとしてロードする。このような仕組みによって、アセンブリ内のクラスが利用可能となるわけである。
| *2 (アセンブリをロードするためだけに)空のクラスのインスタンス化するというのが無駄に感じられる場合は、asm_CheckLibraryClassDelegateメソッドの中に、ダウンロードしたアセンブリに含まれる機能を使用するコードを記述してもよい。例えば、先ほど示したUseSampleClassLibraryメソッドの内容をasm_CheckLibraryClassDelegateメソッドの中に記述してもよい。 |
それでは次のテーマに移ろう。
| INDEX | ||
| ClickOnceの真実 | ||
| 第4回 ClickOnceテクノロジを最大限に生かす開発 | ||
| 1.オンデマンド配置のためのダウンロード・グループの作成 | ||
| 2.オンデマンド配置するコードと進ちょくダイアログの表示 | ||
| 3.ダウンロードしたアセンブリのロードとそこに含まれる機能の実行 | ||
| 4.任意のタイミングで実行するアップデート(手動による更新処理) | ||
| 「ClickOnceの真実」 |
TechTargetジャパン
- Kinectが切り開く“夢の近未来” (2012/2/2)
日本を含めた世界中でKinect for Windowsセンサー商用版とSDK正式版がリリース。未来のコンピューティングはどう変化するのか? - 3つの視点でネイティブと.NETの適材適所を考察 (2012/1/31)
アプリ開発は「ネイティブ」と「.NET」、どちらが最良? その問いには「適材適所」と答えるしかない。では、“適所”は一体どこかを考察する - SQL Azure Data Sync入門 (2012/1/30)
SQL Azure/SQL Serverデータベース間のデータ同期を簡単に実現するサービスとは? その仕組みや使用手順を解説 - Windows Phoneアプリ市場の現状を分析する (2012/1/27)
Windows Phone のアプリ・ストアに日々登録されている多種多様なアプリ。カテゴリ別のアプリ数は? 市場の現状を明らかにする
|
|
キャリアアップ
は.NET開発者中心に生まれ変わりました
スポンサーからのお知らせ
.NET開発者中心コーナー
- - PR -
イベントカレンダー
- - PR -


