分享

.Net CLR Hosting原理及实践

 quasiceo 2012-12-18

.Net CLR Hosting原理及实践

分类: .net(C#/CLI/DLR) 286人阅读 评论(0) 收藏 举报

【篇首语】最近由于.net4升级运行时库的原因,逼得我要去寻求混合编程方法。偶遇这个就转帖过来。通常C++调用.net的dll,都是通过COM的方式,其实也可以采用CLR API,就是本文讲到的内容。这种方式的好处就是效率高,总比COM包装来报装去好一些。

多说一点,1、这个API不能加载4.0以上的代码,因为运行时库更新了。可以参看这个 MSDN的说明文档http://msdn.microsoft.com/en-us/library/dd380851%28v=VS.100%29.aspx。换句话说,本文是#4之下的调用方法,#4之上雷同,但是API要换。

2、本文例子的头文件和lib都在C:/Program Files/Microsoft SDKs/Windows/v7.0A/下面,该例子用到的lib是mscoree.lib,头文件是mscoree.h。

3、该方法似乎只能调用静态函数!

 

在开发CLR的时候,MS实际上是将CLR相关的功能作为一个COM服务实现在一个DLL里面。对这个DLL的选择,是由垫片来选择的。

MS为CLR定义了一个标准的COM接口,并且为该接口和COM服务指定了GUID。

虽然没有垫片mscoree.dll的实现代码,但是咱可以看看头文件….

在MSCorEE头文件里面,定义了一些GUID和非托管的最重要的ICorRuntimeHost接口。

任何windows应用程序,都可以编程实现hosting CLR,可以调用MSCorEE里面的CorBindToRuntime方法来实现寄宿托管程序。在寄宿托管程序的时候,process里面众多的thread只有两种可以执行托管代码:

HRESULT CorBindToRuntimeEx (

LPWSTR pwszVersion,

LPWSTR pwszBuildFlavor,

DWORD flags,

REFCLSID rclsid,

REFIID riid,

LPVOID* ppv

);

调用上面的这个函数的时候,这个函数允许指定期望加载的CLR的版本(pwszVersion,Null表示希望加载最新版本的CLR),加载服务器版本的ee还是工作站版本的ee,控制执行是并发垃圾回收还是非并发的垃圾回收,控制程序集是否以非特定与域的方式进行加载。

最后一个参数的含义,是获取一个接口的指针,该指针可以指向其它设置选项的ICorRuntimeHost。这些选项,允许在宿主启动之前对他们进行配置。

ICorRuntimeHost是hosting APIs里面的一个初始化COM interface。之所以说是初始化的COM interface,是因为这个接口,是在hosting CLR的时候需要用到的第一个接口。

在MSCorEE.h中,找到了ICorRuntimeHost的定义和主要功能:

MIDL_INTERFACE("90F1A06C-7712-4762-86B5-7A5EBA6BDB02")

ICLRRuntimeHost : public IUnknown

{

public
//Start and Stop the CLR run in a Process

virtual HRESULT STDMETHODCALLTYPE Start( void) = 0;

virtual HRESULT STDMETHODCALLTYPE Stop( void) = 0;

virtual HRESULT STDMETHODCALLTYPE SetHostControl(

/* [in] */ IHostControl *pHostControl) = 0;

//CLR Host用来获取CLR实现了哪些寄宿的应用程序接口。

virtual HRESULT STDMETHODCALLTYPE GetCLRControl(

/* [out] */ ICLRControl **pCLRControl) = 0;

//从一个Process里面卸载一个AppDomain

virtual HRESULT STDMETHODCALLTYPE UnloadAppDomain(

/* [in] */ DWORD dwAppDomainId,

/* [in] */ BOOL fWaitUntilDone) = 0;

//在一个特定的应用程序域里面执行一个回调函数。

virtual HRESULT STDMETHODCALLTYPE ExecuteInAppDomain(

/* [in] */ DWORD dwAppDomainId,

/* [in] */ FExecuteInAppDomainCallback pCallback,

/* [in] */ void *cookie) = 0;

//返回给当前Calling Thread某个特定的应用程序域的独一无二的Domain Id

virtual HRESULT STDMETHODCALLTYPE GetCurrentAppDomainId(

/* [out] */ DWORD *pdwAppDomainId) = 0;

//执行一个由外部的Manifest定义的一个标准的2.0里面的Applicaiton

virtual HRESULT STDMETHODCALLTYPE ExecuteApplication(

/* [in] */ LPCWSTR pwzAppFullName,

/* [in] */ DWORD dwManifestPaths,

/* [in] */ LPCWSTR *ppwzManifestPaths,

/* [in] */ DWORD dwActivationData,

/* [in] */ LPCWSTR *ppwzActivationData,

/* [out] */ int *pReturnValue) = 0;

//在默认的应用程序域里面执行一个特定的方法。这个方法,对于只有一个AppDomian的CLR Host来说比较的方便。

virtual HRESULT STDMETHODCALLTYPE ExecuteInDefaultAppDomain(

/* [in] */ LPCWSTR pwzAssemblyPath,

/* [in] */ LPCWSTR pwzTypeName,

/* [in] */ LPCWSTR pwzMethodName,

/* [in] */ LPCWSTR pwzArgument,

/* [out] */ DWORD *pReturnValue) = 0;

};

这里,返回了这个接口之后,如何使用CLR提供的一系列功能呢?这里就涉及到一个非常重要的概念:Hosting Manager。

关于这方面的资料,说实话见到的不多,大多在一些msdn blogs的隐蔽的角落里面。中文方面,比较好的有一篇台湾的蔡學鏞的大內高手專欄中的.NET CLR Hosting 簡介,还有就是filer和MS的zhangyi在blog上面也介绍过。

Hosting Manager是干嘛的呢?简单的来说,就是把一系列的CLR提供的功能组织到一起。把他们组织成为一个逻辑功能的逻辑组合。

在所有的CLR Hosting APIs里面提供的功能,主要包括:CLR的启动和关闭,App Domain相关,自定义错误处理,编程模型的执行,对调试器的支持,Assembly的Load相关,CLR的内部事件,CLR Engine相关,内存管理和垃圾回收,Threading,同步,I/O的支持等。

这里,copy一个提供了CLR的各个功能的接口层次结构图:

和这个接口的层次图对应的,是功能的层次结构图:

这里,才回头到最上面ICLRRuntimeHost接口的定义里面的:

virtual HRESULT STDMETHODCALLTYPE SetHostControl

virtual HRESULT STDMETHODCALLTYPE GetCLRControl

这里的两个部分,就是把控制权交给了CLR,但是根据不同的功能到底是哪一部分实现的,是CLR实现的,还是Host实现的进行了选择。而在有的地方说的,在托管程序加载了CLR之后,就将控制权交给了CLR,指的起始就是SetCLRControl。

举一个简单的例子,就说IHostContrl里面的Assembly Loading这个功能的实现。这个功能的实现,是由宿主来实现的。其功能,都可以通过调用IHostAssemblyManager这个基本接口来实现:

MIDL_INTERFACE("613dabd7-62b2-493e-9e65-c1e32a1e0c5e")

IHostAssemblyManager : public IUnknown

{

public:

virtual HRESULT STDMETHODCALLTYPE GetNonHostStoreAssemblies(

/* [out] */ ICLRAssemblyReferenceList **ppReferenceList) = 0;

virtual HRESULT STDMETHODCALLTYPE GetAssemblyStore(

/* [out] */ IHostAssemblyStore **ppAssemblyStore) = 0;

};

另外有一点需要特别指明一下,如果某一个特定的功能,是由CLR来实现的,调用这个功能的相应的接口就用ICLR来开头,如果这个功能是Host实现的,就调用IHost开头的接口定义的函数。

我们通常说sql server提供了对DotNet的支持,其实就是它实现了这些功能接口的功能,可以直接在CLR中调用相关的功能。

IHostAssemblyManager实现了了对Assembly Load功能,同时还有三个另外的接口也实现了Assembly Load相关的功能:IHostAssemblyStore ,ICLRAssemblyReferenceList,

ICLRAssemblyIdentityManager。他们都提供了不同的功能,分别有CLR和Host来实现。

恩,介绍到这个地方,基本上CLR Hosting的原理和它的一套方法,都说清楚了。接下来,看看一个如何调用CLR功能的一个例子。

下面展示一下如何采用一个非托管的宿主来加载CLR并且执行里面的一些代码。

首先,在非托管宿主里面加载CLR并且启动:

ICLRRuntimeHost *pCLRHost = NULL;
HRESULT hr = CorBindToRuntimeEx(

L"v2.0.40103", //需要加载的CLR版本,Null表示最新的

L"wks", //GC的风格,Null表示默认的工作站模式

STARTUP_CONCURRENT_GC,

CLSID_CLRRuntimeHost, //CLR的CLSID

IID_ICLRRuntimeHost, //ICLRRuntimeHost的IID

(PVOID*) &pCLRHost); //返回的COM接口

初始化并且启动CLR:

pCLRHost->Start();

然后执行一段托管代码:

hr = pCLRHost ->ExecuteInDefaultAppDomain(L"test.exe",

L" test.Program",

L"Start",

NULL,

&retVal);

这里,是实现的硬编码,需要再相同的目录下面有一个test.ext和相关的方法和参数。

从上面可以看到,其实我们如果需要Customzing CLR,调用一些CLR并不展示出来的功能,可以寻求上面的这个思路。

5/17/2008 1:41:30 PM 首发sscli.cnblogs.com

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多