.Net CLR Hosting原理及实践
在开发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
posted on 2008-05-17 14:07 lbq1221119 阅读(4397) 评论(17) 编辑 收藏 举报