@IT会議室は、ITエンジニアに特化した質問・回答コミュニティ「QA@IT」に生まれ変わりました。ぜひご利用ください。
- PR -

クラスをフックする

投稿者投稿内容
kekyo
会議室デビュー日: 2002/12/12
投稿数: 13
投稿日時: 2003-05-21 11:04
こんにちは。

.NET FrameworkではSystem.Runtime.Remoting以下のクラスを使ったリモート処理が可能です。
これらのサポートを使用して、別のマシンに存在するクラスのインスタンスをリモート処理する時に、以下のようなコードで参照を取得します。

Hoge hoge = (Hoge)Activator.GetObject(typeof(Hoge), "tcp://remote.com:1234/Hoge");

このhogeはリモートのインスタンスを参照しているため、メソッドやプロパティの呼び出しなどは全てリモートに中継されます。

不思議なのはここからで、このhogeが参照している(ローカルの)インスタンスは、hogeが実体化したもの「ではなく」、System.Runtime.Remoting.Proxies.__TransparentProxyというクラスです。
(MSDNライブラリには載っていません。ildasmでmscorlibを覗くと見えます。)

ローカルのインスタンスがhogeではないというのは、何となくわかります。ローカルにhogeのインスタンスがあっても無意味だからです。この__TransparentProxyクラスがHogeクラスをシミュレートしているのだろうということは理解できます。
しかし、Activator.GetObject()が返したSystem.ObjectをHogeクラスでキャスト出来ることが謎です。

Hogeでキャスト出来る為には、オブジェクトのインスタンスがHogeクラス自身か、Hogeクラスを継承している必要があると思います。しかし、__TransparentProxyクラスはSystem.Objectから直接派生しています。
通常ならどうやってもキャスト出来ないと思います。

CLRのキャストメカニズムに何か「仕掛け」があるのではないかと思っていますが、判りませんでした。もし、この仕掛けを利用する手段があれば、実行時に動的にカスタマイズしたクラスを生成して、メソッドやプロパティへのアクセスをフックして、追加処理を行わせることが出来るのではないかと思っています。

何か情報をもっていましたらお願いします。

#実行時にクラスを動的に継承して新しいクラスを作ることは出来ます。
#(System.Reflection.Emitを使う。かなり面倒)
#そうすれば、前述のようにHogeにキャストして使用することが出来ます。
#が、「継承した」というしばりから逃れられません。
#たとえば、ベースクラスのコンストラクタを呼ばざるを得ないとか、
#ベースクラスのフィールドが無意味に存在するとか...
Qoo
大ベテラン
会議室デビュー日: 2003/04/08
投稿数: 121
投稿日時: 2003-05-21 11:54
こんにちは。

引用:

hogeraさんの書き込み (2003-05-21 11:04) より:

しかし、Activator.GetObject()が返したSystem.ObjectをHogeクラスでキャスト出来ることが謎です。

Hogeでキャスト出来る為には、オブジェクトのインスタンスがHogeクラス自身か、Hogeクラスを継承している必要があると思います。しかし、__TransparentProxyクラスはSystem.Objectから直接派生しています。
通常ならどうやってもキャスト出来ないと思います。



ヘルプの「明示的な参照変換」のところに、次のような記述があります。
「任意のクラス型 S から、任意のクラス型 T への変換。ただし、S が T の基本クラスである場合。」
今回の場合だと、S はSystem.Object、T はHoge ということで、キャストはできる(あくまでコンパイルが通るというレベルになりますが)ことになると思います。

外していたらすみません。
kekyo
会議室デビュー日: 2002/12/12
投稿数: 13
投稿日時: 2003-05-21 19:21
こんにちは。

引用:

Qooさんの書き込み (2003-05-21 11:54) より:

ヘルプの「明示的な参照変換」のところに、次のような記述があります。
「任意のクラス型 S から、任意のクラス型 T への変換。ただし、S が T の基本クラスである場合。」
今回の場合だと、S はSystem.Object、T はHoge ということで、キャストはできる(あくまでコンパイルが通るというレベルになりますが)ことになると思います。




その通り、コンパイルは通ります。そして、実行時の型チェックで__TransparentProxyクラスをHogeクラスにキャストしようとするので(本当なら)例外が発生します。
しかし、この例ではそれが発生しないのです。

GetObject()が間違いなく__TransparentProxyクラスを返していることはデバッガで確認しています。
Qoo
大ベテラン
会議室デビュー日: 2003/04/08
投稿数: 121
投稿日時: 2003-05-22 11:49
こんにちは。

ヘルプの以下のところと、いくつかのクラスライブラリに関する説明文からの情報です。

---- Help 参照箇所 ---------------------------------------------------------
「.NET Frameworkを使用したプログラミング」
 「異なるアプリケーションドメインのオブジェクトどうしの.NETリモート処理によるアクセス」
  「.NETリモート処理の概要」
   「リモートにアクセスできるオブジェクトとリモートにアクセスできないオブジェクト」
   「オブジェクト参照、プロキシ、メッセージ、およびメッセージシンク」
---------------------------------------------------------------------------

System.MarshalByRefObjectには、CreateObjRef()というメソッドがある。そのメソッドは、
System.Runtime.Remoting.ObjRefという型のオブジェクトを返す。

System.Runtime.Remoting.ObjRefにはTypeInfoというプロパティがある。そのプロパティは、IRemotingTypeInfoというオブジェクトにアクセスする。

IRemotingTypeInfoにはCanCastTo()というメソッドがある。そのメソッドに関する説明文の
中に、「ObjRef から生成されたプロキシをキャストしようとすると、現在のメソッドが呼び出されます。」と記述されている。

で、System.Runtime.Remoting.Proxies.__TransparentProxyとどう結びつくのかというところなんですが...さすがに確たる情報がないですね (^^;
(このクラス自身にキャスト(明示的変換)する処理が実装されてそうな気がするけど)
Dr. K
常連さん
会議室デビュー日: 2003/04/26
投稿数: 25
投稿日時: 2003-05-22 14:15
Dr. K と申します。いち学生で知識・経験はみなさんに大きく劣ると思いますが、以下の件について
引用:

Hogeでキャスト出来る為には、オブジェクトのインスタンスがHogeクラス自身か、Hogeクラスを継承している必要があると思います。しかし、__TransparentProxyクラスはSystem.Objectから直接派生しています。
通常ならどうやってもキャスト出来ないと思います。



これは、CLRが実行時にメタデータを参照してるために可能にのだと思います。Jeffrey Richter著の「Programming .NET Framework」や Serge Lidin著の「Inside Microsoft .NET IL Assembler」という書籍にそう載っています。
.NET CLR ではキャストの実行時にもメタデータを参照しているので、以下のプログラムを実行できるのだと思います。これはかなり簡単な例ですが原理は同じですよね???

void foo1()
{
string str = "";
foo2(str);
}

void foo2(object obj)
{
Console.WriteLine((string)obj);
}

勘違い&的を外していたらすいません。
Izumi, Y.
ベテラン
会議室デビュー日: 2002/03/19
投稿数: 77
お住まい・勤務地: 東京
投稿日時: 2003-05-23 04:59
引用:

で、System.Runtime.Remoting.Proxies.__TransparentProxyとどう結びつくのかというところなんですが...さすがに確たる情報がないですね (^^;


__TransparentProxy クラス自体は何のインターフェイスも実装していないようですし,継承はできない(sealed)みたいですし,まったく謎ですね。
__TransparentProxy クラスに RealProxy 型の private フィールド _rp があります。で,この RealProxy クラスには CreateObjRef メソッドが用意されているのですが,ひょっとしてこれが何か関係あるのでしょうか?

引用:

(このクラス自身にキャスト(明示的変換)する処理が実装されてそうな気がするけど)


う〜ん,やはりこれでしょうか。(Activator.GetObject(typeof(Hoge), "tcp://remote.com:1234/Hoge") as Hoge) == null が成り立てば,ほぼ間違いなくこれなのですが。

ちなみに,メタデータの話はここでは関係ないと思います。

[ メッセージ編集済み 編集者: IZUMI Yusuke 編集日時 2003-05-23 05:01 ]

[ メッセージ編集済み 編集者: IZUMI Yusuke 編集日時 2003-05-23 05:02 ]
小野@どっとねっとふぁん
ぬし
会議室デビュー日: 2001/10/30
投稿数: 402
投稿日時: 2003-05-23 09:25
Activator.GetObject(typeof(Hoge), "tcp://remote.com:1234/Hoge");
ここで、"typeof(Hoge)"が重要だと思います。
Hogeというタイプとしてオブジェクトのインスタンスを生成するよ、という
メソッドですよね。
型を指定してその型にあわせたインスタンスを生成してるんですから、
指定した型にキャストできて当然、ということになると思います。

ここで、間違えた型を指定すると、コンパイルは通るけど実行時に
エラーになる、なんてことも起きるはず。

#うーん、説明がむずかし。
 Typeクラスのことがわかってくると、なんとなくわかるとは思うのですが。

あ、そか

> hogeraさんの書き込み
>
> Hogeでキャスト出来る為には、オブジェクトのインスタンスが
> Hogeクラス自身か、Hogeクラスを継承している必要があると思います。

この文脈で話をすると、
「Activator.GetObjectで生成されるオブジェクトのインスタンスは、
 第一引数で指定した型(クラス)のインスタンス(上記ではHogeクラス)
 になります。」
ということです。

#うー、わかってもらえるだろか。。。
小野@どっとねっとふぁん
ぬし
会議室デビュー日: 2001/10/30
投稿数: 402
投稿日時: 2003-05-23 09:31
#もういっちょ

Activator.GetObjectメソッドが無理やり型変換を行うと考えると
いいですかね。
ここでの型変換は無理やりなので、もし指定した型が合ってないと
実行時にエラーが起きます。

#レイトバインディングといってもいいか。
 余計わかりにくいかな(^^;

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