用Windbg进行CLI探险(1)typeof,RuntimeTypeHandle

文章目的:主要解决typeof的时候到底发生了什么的问题。

工程:想像这样一个Solution,一个基础工程BaseProject中拥有一个接口IInterface,以及一个静态类Manager用来管理所有IInterface的具体实现类。Manager提供一个Regiest方法来注册IInterface具体实现类的相关信息,这些信息包括一个委托Func<IInterface>构造器,以及一个Type类型的实例。

然后有一个扩展工程ExtensionProject,工程中定义了一个IInterface的具体实现类implementclass,然后ExtensionProject引用了BaseProject,并使用静态类Manager的Regiest方法注册了implementclass的类型以及一个构造器.

最后是一个客户端,ConsoleApplication1,这个客户端引用了BaseProject以及ExtensionProject两个工程,然后在ExtensionProject注册完成后,通过Manager取得构造器并在生成IInterface的实例后调用其方法。

工程基本结构如下图所示:

注意看ConsoleApplication的Main方法先调用了ExtensionProject中Class1的Regiest方法注册了implementclass类型和一个Func<Interface>类型的构造器,

    static public class Class1 
{
static public void Regiest()
{
Manager.Regiest(
typeof(implementclass), () => new implementclass());
}

static void Test(Type type)
{
Console.WriteLine(type.ToString());
}
}

 Manager的定义是这样的:

    public static class Manager
{
static Type _type;
static Func<IInterface> _creator;
static public void Regiest(Type type, Func<IInterface> creator)
{
_type
= type;
_creator
= creator;
}

static public void ShowType()
{
Console.WriteLine(_type.ToString());
}

static public IInterface Get()
{
return _creator();
}
}

然后调用了BaseClass1的Test方法执行测试。在这个测试方法里面,我执行了这样的代码:

 

毫无疑问,这个程序可以正常运转。

这个过程的奇妙之处在于,首先.Net里面有一个基本单位Module,我们这个工程在运行时将会在内存中加载3个Module,分别是BaseProject,ExtensionProject,以及ConsoleApplication1。

然后在运行时我们“居然”在BaseProject里面生成了ExtensionProject的imlementclass类的实例,并且BaseProject没有引用ExtensionProject。这在普通的应用程序里也许没什么大惊小怪的,他们的代码实际上都是毫无区别的加载在内存中可以随意使用。但是我们前面说了.Net的程序加载的是相对独立的Module。

每一个Module的元数据都是相互独立,拥有自己的type列表(type_def_table),type引用列表(type_ref_table)等,以及metadatatoken的。

metadatatoken是什么呢,metadatatoken是一个四字节的数字,其中第1个字节表示自己的类型,部分类型列表如下:

typedef enum CorTokenType {

mdtModule
= 0x00000000,
mdtTypeRef
= 0x01000000,
mdtTypeDef
= 0x02000000,
mdtFieldDef
= 0x04000000,
mdtMethodDef
= 0x06000000,

(可以到这里了解更多内容:http://blog.csdn.net/CsToD/article/details/4212568

后3个字节则表明了自己在相应的类型列表中的位置。如果是编译时的引用类型的话,metadatatoken的类型

将会是mdtTypeRef,这个时候可以通过该token从type_ref_table中读取相应信息,然后运用该信息从定义了这个type的module中加载这个类型。

而如果这个类型就是定义在本module内部的话,这个metadatatoken的类型是mdtTypeDef ,可以直接从type_def_table中读取类型的信息。显然,token尤其是mdtTypeDef 的作用域只是module内部而已,无法跨module使用。

我们注册implementclass的地方是在ExtensionProject里面,通过调用的BaseProject中Manager的方法实现的,这个时候如果传入的是metadatatoken,那么显然这个token的类型应该是mdtTypeDef,且在BaseProject的Module中是无法使用的,那么它是怎么做的呢?我们看注册部分的代码(稍微修改了一下):

源代码(Reflector):

 1 public static void Regiest()
2 {
3 Test(typeof(implementclass));
4 Console.WriteLine("Prepare Regiest......");
5 Console.ReadLine();
6 if (CS$<>9__CachedAnonymousMethodDelegate1 == null)
7 {
8 CS$<>9__CachedAnonymousMethodDelegate1 = new Func<IInterface>(null, (IntPtr) <Regiest>b__0);
9 }
10 Manager.Regiest(typeof(implementclass), CS$<>9__CachedAnonymousMethodDelegate1);
11 }
12
13

其中有许多关于构造器的闭包的部分,这一部分我们先不管它,主要看第10行的typeof(implementclass)的部分,正是这个部分把ExtensionProjectModule中implementclass的信息传递过去了的。为了简化这个过程我在第3行写了一个没用的Test方法,通过它我们可以只关注typeof的部分而不必理会闭包的操作。

然后我们看IL代码:

 1 .method public hidebysig static void Regiest() cil managed
2 {
3 .maxstack 4
4 L_0000: nop
5 L_0001: ldtoken ExtensionProject.implementclass
6 L_0006: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
7 L_000b: call void ExtensionProject.Class1::Test(class [mscorlib]System.Type)
8 L_0010: nop
9 L_0011: ldstr "Prepare Regiest......"
10 L_0016: call void [mscorlib]System.Console::WriteLine(string)
11 L_001b: nop
12 L_001c: call string [mscorlib]System.Console::ReadLine()
13 L_0021: pop
14 L_0022: ldtoken ExtensionProject.implementclass
15 L_0027: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
16 L_002c: ldsfld class [mscorlib]System.Func`1<class [BaseProject]BaseProject.IInterface> ExtensionProject.Class1::CS$<>9__CachedAnonymousMethodDelegate1
17 L_0031: brtrue.s L_0046
18 L_0033: ldnull
19 L_0034: ldftn class [BaseProject]BaseProject.IInterface ExtensionProject.Class1::<Regiest>b__0()
20 L_003a: newobj instance void [mscorlib]System.Func`1<class [BaseProject]BaseProject.IInterface>::.ctor(object, native int)
21 L_003f: stsfld class [mscorlib]System.Func`1<class [BaseProject]BaseProject.IInterface> ExtensionProject.Class1::CS$<>9__CachedAnonymousMethodDelegate1
22 L_0044: br.s L_0046
23 L_0046: ldsfld class [mscorlib]System.Func`1<class [BaseProject]BaseProject.IInterface> ExtensionProject.Class1::CS$<>9__CachedAnonymousMethodDelegate1
24 L_004b: call void [BaseProject]BaseProject.Manager::Regiest(class [mscorlib]System.Type, class [mscorlib]System.Func`1<class [BaseProject]BaseProject.IInterface>)
25 L_0050: nop
26 L_0051: ret
27 }

只要看第5,6行是将typeof(implementclass)入栈,然后再第7行调用Test方法的部分。后面的部分我们先不管它。

其中第5行我们看到首先用ldtoken指令,

ldtoken ExtensionProject.implementclass

这个指令可不是将token入栈,按照MSN的说明这个指令是

传递的标记被转换为 RuntimeHandle 并被推送到堆栈上。

这里具体而言是入栈了System.RuntimeTypeHandle,

第6行利用这个RuntimeTypeHandle构造了System.Type(这其实是一个RuntimeType的内部类型,以后的文章中我们将用Windbg看到这一点),

call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)

第7行调用Test方法传入了RuntimeType, 

call void ExtensionProject.Class1::Test(class [mscorlib]System.Type)

综上,调用typeof的时候我们做了2件事情,

一个是生成了一个RuntimeTypeHandle实例,

然后利用这个实例生成了一个RuntimeType,RuntimeType是Type的子类,且显然可以跨Module使用,而且从名称上看应该就是为了在运行时跨Module使用而设计的。

而且我们可以得出结论,静态的引用其他Module的类型我们可以通过mdtTypeRef类型的token实现,运行时动态创建其他Module中定义的类型我们可以通过RuntimeTypeHandle实现,那么更进一步的,这个RuntimeTypeHandle是什么呢?

 我们运行这样的代码看一看:

1             RuntimeTypeHandle rth = Type.GetTypeHandle(new implementclass());
2 Console.WriteLine("RuntimeTypeHandle:"+rth.Value);
3 ModuleHandle mh = rth.GetModuleHandle();
4 Console.WriteLine("metadatatoken" + typeof(implementclass).MetadataToken);
5 var handle = mh.GetRuntimeTypeHandleFromMetadataToken(typeof(implementclass).MetadataToken);
6 Console.WriteLine("TypeHandleFromModule" + handle.Value);

运行结果 :

显然通过ModuleHandle和metadatatoken取得的TypeHandle是相同的,我们于是知道的定义一个元数据需要一个module和一个metadata,或者一个runtimehandle。

记住2638948和33554435两个值,它们的16进制分别是284464和02000003

我们用Windbg来Debug这个程序

0:004> !dumpmt 284464

EEClass:      00332910
Module:       002838c4
Name:         ExtensionProject.implementclass
mdToken:      02000003
File:         F:\Lab\ClientBin\TestCompressedFile\ConsoleApplication1\bin\Debug\ExtensionProject.dll
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 1

显然,十进制的2638948正是implementclass的MT(methodtable)的地址,这个RuntimeTypeHandle既然保存了MethodTable的内存地址当然可以提供跨Moduel的信息。而mdToken的02000003则正是十进制的33554435,其中第一个字节的02代表它是一个mdtTypeDef,后面的000003则指出了它在type_def_table中的位置。

 

转载于:https://www.cnblogs.com/HaFoLuoKe/archive/2011/09/13/2173446.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值