- PR -

.NET Remotingで公開するクラスをGACに登録するとFileNotFoundException

投稿者投稿内容
ryuuji
ベテラン
会議室デビュー日: 2003/07/10
投稿数: 53
お住まい・勤務地: 東京都
投稿日時: 2005-03-08 17:42
Win2k + VS.NET2003 C#+ .NET Framework1.1で開発しております。

.NET Remotingでクライアント(EXE)/サーバ(EXE)+ビジネスロジック(DLL)間のプロセス間通信を以下の構成で作成しました。

+------+ +---------------+ +----+
|クライアント|<->|サーバビジネスロジック|<->|サーバ|
| | |IMyGAC, M yGAC| | |
+------+ +---------------+ +----+


■サーバEXE
Httpチャンネルを指定しRemotingConfiguration.RegisterWellKnownServiceTypeでMyGACクラス型を登録。
(サーバビジネスロジックを参照)

■サーバビジネスロジックDLL
IMyGACインタフェースを公開。その派生クラスMyGACの実装クラスも公開(は呼び出されたらログを出力する単純な実装)。

■クライアントEXE
Httpチャンネルを指定しActivator.GetObject()でIMyGACをアクティベート
(サーバビジネスロジックを参照)

上記のサーバビジネスロジック(DLL)をサイドバイサイドで実行すると問題が無いのですが、サーバビジネスロジック(DLL)に厳密名を付けGACに登録すると、クライアントEXEでアクティベートしたオブジェクトのメソッドコールを契機にサーバEXE内でFileNotFoundExceptionが発生します。

---------------
System.IO.FileNotFoundException: ファイルまたはアセンブリ名 MyGAC、またはその依存関係の 1 つが見つかりませんでした。
---------------

.NET RemotingではRemotingクライアントからの初回のメソッドコールで、Remotingサーバは公開した型情報をリフレクションで読込む(スタックトレースにもそのように出力されていました)のですが、そのリフレクションによるLoadが失敗していました。

GACモジュールはリフレクションできないのか?と思い、サンプルコードを書いたところ、問題なくAssembly.LoadFromできました。

.NET Remotingサーバが公開するクラスにGACモジュールを指定してはならない、という仕様があるのでしょうか?参考となる文献など紹介していただけないでしょうか。
(MSDNでRegisterWellKnownServiceType、Activator.GetObjectを調べましたが分かりませんでした。参照すべきところが違うのでしょうか?)

[ メッセージ編集済み 編集者: ryuuji 編集日時 2005-03-08 18:02 ]
ryuuji
ベテラン
会議室デビュー日: 2003/07/10
投稿数: 53
お住まい・勤務地: 東京都
投稿日時: 2005-03-08 18:06
構成図がおかしくなっていたので編集しようとしたら出来ませんした。
書き直します。汚くてすいません。これからはプレビューします。

コード:
+------+   +---------------+   +----+
|クライアント|<->|サーバビジネスロジック|<->|サーバ|
|      |   |IMyGAC,  M yGAC|   |    |
+------+   +---------------+   +----+

nodera
大ベテラン
会議室デビュー日: 2003/09/08
投稿数: 200
投稿日時: 2005-03-08 18:10
こんにちは。

引用:

ryuujiさんの書き込み (2005-03-08 17:42) より:

<途中省略>

.NET RemotingではRemotingクライアントからの初回のメソッドコールで、Remotingサーバは公開した型情報をリフレクションで読込む(スタックトレースにもそのように出力されていました)のですが、そのリフレクションによるLoadが失敗していました。

GACモジュールはリフレクションできないのか?と思い、サンプルコードを書いたところ、問題なくAssembly.LoadFromできました。



サンプルではなく、実際の実装コード側もLoadFromなんでしょうか?LoadFromはパス指定だから、GACに登録されているアセンブリは読み込めないんじゃ。。。
(ていうか、LoadFromでやったらサンプルも失敗するのでは)

そこは、どうなってるいるのでしょうか?
ryuuji
ベテラン
会議室デビュー日: 2003/07/10
投稿数: 53
お住まい・勤務地: 東京都
投稿日時: 2005-03-08 18:36
早速の書込み、有難うございます。

引用:

サンプルではなく、実際の実装コード側もLoadFromなんでしょうか?LoadFromはパス指定だから、GACに登録されているアセンブリは読み込めないんじゃ。。。
(ていうか、LoadFromでやったらサンプルも失敗するのでは)

そこは、どうなってるいるのでしょうか?



すいません、実際のコードが例外を起こした際のスタックトレースを見ると、下記のように出ています。クライアントコードがアクティベートしたサーバオブジェクトのメソッドを呼び出した契機でサーバ側でAssembly.Load()が呼び出されています。

------
at System.Reflection.Assembly.nLoad(AssemblyName ...省略)

at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, ...省略)

at System.Reflection.Assembly.InternalLoad(String assemblyString, ...省略)

at System.Reflection.Assembly.Load(String assemblyString)
------

サンプルコードではGACをリフレクションでロードできるか?という観点で調べてしまったので、直接パスを指定するAssembly.LoadFrom()を使ってしまいました。ちなみにGACモジュールの絶対パスを指定すると、LoadFrom、Assembly.GetTypeで型の取得は成功しました。

Assembly.Load()がサイドバイサイドのモジュールしかロードできない(できなさそうです。MSDN調べます)のであれば、それが原因だとして、何故そのような仕様にしているのかが分かりません。。。
nodera
大ベテラン
会議室デビュー日: 2003/09/08
投稿数: 200
投稿日時: 2005-03-08 18:55
ああ、リフレクションでLoadしているのは、自分自身で書いたコードじゃなくて、リモーティングしたときに自動的に呼び出されているってことですね。勘違いしました(^^;

Assembly.Load()メソッドは、GACに登録されているアセンブリもロードできますが、アセンブリ名に完全限定名を指定する必要があります。
例:"System.data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, Custom=null"

この場合、このような形式の文字列が渡されているのかどうかってことになりますが、GACに登録されているアセンブリの型をRegisterWellKnownServiceTypeで登録した場合、どうなんだろ。
やったこと無いので、自分もわかりませぬ。。。あう。

RegisterWellKnownServiceTypeではなく、.Configを使うタイプ(RemotingConfiguration.Configure)で、type属性のところに完全限定名を書いたらいかないかなぁ。。。
ryuuji
ベテラン
会議室デビュー日: 2003/07/10
投稿数: 53
お住まい・勤務地: 東京都
投稿日時: 2005-03-08 19:56
ご意見、有難うございます。

[QUOTE]
RegisterWellKnownServiceTypeではなく、.Configを使うタイプ(RemotingConfiguration.Configure)で、type属性のところに完全限定名を書いたらいかないかなぁ。。。
[/QOUTE]

下記のようにServer.exe.configを作成してみましが、同じ例外が出てしまいました。

<wellknown mode="Singleton" type="MyGac, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxx" objectUri="service1.soap"/>

そこで勘違いに気づきました。例外が発生していたのは、クライアント側でした。サーバ側は呼び出されてもいませんでした。サーバ.exeと同じディレクトリにGACモジュール(サイドバイサイド)を置くと例外が出なくなっていたので勘違いしてしまいました。すみません。

Remotingクライアントがアクティベートされたサーバオブジェクトのメソッドを呼び出す際、サーバ.exeが置かれているディレクトリから公開している型情報をリフレクションでLoadしようとして例外を起こしているようです。

それではと、思いRemotingクライアント側でapp.configから読込むように変更し、type属性に完全限定名を指定したところ、

-----
System.Runtime.Remoting.RemotingException: 例外 System.Runtime.Remoting.RemotingException: アセンブリ名 'Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxx' に存在するバージョン情報は client wellknown エントリには使用できません。
-----

という例外が発生してしまいました。恐らく何らかのセキュリティ上の理由で禁止されているように思えるのですが、どこに記載されているか。。。MSDNでRemotingのあたりをもう一度さらってみます。
nodera
大ベテラン
会議室デビュー日: 2003/09/08
投稿数: 200
投稿日時: 2005-03-08 20:16
type属性にはクラス名とアセンブリ名を指定する必要があります。
クラス名もアセンブリ名も同じMyGacでしょうか?とすると、こうかな。
type="MyGac, MyGac, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxx"

あと、技術的なところでいままで答えていましたが、そもそもこのMyGacはGAC登録する必要のあるアセンブリなんでしょうか?GAC登録しなければいけないようなモジュールは、そうあるものではないので、そのあたりも見直してみたほうが良いかもしれません。

またMyGacアセンブリはクライアント側プログラムが型を認識するために、クライアント側にもインストールされていなければいけない、ってところも認識されているでしょうか?
ryuuji
ベテラン
会議室デビュー日: 2003/07/10
投稿数: 53
お住まい・勤務地: 東京都
投稿日時: 2005-03-08 20:49
お忙しい中、毎々有難うございます。

引用:

そもそもこのMyGacはGAC登録する必要のあるアセンブリなんでしょうか?

またMyGacアセンブリはクライアント側プログラムが型を認識するために、クライアント側にもインストールされていなければいけない、ってところも認識されているでしょうか?



まず、.NET Remotingでのノード越えは考えておらず、プロセス間通信を実現する1つの方法として利用したいと考えています(なのでHttpではなくTcpチャンネルで十分)。

実際のプログラム設計は以下の通りでGAC登録は必須なのでは、と考えています。

コード:
[App1]<--+
[App2]<--+-->[RemotingHelper.dll]<-->[Windowsサービス]
[App3]<--+
      参照設定      (GAC)        .NET Remoting



1端末上にWindowsサービスがあり、色んなアプリケーション(App1〜3)から呼び出したい、けど全員でプロセス間通信のクライアントコードを書くのは面倒です。そこでRemotingHelperというGACモジュールを置いて、このアセンブリがRemotingクライアントとして必要な処理を隠蔽するようにします。GACモジュールはプライベートアセンブリを参照できませんので、サーバが公開するクラスはGACモジュールに格納されていなければならない、というのが「MyGac.dllがGACに登録する必要がある」と考えている理由です。

Remotingサーバと同じディレクトリにMyGacを置けば一応の解決にはなるのですが、GACにも登録して、サイドバイサイドとしても置いて、というのがイマイチすっきりしません。
(「なぜ駄目なのか」という技術的な理由が知りたい、というのもあります)

クライアント・アクティベーションのあたりを読んでも特に記載されていません。。。
ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1041/cpguide/html/cpconclientactivation.htm

スキルアップ/キャリアアップ(JOB@IT)