在 CLR 1.1 中获取 IL 函数体信息

在 CLR 2.0 中 Reflection 部分做了不少改进,例如增加了仅用于 Reflection 的 Assembly 加载模式,MethodBody 等一组与 IL 函数体相关的对象,以及 Token 句柄等支持。详细的介绍可以参考  What's new in System.Reflection (and friends) 一文。
而其中某些功能还是非常具有吸引力的,例如通过 MethodBody 类可以对 IL 函数体信息进行获取,包括 IL 代码流、异常处理、局部变量以及堆栈等等信息,在编写底层代码时非常方便。下面将根据在 CLR 1.1 中实现相同功能的 MethodBody 类的过程,简要介绍  Metadata 中的函数定义与基本结构。因为本文主要目的是介绍 MethodBody 的获取流程,所以涉及到过于基础的知识就不一一展开了,有兴趣的朋友可以参考我 blog 上以前的相关文章。

首先我们仿照 CLR 2.0 定义与 MethodBody 相关的几个结构:

None.gif namespace  NSFocus.ILEngine.Model
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
public enum ExceptionHandlingClauseOptions
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    Clause 
= 0,
InBlock.gif    Fault 
= 4,
InBlock.gif    Filter 
= 1,
InBlock.gif    Finally 
= 2
ExpandedSubBlockEnd.gif  }

InBlock.gif  
InBlock.gif
public class ExceptionHandlingClause // 异常处理
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif
public Type CatchType dot.gifget; }  // 捕获的目标异常类型
ExpandedSubBlockStart.gifContractedSubBlock.gif
public int FilterOffset dot.gifget; }  // 过滤代码的偏移
ExpandedSubBlockStart.gifContractedSubBlock.gif
public ExceptionHandlingClauseOptions Flags dot.gifget; } // 过滤的类型
ExpandedSubBlockStart.gifContractedSubBlock.gif
public int HandlerLength dot.gifget; } // 处理代码的长度
ExpandedSubBlockStart.gifContractedSubBlock.gif
public int HandlerOffset dot.gifget; } // 处理代码的偏移
ExpandedSubBlockStart.gifContractedSubBlock.gif
public int TryLength dot.gifget; }  // 包含代码的长度
ExpandedSubBlockStart.gifContractedSubBlock.gif
public int TryOffset dot.gifget; }  // 包含代码的偏移
ExpandedSubBlockEnd.gif
  }

InBlock.gif
InBlock.gif  
public class LocalVariableInfo // 局部变量
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif
public virtual bool IsPinned dot.gifget; } 
ExpandedSubBlockStart.gifContractedSubBlock.gif
public virtual bool IsByRef dot.gifget; }
ExpandedSubBlockStart.gifContractedSubBlock.gif
public virtual bool IsPointer dot.gifget; }
ExpandedSubBlockStart.gifContractedSubBlock.gif
public virtual int LocalIndex dot.gifget; } // 局部变量索引
ExpandedSubBlockStart.gifContractedSubBlock.gif
public virtual Type LocalType dot.gifget; } // 局部变量类型
ExpandedSubBlockEnd.gif
}

InBlock.gif
InBlock.gif
public class MethodBody // 方法体
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif
public byte[] GetILAsByteArray() dot.gifget; }  // IL 代码流
ExpandedSubBlockStart.gifContractedSubBlock.gif
public ExceptionHandlingClause[] ExceptionHandlingClauses dot.gifget; } // 异常处理语句集
ExpandedSubBlockStart.gifContractedSubBlock.gif
public bool InitLocals dot.gifget; }  // 是否初始化局部变量
ExpandedSubBlockStart.gifContractedSubBlock.gif
public int LocalSignatureMetadataToken dot.gifget; }  // 局部变量类型定义
ExpandedSubBlockStart.gifContractedSubBlock.gif
public LocalVariableInfo[] LocalVariables dot.gifget; }  // 局部变量集
ExpandedSubBlockStart.gifContractedSubBlock.gif
public int MaxStackSize dot.gifget; } // 最大堆栈长度
ExpandedSubBlockEnd.gif
}

ExpandedBlockEnd.gif}

None.gif 
None.gif


熟悉 CLR PE 结构的朋友可以发现,这几个类型基本上是与 CLE PE 中对函数体的定义对应的,只不过将内部定义时的 Token 和 Signature 变成了运行时的 Reflection 类型。
因此要获取这些信息,我们可以分两步完成:

1.获取静态的函数体定义
2.将静态定义转换为动态 Reflection 类型

对第一步来说,实现的方法很多:

首先可以自己对 Metadata 结构进行解析,例如我以前 blog 上对 Metadata 结构进行分析的系列文章,就给出了在 Delphi- Jedi JCL 项目中提供的 Delphi 版本实现。这种方式实现的灵活性和扩展性非常强,但工作量以及对 CLR PE/Metadata 的理解也要去较高。有兴趣的朋友可以参考我以前的文章,这里就不罗嗦了。
其次可以通过 CLR 底层提供的 Unmanaged API 来完成,.Net Framework 自带的  Metadata Unmanaged API.doc 文档里面有较为详细的介绍。这种方案无需对 CLR PE 结构有太深入的理解,但功能上受到一定限制,例如对 MethodBody 就没有提供函数一级的支持。
另外一种相对较好的方法,是委托让 CLR 自己进行解析和处理,我们直接通过 Reflection 的方式去获取,并进行一定程度的解析。本文后面的实现就将使用此思路,Managed C# 处理上层逻辑,Managed C++ 处理底层 解析。
而在代码组织上,以名字空间 NSFocus.ILEngine.Model 定义基本的模型类;以名字空间  NSFocus.ILEngine.Helper 提供底层的辅助解析实现类;以名字空间 NSFocus.ILEngine.Core 提供高层组装支持,使用 Helper 对 Model 进行组织,并对外提供统一的接口。如果有必要,还可以通过多 Module 的方式将不同语言实现的代码组织到一个 Assembly 中进行签名和发布。

在考察了 MethodBase/MethodInfo 类型后,可以发现虽然 CLR 1.1 没有提供底层信息的封装,但仍留有一个突破口,这就是  MethodBase.MethodHandle 属性。这个属性是一个 RuntimeMethodHandle 类型的值对象,保存了一个指向运行时 MethodDesc 结构的指针。CLR 中对每个类型维护一个 MethodTable,每个 MethodTable 包含多个  MethodDesc 来表述其方法。与 MethodTable/MethodDesc 相关的信息,请参考 《.NET本质论 第1卷:公共语言运行库》 以及我发的 《用WinDbg探索CLR世界 [3] 跟踪方法的 JIT 过程》一文。
在 MethodDesc 中与我们关系较大的,是其包含的 CodeOrIL 字段。 MethodDesc 的结构大致如下:

None.gif #define  METHOD_IS_IL_FLAG  0xC0000000
None.gif
None.gif
private  __gc  class  MethodBodyImpl :  public  MethodBody
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {        
InBlock.gif
private:
InBlock.gif  __value 
struct MethodDesc
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    WORD SlotNumber;
InBlock.gif    WORD Flags;
InBlock.gif    DWORD CodeOrIL;
InBlock.gif
InBlock.gif    DWORD GetRVA()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif      
if((CodeOrIL & METHOD_IS_IL_FLAG) == METHOD_IS_IL_FLAG)
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif        
return CodeOrIL & ~METHOD_IS_IL_FLAG;
ExpandedSubBlockEnd.gif      }

InBlock.gif
InBlock.gif      
return 0;
ExpandedSubBlockEnd.gif    }

ExpandedSubBlockEnd.gif  }
;
ExpandedBlockEnd.gif}

None.gif

 

如果 CodeOrIL 的 METHOD_IS_IL_FLAG 标志位被设置,则此字段的低 24 保存着 IL 代码在模块中的 RVA (相对地址),通过这些信息我们可以很容易获取到方法体被加载到内存中的位置。

None.gif private  __gc  class  MethodBodyImpl :  public  MethodBody
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {     
InBlock.gif  
public:
InBlock.gif    MethodBodyImpl(IMetaDataImport 
*pMdImport, MethodBase *method)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{        
InBlock.gif      MethodDesc 
*pMD = (MethodDesc *)(method->get_MethodHandle().Value.ToPointer());         
InBlock.gif
InBlock.gif      IntPtr pFilename 
= Marshal::StringToHGlobalUni(method->DeclaringType->Assembly->Location);
InBlock.gif
InBlock.gif      HMODULE hDll 
= ::LoadLibraryW((LPCWSTR)pFilename.ToPointer());
InBlock.gif
InBlock.gif      Marshal::FreeHGlobal(pFilename);
InBlock.gif      
InBlock.gif      DWORD dwAddr 
= (DWORD)hDll + pMD->GetRVA();
InBlock.gif
InBlock.gif
// 对方法体进行解析
InBlock.gif
// dot.gif
ExpandedSubBlockEnd.gif
  }

ExpandedBlockEnd.gif}

None.gif
None.gif

 

注意这里获取模块加载地址,使用了 LoadLibraryW 对方法所在 Assembly 的 DLL 重新加载的方法。因为在 Win32 中,重复对一个 DLL 加载不会执行任何操作,仅仅返回其原本加载的基址。
而在对方法体进行解析的时候,笔者直接使用 CLR 提供的一组辅助类 COR_ILMETHOD_DECODER 等,这些类的定义可以在 corhlpr.h/corhlpr.cpp 中找到。
而更为基本的方法体结构在 CorHdr.h 中被定义如下:

ExpandedBlockStart.gif ContractedBlock.gif /**/ /***************************************************************************/
ExpandedBlockStart.gifContractedBlock.gif
/**/ /* Used when the method is tiny (< 64 bytes), and there are no local vars */
None.giftypedef 
struct  IMAGE_COR_ILMETHOD_TINY
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    BYTE Flags_CodeSize;    
ExpandedBlockEnd.gif}
 IMAGE_COR_ILMETHOD_TINY;
None.gif
ExpandedBlockStart.gifContractedBlock.gif
/**/ /************************************/
None.gif
//  This strucuture is the 'fat' layout, where no compression is attempted. 
None.gif
//  Note that this structure can be added on at the end, thus making it extensible
None.gif
typedef  struct  IMAGE_COR_ILMETHOD_FAT
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    unsigned Flags    : 
12;     // Flags    
InBlock.gif
    unsigned Size     :  4;     // size in DWords of this structure (currently 3)   
InBlock.gif
    unsigned MaxStack : 16;     // maximum number of items (I4, I, I8, obj dot.gif), on the operand stack   
InBlock.gif
    DWORD   CodeSize;           // size of the code 
InBlock.gif
    mdSignature   LocalVarSigTok;     // token that indicates the signature of the local vars (0 means none)  
ExpandedBlockEnd.gif
}
 IMAGE_COR_ILMETHOD_FAT;
None.gif
None.giftypedef union IMAGE_COR_ILMETHOD
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    IMAGE_COR_ILMETHOD_TINY       Tiny;   
InBlock.gif    IMAGE_COR_ILMETHOD_FAT        Fat;    
ExpandedBlockEnd.gif}
 IMAGE_COR_ILMETHOD;
None.gif 
None.gif
None.gif

 

根据方法的复杂程度,其方法头有 Tiny/Fat 两种模式。对前者来说,方法不包含局部变量、不使用堆栈因此只需要一个定义长度的字段即可。
CorHlpr.h 对这些基本信息的访问进行了封装,使我们不必直接对字段进行繁琐的操作:

ExpandedBlockStart.gif ContractedBlock.gif /**/ /***************************************************************************/
ExpandedBlockStart.gifContractedBlock.gif
/**/ /* Used when the method is tiny (< 64 bytes), and there are no local vars */
None.giftypedef 
struct  tagCOR_ILMETHOD_TINY : IMAGE_COR_ILMETHOD_TINY
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif    
bool     IsTiny() const         dot.gifreturn((Flags_CodeSize & (CorILMethod_FormatMask >> 1)) == CorILMethod_TinyFormat); } 
ExpandedSubBlockStart.gifContractedSubBlock.gif    unsigned GetCodeSize() 
const    dot.gifreturn(((unsigned) Flags_CodeSize) >> (CorILMethod_FormatShift-1)); } 
ExpandedSubBlockStart.gifContractedSubBlock.gif    unsigned GetMaxStack() 
const    dot.gifreturn(8); }  
ExpandedSubBlockStart.gifContractedSubBlock.gif    BYTE
*    GetCode() const        dot.gifreturn(((BYTE*this+ sizeof(struct tagCOR_ILMETHOD_TINY)); } 
ExpandedSubBlockStart.gifContractedSubBlock.gif    DWORD    GetLocalVarSigTok() 
const  dot.gifreturn(0); }  
ExpandedSubBlockStart.gifContractedSubBlock.gif    COR_ILMETHOD_SECT
* GetSect() const dot.gifreturn(0); }   
ExpandedBlockEnd.gif}
 COR_ILMETHOD_TINY;
None.gif
None.gif
ExpandedBlockStart.gifContractedBlock.gif
/**/ /************************************/
None.gif
//  This strucuture is the 'fat' layout, where no compression is attempted. 
None.gif
//  Note that this structure can be added on at the end, thus making it extensible
None.gif
typedef  struct  tagCOR_ILMETHOD_FAT : IMAGE_COR_ILMETHOD_FAT
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif    
bool     IsFat() const              dot.gifreturn((Flags & CorILMethod_FormatMask) == CorILMethod_FatFormat); }  
ExpandedSubBlockStart.gifContractedSubBlock.gif    unsigned GetMaxStack() 
const        dot.gifreturn(MaxStack); }   
ExpandedSubBlockStart.gifContractedSubBlock.gif    unsigned GetCodeSize() 
const        dot.gifreturn(CodeSize); }   
ExpandedSubBlockStart.gifContractedSubBlock.gif    mdToken  GetLocalVarSigTok() 
const      dot.gifreturn(LocalVarSigTok); } 
ExpandedSubBlockStart.gifContractedSubBlock.gif    BYTE
*    GetCode() const            dot.gifreturn(((BYTE*this+ 4*Size); }    
ExpandedSubBlockStart.gifContractedSubBlock.gif    
const COR_ILMETHOD_SECT* GetSect() const dot.gif{  
InBlock.gif        
if (!(Flags & CorILMethod_MoreSects)) return(0);    
InBlock.gif        
return(((COR_ILMETHOD_SECT*) (GetCode() + GetCodeSize()))->Align());    
ExpandedSubBlockEnd.gif        }
   
ExpandedBlockEnd.gif}
 COR_ILMETHOD_FAT;
None.gif
None.gif
struct  COR_ILMETHOD
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
// a COR_ILMETHOD header should not be decoded by hand.  Instead us 
InBlock.gif    
// COR_ILMETHOD_DECODER to decode it.   
InBlock.gif
    friend class COR_ILMETHOD_DECODER;  
InBlock.gif
InBlock.gif
//private:
InBlock.gif
    union   
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{   
InBlock.gif        COR_ILMETHOD_TINY       Tiny;   
InBlock.gif        COR_ILMETHOD_FAT        Fat;    
ExpandedSubBlockEnd.gif    }
;  
InBlock.gif        
// Code follows the Header, then immedately after the code comes    
InBlock.gif        
// any sections (COR_ILMETHOD_SECT).    
ExpandedBlockEnd.gif
}
;
None.gif
None.gif

 而真正进行方法头解码的是在 COR_ILMETHOD_DECODER 类中调用的 DecoderInit 函数:
 

None.gif class  COR_ILMETHOD_DECODER :  public  COR_ILMETHOD_FAT  
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
public:
InBlock.gif        
// Decode the COR header into a more convinient internal form   
InBlock.gif        
// This is the ONLY way you should access COR_ILMETHOD so format changes are easier 
ExpandedSubBlockStart.gifContractedSubBlock.gif
    COR_ILMETHOD_DECODER(const COR_ILMETHOD* header) dot.gif{ DecoderInit(this,(COR_ILMETHOD*)header); };   
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    unsigned EHCount() 
const dot.gif{  
InBlock.gif        
if (EH == 0return(0); 
InBlock.gif        
else return(EH->EHCount()); 
ExpandedSubBlockEnd.gif        }
   
InBlock.gif
InBlock.gif    
// Flags        these are available because we inherit COR_ILMETHOD_FAT 
InBlock.gif    
// MaxStack 
InBlock.gif    
// CodeSize 
InBlock.gif
    const BYTE* Code;   
InBlock.gif    PCCOR_SIGNATURE LocalVarSig;        
// pointer to signature blob, or 0 if none  
InBlock.gif
    const COR_ILMETHOD_SECT_EH* EH;     // eh table if any  0 if none   
InBlock.gif
    const COR_ILMETHOD_SECT* Sect;      // additional sections  0 if none   
ExpandedBlockEnd.gif
}
;
None.gif
None.gif
void  __stdcall DecoderInit( void   *  pThis, COR_ILMETHOD *  header) 
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    memset(pThis, 
0sizeof(COR_ILMETHOD_DECODER));
ExpandedSubBlockStart.gifContractedSubBlock.gif    
if (header->Tiny.IsTiny()) dot.gif{
InBlock.gif        ((COR_ILMETHOD_DECODER
*)pThis)->MaxStack = header->Tiny.GetMaxStack();
InBlock.gif        ((COR_ILMETHOD_DECODER
*)pThis)->Code = header->Tiny.GetCode();
InBlock.gif        ((COR_ILMETHOD_DECODER
*)pThis)->CodeSize = header->Tiny.GetCodeSize();
InBlock.gif        ((COR_ILMETHOD_DECODER
*)pThis)->Flags |= CorILMethod_TinyFormat;
InBlock.gif        
return;
ExpandedSubBlockEnd.gif    }

ExpandedSubBlockStart.gifContractedSubBlock.gif    
if (header->Fat.IsFat()) dot.gif{
InBlock.gif        _ASSERTE((((size_t) header) 
& 3== 0);        // header is aligned
InBlock.gif
        *((COR_ILMETHOD_FAT*) pThis) = header->Fat;
InBlock.gif        ((COR_ILMETHOD_DECODER
*)pThis)->Code = header->Fat.GetCode();
InBlock.gif        _ASSERTE(header
->Fat.Size >= 3);        // Size if valid
InBlock.gif
        ((COR_ILMETHOD_DECODER*)pThis)->Sect = header->Fat.GetSect();
ExpandedSubBlockStart.gifContractedSubBlock.gif        
if (((COR_ILMETHOD_DECODER*)pThis)->Sect != 0 && ((COR_ILMETHOD_DECODER*)pThis)->Sect->Kind() == CorILMethod_Sect_EHTable) dot.gif{
InBlock.gif            ((COR_ILMETHOD_DECODER
*)pThis)->EH = (COR_ILMETHOD_SECT_EH*) ((COR_ILMETHOD_DECODER*)pThis)->Sect;
InBlock.gif            ((COR_ILMETHOD_DECODER
*)pThis)->Sect = ((COR_ILMETHOD_DECODER*)pThis)->Sect->Next();
ExpandedSubBlockEnd.gif        }

InBlock.gif        
return;
ExpandedSubBlockEnd.gif    }

InBlock.gif    
// so we don't asert on trash  _ASSERTE(!"Unknown format"[img]/images/wink.gif[/img];
ExpandedBlockEnd.gif
}

None.gif 
None.gif

 

值得注意的是,这里提供的 DecoderInit 函数,实际上进行的是一个并不完整的解码过程,方法体中非常重要的局部变量信息 LocalSig  并没有提供。在其注释和 sscli 的实现中,提供了另外一个依赖于 IMDInternalImport 内部接口的实现,提供完成的处理支持。因此我们只能通过现有的这些有限支持,自行对局部变量进行处理。在下面可以看到大量的代码,用于对局部变量的 Signature 进行解码 :S

在获取方法体入口并进行解码后,大部分工作都是将解码后的信息填充到 MethodBody 的内容中:

None.gif private  __gc  class  MethodBodyImpl :  public  MethodBody
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {        
InBlock.gif
public:
InBlock.gif  MethodBodyImpl(IMetaDataImport 
*pMdImport, MethodBase *method)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif
InBlock.gif   
// 获取方法体地址
InBlock.gif   
InBlock.gif   
// 对方法头进行解码
InBlock.gif
InBlock.gif
// 填充方法体信息
InBlock.gif
InBlock.gif
// 构造结构化异常语句集
InBlock.gif
InBlock.gif
// 构造局部变量集
ExpandedSubBlockEnd.gif
}

ExpandedBlockEnd.gif}
    
None.gif
None.gif

 

可以看到剩余代码主要分为三个部分:

1.填充 MethodBody 的信息
2.构造结构化异常语句集
3.构造局部变量集

对方法体信息的填充,以及结构化异常信息的填充,基本上是平铺直叙的代码,这里就不多说了。

麻烦的是如何对局部变量进行解码。因为在方法头中,局部变量是以一个 token 的方式提供的,它指向 StandAloneSig 元数据表中的一个 Sign,用以定义局部变量集。其 Sign 的格式 LocalVarSign 大致如下:

以下为引用:

                                   +------------------------------------+
                                    |   +------------+                            \
                                    v  v                   \                            \
LOCAL_SIG -> Count ---> Constraint --> ByRef -> Type ->
                                        \                     ^  \            ^
                                         \                   /    \          /
                                          +----------+        +---+ 
 

 

呵呵,懒得贴图了,画个 ASIIC 的示意图意思意思吧,有兴趣的朋友可以看看 .NET Framework SDK 自带的 Partition II Metadata.doc 文档的 LocalVarSign 相关图表。

可以看到解码的工作大概可以分为两个阶段。

首先是判断是否为 LocalVarSign,并获取局部变量的数量:

None.gif private  __gc  class  MethodBodyImpl :  public  MethodBody
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {        
InBlock.gif
public:
InBlock.gif  MethodBodyImpl(IMetaDataImport 
*pMdImport, MethodBase *method)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif
InBlock.gif    
if(m_localSignatureMetadataToken == NULL)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif      m_localVariables 
= __gc new LocalVariableInfo *[];
ExpandedSubBlockEnd.gif    }

InBlock.gif    
else
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif      PCCOR_SIGNATURE pSig;
InBlock.gif      DWORD cbSig;
InBlock.gif
InBlock.gif      Marshal::ThrowExceptionForHR(pMdImport
->GetSigFromToken(m_localSignatureMetadataToken, &pSig, &cbSig));
InBlock.gif
InBlock.gif      
if(CorSigUncompressCallingConv(pSig) == IMAGE_CEE_CS_CALLCONV_LOCAL_SIG)
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif        DWORD dwCount;
InBlock.gif
InBlock.gif        pSig 
+= CorSigUncompressData(pSig, &dwCount);
InBlock.gif
InBlock.gif        m_localVariables 
= __gc new LocalVariableInfo *[dwCount];
InBlock.gif
InBlock.gif        
for(int i=0; i<m_localVariables->Length; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{                                
InBlock.gif          m_localVariables[i] 
= __gc new LocalVariableInfoImpl(
InBlock.gif            i, pMdImport, method
->DeclaringType->Module, pSig);
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif      }

ExpandedSubBlockEnd.gif    }
         
ExpandedSubBlockEnd.gif}

ExpandedBlockEnd.gif}

None.gif
None.gif

 

对每个局部变量,构造一个 LocalVariableInfoImpl 对象进行实际的解析。

None.gif private  __gc  class  LocalVariableInfoImpl :  public  LocalVariableInfo
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
public:
InBlock.gif  LocalVariableInfoImpl(
int idx, IMetaDataImport *pMdImport, Module *pModule, PCCOR_SIGNATURE &pSig)
InBlock.gif    : LocalVariableInfo(idx)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    m_isPinned 
= ELEMENT_TYPE_PINNED == *pSig;
InBlock.gif
InBlock.gif    
if(m_isPinned) pSig++;               
InBlock.gif
InBlock.gif    m_isByRef 
= ELEMENT_TYPE_BYREF == *pSig;
InBlock.gif
InBlock.gif    
if(m_isByRef) pSig++;
InBlock.gif
InBlock.gif    Type 
*type = UncompressElement(pMdImport, pModule, pSig);
InBlock.gif
InBlock.gif    
if(type)
InBlock.gif      m_typeHandle 
= type->TypeHandle;
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif 
None.gif

 

对 LocalVarSign 来说,Constraint 只有一种可选的 ELEMENT_TYPE_PINNED,定义此对象是否被固定在堆中,以提供 Interop 的支持。而 ByRef 则通过可选的 ELEMENT_TYPE_BYREF 定义此局部变量是否为引用。最后也是最关键的  Type 元素,解析过程被封装到 UncompressElement 函数中。它将提供对 Type 的解析并返回一个 Reflection 的对应 Type 对象。
最终在 LocalVariableInfo.m_typeHandle 中保存此类型的 RuntimeTypeHandle 以便在后期使用。而这个 RuntimeTypeHandle 与前面 RuntimeMethodHandle 很类似,内部保存了执行 MethodTable 的指针,并可使用 Type.GetTypeFromHandle 函数将之转换回类型。在 CLR 2.0 中,这几种 Handle 的功能被大大增强,回头有空再单独写文章详细讨论。

None.gif namespace  NSFocus.ILEngine.Model
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
public class LocalVariableInfo
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif  
protected RuntimeTypeHandle m_typeHandle;
InBlock.gif
InBlock.gif    
public virtual Type LocalType
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif      
get
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif        
return m_typeHandle.Value != IntPtr.Zero ? Type.GetTypeFromHandle(m_typeHandle) : null;
ExpandedSubBlockEnd.gif      }

ExpandedSubBlockEnd.gif    }

ExpandedSubBlockEnd.gif}

ExpandedBlockEnd.gif}

None.gif 
None.gif

        最后剩下的 UncompressElement 较为复杂,因为 Type 这个 Signature 为了灵活性,提供了非常强大的扩展机制。

以下为引用:

Type ::=  
BOOLEAN | CHAR | I1 | U1 | I2 | U2 | I4 | U4 | I8 | U8 | R4 | R8 | I  | U |
| VALUETYPE TypeDefOrRefEncoded
| CLASS TypeDefOrRefEncoded
| STRING
| OBJECT
| PTR CustomMod* VOID
| PTR CustomMod* Type
| FNPTR MethodDefSig
| FNPTR MethodRefSig
| ARRAY Type ArrayShape  (general array, see clause 22.2.13)
| SZARRAY CustomMod* Type (single dimensional, zero-based array i.e. vector)
 

 

因此 UncompressElement 将分为几个部分介绍:

1.原始类型
2.结构和类
3.数组
4.指针与函数指针

对原始类型的处理较为简单,基本上都是对 CorElementType 枚举类型中元素,与 CLR 中对应元素的映射。

None.gif private  __gc  class  LocalVariableInfoImpl :  public  LocalVariableInfo
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  Type 
*UncompressElement(IMetaDataImport *pMdImport, Module *pModule, PCCOR_SIGNATURE &pSig)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
switch(CorSigUncompressElementType(pSig))
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif    
// 处理原始类型
InBlock.gif
    case ELEMENT_TYPE_VOID: // 0x1
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(Void);
ExpandedSubBlockEnd.gif      }

InBlock.gif    
case ELEMENT_TYPE_BOOLEAN: // 0x2
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(Boolean);
ExpandedSubBlockEnd.gif      }
            
InBlock.gif    
case ELEMENT_TYPE_CHAR: // 0x3
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(Char);
ExpandedSubBlockEnd.gif      }

InBlock.gif    
case ELEMENT_TYPE_I1: // 0x4
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(SByte);
ExpandedSubBlockEnd.gif      }

InBlock.gif    
case ELEMENT_TYPE_U1: // 0x5
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(Byte);
ExpandedSubBlockEnd.gif      }

InBlock.gif    
case ELEMENT_TYPE_I2: // 0x6
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(Int16);
ExpandedSubBlockEnd.gif      }

InBlock.gif    
case ELEMENT_TYPE_U2: // 0x7
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(UInt16);
ExpandedSubBlockEnd.gif      }

InBlock.gif    
case ELEMENT_TYPE_I4: // 0x8
InBlock.gif
    case ELEMENT_TYPE_I: // 0x18, native integer size  
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(Int32);
ExpandedSubBlockEnd.gif      }

InBlock.gif    
case ELEMENT_TYPE_U4: // 0x9
InBlock.gif
    case ELEMENT_TYPE_U: // 0x19, native unsigned integer size 
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(UInt32);
ExpandedSubBlockEnd.gif      }

InBlock.gif    
case ELEMENT_TYPE_I8: // 0xa
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(Int64);
ExpandedSubBlockEnd.gif      }

InBlock.gif    
case ELEMENT_TYPE_U8: // 0xb
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(UInt64);
ExpandedSubBlockEnd.gif      }

InBlock.gif    
case ELEMENT_TYPE_R4: // 0xc
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(Single);
ExpandedSubBlockEnd.gif      }

InBlock.gif    
case ELEMENT_TYPE_R8: // 0xd
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(Double);
ExpandedSubBlockEnd.gif      }

InBlock.gif    
case ELEMENT_TYPE_STRING: // 0xe
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(String);
ExpandedSubBlockEnd.gif      }

InBlock.gif    
case ELEMENT_TYPE_OBJECT: // 0x1C, Shortcut for System.Object
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(Object);
ExpandedSubBlockEnd.gif      }
          
InBlock.gif    
case ELEMENT_TYPE_TYPEDBYREF: // 0x16, This is a simple type.   
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        
return __typeof(TypedReference);
ExpandedSubBlockEnd.gif      }
           
InBlock.gif    
// 处理结构和类
InBlock.gif
InBlock.gif
// 处理数组
InBlock.gif
InBlock.gif
// 处理指针与函数指针
ExpandedSubBlockEnd.gif
}

ExpandedSubBlockEnd.gif}

InBlock.gif
InBlock.gif

 

对结构和类型来说,则需要根据其提供的 token 来定位实际的类型。

None.gif private  __gc  class  LocalVariableInfoImpl :  public  LocalVariableInfo
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  Type 
*UncompressElement(IMetaDataImport *pMdImport, Module *pModule, PCCOR_SIGNATURE &pSig)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
switch(CorSigUncompressElementType(pSig))
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif    
// 处理原始类型
InBlock.gif

InBlock.gif    
case ELEMENT_TYPE_VALUETYPE: // 0x11, VALUETYPE <class Token> 
InBlock.gif
    case ELEMENT_TYPE_CLASS: // 0x12, CLASS <class Token>  
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        mdToken token;
InBlock.gif
InBlock.gif        pSig 
+= CorSigUncompressToken(pSig, &token);
InBlock.gif
InBlock.gif        wchar_t typeName[MAX_CLASSNAME_LENGTH];
InBlock.gif
InBlock.gif        
switch(TypeFromToken(token))
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
case mdtTypeDef:  // 类型定义
InBlock.gif
case mdtTypeRef: // 类型引用
InBlock.gif
case mdtTypeSpec: // 类型描述
ExpandedSubBlockEnd.gif
}

InBlock.gif
break;
ExpandedSubBlockEnd.gif}

InBlock.gif
InBlock.gif
// 处理数组
InBlock.gif
InBlock.gif
// 处理指针与函数指针
ExpandedSubBlockEnd.gif
}

ExpandedSubBlockEnd.gif}
 
InBlock.gif
InBlock.gif


 

根据 Type Signature 的定义,这里 token 是 TypeDefOrRefEncoded 类型,其 token 可以指向  TypeDef, TypeRef 或 TypeSpec 三种不同的元数据表。因此在使用 CorSigUncompressToken 函数对  TypeDefOrRefEncoded 类型进行解码后,需要分别处理三种情况。

对类型定义 (TypeDef) 来说,局部变量使用的类型与方法在同一个 Module 中,因此理论上只要知道名字就可以直接通过 Module.GetType 来获取 Type,例如

None.gif wchar_t typeName[MAX_CLASSNAME_LENGTH];
None.gif
None.gifDWORD dwFlags;
None.gifmdToken tkExtends;
None.gif
None.gifMarshal::ThrowExceptionForHR(pMdImport
-> GetTypeDefProps(
None.gif  token, typeName, MAX_CLASSNAME_LENGTH, NULL, 
& dwFlags,  & tkExtends));  
None.gif
None.gif
return  pModule -> GetType(Marshal::PtrToStringUni(typeName),  true false ); 
None.gif 
None.gif
None.gif

 

但实际上我们还需要对嵌套类型进行处理,例如:

None.gif public   abstract   class  QueuedSemaphore : Semaphore
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
internal abstract class WaitQueue 
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
public class WaitNode 
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif
//dot.gif
ExpandedSubBlockEnd.gif
}

ExpandedSubBlockEnd.gif}

ExpandedBlockEnd.gif}

None.gif

 

对这种情况而言,如果局部变量类型的 token 直接指向 WaitNode,在 GetTypeDefProps 时获得的也只是嵌套类型自身的名字,而仅通过这个名字是无法从 Module 进行类型查询的。因此我们必须通过判断 GetTypeDefProps 方法返回的 dwFlags 是否设置为 tdNestedXXX,来决定是否需要使用 GetNestedClassProps 方法来获取当前类型的 enclosing type (外部类),处理代码如下:

None.gif case  mdtTypeDef:
ExpandedBlockStart.gifContractedBlock.gif  
dot.gif {
InBlock.gif    DWORD dwFlags;
InBlock.gif    mdToken tkExtends;
InBlock.gif
InBlock.gif    Stack 
*enclosing = __gc new Stack();
InBlock.gif    Stack 
*binding = __gc new Stack();
InBlock.gif
InBlock.gif    
while(token)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif      Marshal::ThrowExceptionForHR(pMdImport
->GetTypeDefProps(
InBlock.gif        token, typeName, MAX_CLASSNAME_LENGTH, NULL, 
&dwFlags, &tkExtends));  
InBlock.gif
InBlock.gif      enclosing
->Push(Marshal::PtrToStringUni(typeName));
InBlock.gif
InBlock.gif      
if((dwFlags & tdVisibilityMask) <= tdPublic)
InBlock.gif        
break;
InBlock.gif
InBlock.gif      binding
->Push(__box(((dwFlags & tdVisibilityMask) == tdNestedPublic ? 
InBlock.gif        BindingFlags::Public : BindingFlags::NonPublic) 
| BindingFlags::Static));
InBlock.gif      
InBlock.gif      Marshal::ThrowExceptionForHR(pMdImport
->GetNestedClassProps(token, &token));
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    Type 
*type = pModule->GetType(static_cast<String *>(enclosing->Pop()), truefalse);
InBlock.gif
InBlock.gif    
while(type && enclosing->Count > 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif
InBlock.gif      String 
*enclosingTypeName = static_cast<String *>(enclosing->Pop());
InBlock.gif      BindingFlags bindingFlags 
= (BindingFlags)*dynamic_cast<__box int*>(binding->Pop());
InBlock.gif
InBlock.gif      type 
= type->GetNestedType(enclosingTypeName, bindingFlags);
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
return type;
ExpandedBlockEnd.gif  }

None.gif
None.gif


对类型引用 (TypeRef) 来说则更加麻烦,因为 GetTypeRefProps 返回的仅仅是针对目标 Module/Assembly 的类型名字。这里设计到 Metadata 加载中的一个 Scope 的概念,表示一个类型所加载的有效范围,有点类似于 Java 中的  ClassLoader 的类型域的概念。范围一般是以 Module/Assembly 来界定的,因此对 TypeRef 能够获取的是类型的名称及其所在的范围。
 
 

None.gif case  mdtTypeRef:
ExpandedBlockStart.gifContractedBlock.gif  
dot.gif {
InBlock.gif    mdToken tkResScope;
InBlock.gif
InBlock.gif    Marshal::ThrowExceptionForHR(pMdImport
->GetTypeRefProps(
InBlock.gif      token, 
&tkResScope, typeName, MAX_CLASSNAME_LENGTH, NULL));
InBlock.gif          
InBlock.gif    
// 对类型进行定位
InBlock.gif
    switch(TypeFromToken(tkResScope))
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif     
case mdtModuleRef: // 外部 Module
InBlock.gif
     case mdtAssemblyRef: // 外部 Assembly
ExpandedSubBlockEnd.gif
    }
 
ExpandedBlockEnd.gif}

None.gif
None.gif

ModuleRef 一般用于处理在一个 Assembly 中包含多个 Module 的情况。例如这段代码自身,就可以封装成由三个 Module  组成的 Assembly,分别用 C# 和 Managed C++ 来实现。此时在进行局部变量引用的时候,就会出现跨 Module 的情况。

None.gif switch (TypeFromToken(tkResScope))
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
case mdtModuleRef:
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    wchar_t moduleName[MAX_CLASSNAME_LENGTH];
InBlock.gif
InBlock.gif    Marshal::ThrowExceptionForHR(pMdImport
->GetModuleRefProps(
InBlock.gif      tkResScope, moduleName, MAX_CLASSNAME_LENGTH, NULL));      
InBlock.gif
InBlock.gif    
return pModule->Assembly->
InBlock.gif      GetModule(Marshal::PtrToStringUni(moduleName))
->
InBlock.gif        GetType(Marshal::PtrToStringUni(typeName), 
truefalse);
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif

 

而更多的情况则是跨 Assembly 的类型引用,为此我们需要获取一个新的 IMetaDataAssemblyImport 接口,用于  Assembly 一级的处理。这里 IMetaDataImport/IMetaDataAssemblyImport 等接口我就不详细介绍了,有兴趣的朋友可以直接参考 Unmanaged Metadata API.doc 文档。

通过 IMetaDataAssemblyImport.GetAssemblyRefProps 方法,我们可以获取 AssemblyRef 的一些详细信息,包括 Assembly 的名字、版本、hash key 等等。因为被分析的目标 Assembly 是被 CLR 加载的,因此它所引用的  Assembly 也会被相应加载和定位。我们可以直接通过 GetAssemblyRefProps 获取到的信息,在目标 Assembly 的引用 Assembly 中定位到需要的 Assembly。
 

None.gif case  mdtAssemblyRef:
ExpandedBlockStart.gifContractedBlock.gif  
dot.gif {
InBlock.gif    IMetaDataAssemblyImport 
*pAsmImport;
InBlock.gif
InBlock.gif    wchar_t asmName[MAX_CLASSNAME_LENGTH];
InBlock.gif    ASSEMBLYMETADATA asmMD;
InBlock.gif    
const void *pbPublicKeyOrToken = NULL, *pbHashValue = NULL;
InBlock.gif    DWORD cbPublicKeyOrToken, cbHashValue, dwAssemblyRefFlags;
InBlock.gif
InBlock.gif    Marshal::ThrowExceptionForHR(pMdImport
->QueryInterface(
InBlock.gif      IID_IMetaDataAssemblyImport, (
void **)&pAsmImport));
InBlock.gif
InBlock.gif    HRESULT hr 
= pAsmImport->GetAssemblyRefProps(
InBlock.gif      tkResScope, 
&pbPublicKeyOrToken, &cbPublicKeyOrToken, 
InBlock.gif      asmName, MAX_CLASSNAME_LENGTH, NULL, 
&asmMD,
InBlock.gif      
&pbHashValue, &cbHashValue, &dwAssemblyRefFlags);
InBlock.gif
InBlock.gif    pAsmImport
->Release();
InBlock.gif
InBlock.gif    Marshal::ThrowExceptionForHR(hr);
InBlock.gif
InBlock.gif    AssemblyName 
*refAsm[] = pModule->Assembly->GetReferencedAssemblies();                
InBlock.gif
InBlock.gif    
for(int i=0; i<refAsm->Length; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif      
if(refAsm[i]->Name->Equals(Marshal::PtrToStringUni(asmName)) &&
InBlock.gif         refAsm[i]
->Version->Major == asmMD.usMajorVersion &&
InBlock.gif         refAsm[i]
->Version->Minor == asmMD.usMinorVersion &&
InBlock.gif         refAsm[i]
->Version->Revision == asmMD.usRevisionNumber &&
InBlock.gif         refAsm[i]
->Version->Build == asmMD.usBuildNumber)
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif        
if(pbPublicKeyOrToken && cbPublicKeyOrToken > 0 &&
InBlock.gif           refAsm[i]
->GetPublicKeyToken() != NULL &&
InBlock.gif           refAsm[i]
->GetPublicKeyToken()->Length == cbPublicKeyOrToken)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif          unsigned 
char *key = new unsigned char[cbPublicKeyOrToken];
InBlock.gif
InBlock.gif          Marshal::Copy(refAsm[i]
->GetPublicKeyToken(), 0, key, cbPublicKeyOrToken);
InBlock.gif
InBlock.gif          
if(0 == memcmp(pbPublicKeyOrToken, key, cbPublicKeyOrToken))
ExpandedSubBlockStart.gifContractedSubBlock.gif          
dot.gif{
InBlock.gif            
return Assembly::Load(refAsm[i])->
InBlock.gif              GetType(Marshal::PtrToStringUni(typeName), 
truefalse);
ExpandedSubBlockEnd.gif          }

ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif}

ExpandedSubBlockEnd.gif}

ExpandedBlockEnd.gif}

None.gif}
None.gif
None.gif

而对类型描述 (mdtTypeSpec) 的处理就比较简单了,通过其 Token 找到描述 Signature 并解析之即可。

None.gif case  mdtTypeSpec:
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  PCCOR_SIGNATURE pTypeSig;
InBlock.gif  DWORD cbSig;
InBlock.gif
InBlock.gif  Marshal::ThrowExceptionForHR(pMdImport
->GetSigFromToken(token, &pTypeSig, &cbSig));
InBlock.gif  
InBlock.gif  
return UncompressElement(pMdImport, pModule, pTypeSig);
ExpandedBlockEnd.gif}

None.gif

 

与类型的处理相比,对数组的处理相对容易一些,笔者利用 CLR 提供的 Array.CreateInstance 特性,根据数组的定义信息来动态创建数组实例,并最终获取其类型。这样可以大大降低对数组类型自行定位的繁琐。

None.gif case  ELEMENT_TYPE_SZARRAY:  //  0x1D, Shortcut for single dimension zero lower bound array, SZARRAY <type>
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  SkipCustomMod(pSig);
InBlock.gif
InBlock.gif  
return Array::CreateInstance(UncompressElement(pMdImport, pModule, pSig), 0)->GetType();
ExpandedBlockEnd.gif}

None.gif
case  ELEMENT_TYPE_ARRAY:  //  0x14, MDARRAY <type> <rank> <bcount> <bound1> dot.gif <lbcount> <lb1> dot.gif  
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  Type 
*elementType = UncompressElement(pMdImport, pModule, pSig);
InBlock.gif
InBlock.gif  DWORD dwCount;
InBlock.gif
InBlock.gif  pSig 
+= CorSigUncompressData(pSig, &dwCount); // Rank
InBlock.gif

InBlock.gif  
int lengths __gc[] = new int __gc[dwCount];
InBlock.gif
InBlock.gif  pSig 
+= CorSigUncompressData(pSig, &dwCount); // NumSizes
InBlock.gif

InBlock.gif  DWORD dwValue;
InBlock.gif
InBlock.gif  
for(size_t i=0; i<dwCount; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    pSig 
+= CorSigUncompressData(pSig, &dwValue); // Size
InBlock.gif

InBlock.gif    lengths[i] 
= dwValue;
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  pSig 
+= CorSigUncompressData(pSig, &dwCount); // NumLowBounds
InBlock.gif

InBlock.gif  
int loBounds __gc[] = new int __gc[dwCount];
InBlock.gif
InBlock.gif  
for(size_t i=0; i<dwCount; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    pSig 
+= CorSigUncompressData(pSig, &dwValue); // Size
InBlock.gif

InBlock.gif    loBounds[i] 
= dwValue;
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
return Array::CreateInstance(elementType, lengths, loBounds)->GetType();
ExpandedBlockEnd.gif}

None.gif
None.gif

 

这里 ELEMENT_TYPE_SZARRAY 实际上是对最常见的一维数组的优化,而 ELEMENT_TYPE_ARRAY 则提供较为完整的数组类型定义。可以细到有几维,每个维度的数组长度和起始索引号。
最后的对指针和函数指针的处理较为直接,基本上是前面用过的函数的汇总:

None.gif case  ELEMENT_TYPE_PTR:  //  0xf, PTR <type>   
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  SkipCustomMod(pSig);
InBlock.gif
InBlock.gif  m_isPointer 
= true;
InBlock.gif
InBlock.gif  
return UncompressElement(pMdImport, pModule, pSig);
ExpandedBlockEnd.gif}

None.gif
case  ELEMENT_TYPE_FNPTR:  //  0x1B, FNPTR <complete sig for the function including calling convention>
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
bool hasThis = IMAGE_CEE_CS_CALLCONV_HASTHIS == *pSig;
InBlock.gif
InBlock.gif  
if(hasThis) pSig++;
InBlock.gif
InBlock.gif  
bool explicitThis = IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS == *pSig;
InBlock.gif
InBlock.gif  
if(explicitThis) pSig++;
InBlock.gif
InBlock.gif  DWORD dwCallConv, dwParamCount;
InBlock.gif
InBlock.gif  pSig 
+= CorSigUncompressData(pSig, &dwCallConv);
InBlock.gif  pSig 
+= CorSigUncompressData(pSig, &dwParamCount);
InBlock.gif
InBlock.gif  SkipCustomMod(pSig);
InBlock.gif
InBlock.gif  
bool isByRef = ELEMENT_TYPE_BYREF == *pSig;
InBlock.gif
InBlock.gif  
if(isByRef) pSig++;
InBlock.gif
InBlock.gif  Type 
*retType = UncompressElement(pMdImport, pModule, pSig);
InBlock.gif
InBlock.gif  
for(size_t i=0; i<dwParamCount; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    SkipCustomMod(pSig);
InBlock.gif
InBlock.gif    
if(isByRef) pSig++;
InBlock.gif
InBlock.gif    Type 
*paramType = UncompressElement(pMdImport, pModule, pSig);
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif 
None.gif
None.gif

 

至此对方法体结构的解析工作就基本完成了,针对特定的情况可能需要做一些兼容性的修改。
而对其的使用也非常简单

None.gif using (MetadataHelper helper  =   new  MetadataHelper(_method.DeclaringType.Assembly.Location,  true ))
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  MethodBody body 
= helper.GetMethodBody(_method); 
InBlock.gif
InBlock.gif  
foreach(LocalVariableInfo var in body.LocalVariables)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    stats.Add(
new CodeVariableDeclarationStatement(var.LocalType, "var" + var.LocalIndex)); 
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}
           
None.gif
None.gif

 

因为时间和精力有限,很多问题我就不展开讲了,只是通过贴代码的方式把思路提出来。如果有什么疑惑或者改进的思路,欢迎讨论。

出于完整性考虑,后面把 Unmanaged Metadata API 的环境构建代码大致贴一下:

ExpandedBlockStart.gif ContractedBlock.gif /**/ /
None.gif //  MetadataHelper.h
None.gif
//
None.gif
#pragma once
None.gif
None.gif#include 
< cor.h >
None.gif#include 
< CorHdr.h >
None.gif
None.gif#
using   < mscorlib.dll >
None.gif
None.gif#include 
" MethodHelper.h "
None.gif
None.gif
using   namespace  System;
None.gif
using   namespace  System::Reflection;
None.gif
using   namespace  System::Runtime::InteropServices;
None.gif
None.gif
using   namespace  NSFocus::ILEngine::Model;
None.gif
None.gif
namespace  NSFocus
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
namespace ILEngine
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{          
InBlock.gif    
namespace Helper
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif      __gc 
public class MetadataHelper : public IDisposable
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif      
private:      
InBlock.gif        IntPtr m_pDispenser, m_pMDImport;
InBlock.gif
InBlock.gif        
static IntPtr CreateDispenser();
InBlock.gif      
public:
InBlock.gif        MetadataHelper(String 
*filename, bool readonly);
InBlock.gif        MetadataHelper(IntPtr pData, size_t cbData, 
bool readonly);
InBlock.gif        
InBlock.gif        
virtual void Dispose();
InBlock.gif
InBlock.gif        __property IMetaDataDispenserEx 
*get_MetaDataDispenser()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif          
return static_cast<IMetaDataDispenserEx *>(m_pDispenser.ToPointer());
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        __property IMetaDataImport 
*get_MetaDataImport()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif          
return static_cast<IMetaDataImport *>(m_pMDImport.ToPointer());
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        MethodBody 
*GetMethodBody(MethodBase *method)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif          
return (__gc new MethodHelper(MetaDataImport, method))->GetMethodBody();
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif      }
;
ExpandedSubBlockEnd.gif    }

ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif 
None.gif 
None.gif
None.gif
None.gif以下内容为程序代码:
None.gif
ExpandedBlockStart.gifContractedBlock.gif
/**/ ///
None.gif //  MetadataHelper.cpp
None.gif
//
None.gif
#include  " StdAfx.h "
None.gif
None.gif#include 
" MetadataHelper.h "
None.gif
None.gif
namespace  NSFocus
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
namespace ILEngine
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{   
InBlock.gif    
namespace Helper
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif      MetadataHelper::MetadataHelper(String 
*filename, bool readonly)
InBlock.gif        : m_pDispenser(CreateDispenser())
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif        IntPtr pFilename 
= Marshal::StringToHGlobalUni(filename);
InBlock.gif
InBlock.gif        IMetaDataImport 
*pImport;
InBlock.gif
InBlock.gif        HRESULT hr 
= MetaDataDispenser->OpenScope((LPCWSTR)pFilename.ToPointer(), 
InBlock.gif          
readonly ? 0 : 1, IID_IMetaDataImport, (IUnknown **)&pImport);
InBlock.gif
InBlock.gif        m_pMDImport 
= pImport;
InBlock.gif
InBlock.gif        Marshal::FreeHGlobal(pFilename);
InBlock.gif
InBlock.gif        Marshal::ThrowExceptionForHR(hr);
ExpandedSubBlockEnd.gif      }

InBlock.gif
InBlock.gif      MetadataHelper::MetadataHelper(IntPtr pData, size_t cbData, 
bool readonly
InBlock.gif        : m_pDispenser(CreateDispenser())
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{        
InBlock.gif        IMetaDataImport 
*pImport;
InBlock.gif
InBlock.gif        HRESULT hr 
= MetaDataDispenser->OpenScopeOnMemory(pData.ToPointer(), cbData,
InBlock.gif          
readonly ? 0 : 1, IID_IMetaDataImport, (IUnknown **)&pImport);
InBlock.gif
InBlock.gif        m_pMDImport 
= pImport;
InBlock.gif
InBlock.gif        Marshal::ThrowExceptionForHR(hr);
ExpandedSubBlockEnd.gif      }

InBlock.gif
InBlock.gif      IntPtr MetadataHelper::CreateDispenser()
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif        IMetaDataDispenserEx 
*pDispenser;
InBlock.gif
InBlock.gif        Marshal::ThrowExceptionForHR(::CoCreateInstance(CLSID_CorMetaDataDispenserRuntime, NULL, CLSCTX_INPROC_SERVER, 
InBlock.gif          IID_IMetaDataDispenserEx, (LPVOID 
*)&pDispenser));    
InBlock.gif
InBlock.gif        
return pDispenser;
ExpandedSubBlockEnd.gif      }

InBlock.gif
InBlock.gif      
void MetadataHelper:[img]/images/biggrin.gif[/img]ispose()
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif        
if(MetaDataDispenser)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif          MetaDataDispenser
->Release();
InBlock.gif          m_pDispenser 
= IntPtr::Zero;
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
if(MetaDataImport)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif          MetaDataImport
->Release();
InBlock.gif          m_pMDImport 
= IntPtr::Zero;
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif      }

ExpandedSubBlockEnd.gif    }

ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif
None.gif


 

转载于:https://www.cnblogs.com/spdevelop/archive/2005/10/27/263423.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值