CLR寄宿(上) MSCOREE.DLL

托管代码调用非托管代码,.NET提供了P/Invoke(平台调用)方式,它作为.NET的基础出现在各类书籍和网络资源上,这里不再讨论。那么非托管代码如何去调用托管代码呢?我们知道,一个托管应用程序首先被操作系统启动,然后由操作系统调用CLR来托管该程序。那么.NET框架到底以什么方式让操作系统来认识它并且可以启动它呢?微软实际将其作为COM服务器实现在一个DLL中,并提供了标准的COM接口。既然是COM服务,也就意味着普通的非托管程序也可以调用CLR来运行托管代码,把这种调用方式叫做寄宿,把调用CLR的非托管程序叫做宿主。宿主程序不仅可以调用CLR,还可以通过它来进行内存管理、垃圾回收管理、策略管理、事件管理以及线程控制等高级管理。

1.1  核心组件MSCOREE.DLL

MSCOREE.DLL负责选择.NET版本、调用和初始化CLR等工作。非托管程序想要启动CLR也必须引用MSCOREE.DLL,利用它的导出函数加载托管代码和进行定制CLR等操作。既然现在的焦点集中在MSCOREE.DLL,下面一同来看它的庐山真面目。

查看MSCOREE.DLL的头文件

对于MSCOREE.DLL文件,我们没有办法查看它的源代码,但是FrameWork SDK提供了mscoree.h文件,可以到相关版本的SDK安装目录中查看该文件。由于该文件很大,这里就不贴出源代码进行分析了。接下来会对相关的接口和函数做介绍。

MSCOREE.DLL的导出函数

在命令行启动DumpBin.exe,执行以下代码查看MSCOREE.DLL的导出函数列表:

C:\Windows\System32>dumpbin /exports MSCOREE.DLL

结果如图1-3所示。

图1-3  输出MSCOREE.DLL的导出函数列表

从运行的结果可以知道,MSCOREE.DLL中的导出函数有100多个,所有这些函数都可以被公开调用,而且在MSDN上可以找到每个函数的解释。这里只介绍其中几个常用的函数。

CorBindToRuntimeEx函数

该函数由宿主调用来加载CLR,该函数的定义如代码清单1-4所示。

代码清单1-4 CorBindToRuntimeEx函数定义

HRESULT CorBindToRuntimeEx (

[in]  LPWSTR    pwszVersion,

[in]  LPWSTR    pwszBuildFlavor,

[in]  DWORD     startupFlags,

[in]  REFCLSID  rclsid,

[in]  REFIID    riid,

[out] LPVOID*   ppv

);

代码中的CorBindToRuntimeEx函数需要6个参数,每个参数的定义如下:

pwszVersion。一个字符串,描述要加载的 CLR 的版本。

pwszBuildFlavor。一个字符串,指定是加载 CLR 的服务器版本还是工作站版本。有效值为:svr 和 wks。服务器版本已经过优化,可以利用多个处理器进行垃圾回收,而工作站版本则针对单处理器计算机上运行的客户端应用程序进行了优化。

startupFlags。STARTUP_FLAGS 枚举值的组合。这些标志控制并发垃圾回收、非特定于域的代码以及 pwszVersion 参数的行为。如果未设置标志,则默认值为一个域。

rclsid。实现 ICorRuntimeHost 接口的 coclass 的 CLSID。支持的值为 CLSID_CorRuntimeHost或 CLSID_CLRRuntimeHost。

riid。请求自 rclsid 接口的 IID。支持的值为 IID_ICorRuntimeHost 或 IID_ICLRRuntimeHost。

ppv。返回的指向 riid 的接口指针。

提示使用CorBindToCurrentRuntime函数并使用存储在 XML文件中的版本信息可以将公共语言运行库 (CLR) 加载到进程中,还可以使用CorBindToRuntimeByCfg 函数从 XML文件中读取的版本信息将公共语言运行库 (CLR) 加载到进程中。

CorExitProcess函数

该函数用来关闭当前的非托管进程,定义如代码清单1-5所示。

代码清单1-56  CorExitProcess 函数定义

void STDMETHODCALLTYPE CorExitProcess (

[out] int  exitCode

);

其中,参数exitCode表示一个指定进程退出代码的整数。

ClrCreateManagedInstance 函数

该函数用来创建指定托管类型的实例,定义如代码清单1-6所示。

代码清单1-6  ClrCreateManagedInstance 函数定义

STDAPI ClrCreateManagedInstance (

[in]  LPCWSTR  pTypeName,

[in]  REFIID   riid,

[out] void     **ppObject

);

代码中函数ClrCreateManagedInstance的各参数定义如下:

pTypeName。一个指向所请求的实例类型名称的指针。

riid。所请求实例类型的 IID。

**ppObject。一个指向指针的指针,它指向的指针指向调用方请求的托管类型实例。

CallFunctionShim函数

该函数用来调用指定库中具有指定名称和参数的函数。定义如代码清单1-7所示。

代码清单1-7  CallFunctionShim 函数定义

HRESULT CallFunctionShim (

[in] LPCWSTR     szDllName,

[in] LPCSTR      szFunctionName,

[in] LPVOID      lpvArgument1,

[in] LPVOID      lpvArgument2,

[in] LPCWSTR     szVersion,

[in] LPVOID      pvReserved

);

代码中函数CallFunctionShim的各参数定义如下:

szDllName。包含函数的库的名称。

szFunctionName。函数的名称。

lpvArgument1。要传递到函数的第一个参数。

lpvArgument2。要传递到函数的第二个参数。

szVersion。包含函数的库的版本。

pvReserved。留作未来使用。在此参数中传递零。

更多的导出函数读者可自行操作获得它们的名称,然后查阅MSDN来进一步了解。

MSCOREE.DLL宿主接口

宿主接口使宿主能够对运行库的更多方面进行控制,从而能够在 CLR 和宿主的执行模型之间进行更紧密的集成。在.NET Framework 1 版中,宿主模型使非托管宿主能够将 CLR 加载到进程中、配置某些设置以及接收事件通知。但在通常情况下,宿主和 CLR 可以在该进程中独立运行。通过 .NET Framework 2.0 版及更高版本中新的抽象层,宿主可以提供当前由 Win32 程序集中的类型提供的多种资源,并扩展了宿主可以配置的功能集。

MSCOREE.DLL同样提供了多个宿主接口,这里只介绍较为重要的几个,更多的内容请读者参考MSDN。

ICorRuntimeHost 接口

该接口是调用CLR要用到的第一个接口,它负责初始化工作。该接口由上面提到的CorBindToRuntimeEx函数返回,其定义如代码清单1-8所示。

代码清单1-8  ICorRuntimeHost接口定义

interface ICLRRuntimeHost : IUnknown {

HRESULT ExecuteApplication (

[in] LPCWSTR                   pwzAppFullName,

[in] DWORD                     dwManifestPaths,

[in] LPCWSTR                   *ppwzManifestPaths,

[in] DWORD                     dwActivationData,

[in] LPCWSTR                   *ppwzActivationData,

[out] int                      *pReturnValue

);

HRESULT ExecuteInAppDomain (

[in] DWORD                     appDomainId,

[in] FExecuteInDomainCallback  pCallback,

[in] void*                     cookie

);

HRESULT ExecuteInDefaultAppDomain (

[in] LPCWSTR                   pwzAssemblyPath,

[in] LPCWSTR                   pwzTypeName,

[in] LPCWSTR                   pwzMethodName,

[in] LPCWSTR                   pwzArgument,

[out] DWORD                    *pReturnValue

);

HRESULT GetCLRControl (

[out] ICLRControl              **pCLRControl

);

HRESULT GetCurrentAppDomainId (

[out] DWORD                    *pdwAppDomainId

);

HRESULT SetHostControl (

[in] IHostControl              *pHostControl

);

HRESULT Start();

HRESULT Stop();

HRESULT UnloadAppDomain (

[in] DWORD                     dwAppDomainId

[in] BOOL                      fWaitUntilDone

);

};

对代码清单1-8中的各方法说明如下:

q  ICLRRuntimeHost::ExecuteApplication方法

一个字符串,描述要加载基于清单的 ClickOnce 部署方案中用于指定要在新域中激活的应用程序。

q  ICLRRuntimeHost::ExecuteInAppDomain 方法

指定要在其中执行指定托管代码的 AppDomain。

q  ICLRRuntimeHost::ExecuteInDefaultAppDomain 方法

调用指定程序集中属于指定类型的指定方法。

q  ICLRRuntimeHost::GetCLRControl 方法

获取一个ICLRControl类型的接口指针,宿主可以使用该类型自定义公共语言运行库 (CLR) 的各个方面。

q  ICLRRuntimeHost::GetCurrentAppDomainId 方法

获取当前正在执行的AppDomain的数字标识符。

q  ICLRRuntimeHost::SetHostControl 方法

设置主机控制接口。在调用 Start 之前必须调用 SetHostControl。

q  ICLRRuntimeHost::Start 方法

将 CLR 初始化到进程中。

q  ICLRRuntimeHost::Stop 方法

使运行库停止代码的执行。

q  ICLRRuntimeHost::UnloadAppDomain 方法

卸载与指定的数字标识符对应的 AppDomain。

ICLRGCManager接口

该接口提供允许宿主与公共语言运行库的垃圾回收系统进行交互的方法。该接口的定义如代码清单1-9所示。

代码清单1-9  ICLRGCManager接口定义

interface ICLRGCManager : IUnknown {

HRESULT Collect (

[in] LONG Generation

);

HRESULT GetStats (

[in, out] COR_GC_STATS *pStats

);

HRESULT SetGCStartupLimits (

[in] DWORD SegmentSize,

[in] DWORD MaxGen0Size

);

};

对代码清单1-10中ICLRGCManager接口的各方法说明如下:

q  ICLRGCManager::Collect方法

为指定的生成强制执行垃圾回收。

q  ICLRGCManager::GetStats方法

获取有关垃圾回收系统的一组当前统计信息。

q  ICLRGCManager::SetGCStartupLimits方法

设置垃圾回收段的大小和垃圾回收系统零代的最大大小。

IHostControl接口

该接口提供一些方法,以配置程序集的加载和确定宿主支持的宿主接口。该接口的定义如代码清单1-10所示。

代码清单1-10 IHostControl接口定义

interface IHostControl : IUnknown {

HRESULT GetHostManager (

[in] REFIID riid,

[out, iid_is(riid)] IUnknown** ppObject

);

HRESULT SetAppDomainManager (

[in] DWORD dwAppDomainID,

[in] IUnknown* pUnkAppDomainManager

);

};

对代码清单1-10中IHostControl接口中的方法说明如下:

q  IHostControl::GetHostManager 方法

获取一个接口指针,该指针指向宿主对具有指定 IID 接口的实现。

q  IHostControl::SetAppDomainManager 方法

通知宿主已创建了一个应用程序域。

其他接口

其他接口这里不详细介绍,读者可根据需要按照下面的简介自行查阅相关的文档。

IActionOnCLREvent提供为已注册的事件执行回调的方法。

IApartmentCallback提供用于在单元内进行回调的方法。

IAppDomainBinding提供用于设置运行时配置的方法。

ICatalogServices提供用于编录服务的方法。(此接口支持.NET Framework 基础结构,但不应在代码中直接使用。)

ICLRAssemblyIdentityManager提供支持宿主和 CLR 之间就程序集问题进行通信的方法。

ICLRAssemblyReferenceList管理由CLR(而非宿主)加载的程序集的列表。

ICLRControl提供一些方法,以便宿主可以获取对CLR的访问权限并对CLR的各个方面进行配置。

ICLRDebugManager提供使宿主能够将一组任务与某个标识符及友好名称关联起来的方法。

ICLRErrorReportingManager提供使宿主能够为错误报告配置自定义堆转储的方法。

ICLRHostBindingPolicyManager提供允许宿主计算并传达程序集策略信息中的更改的方法。

ICLRHostProtectionManager使宿主能够阻止特定的托管类、方法、属性和字段在部分受信任的代码中运行。

ICLRIoCompletionManager实现使宿主能够向 CLR 通知指定 I/O 请求的状态的回调方法。

ICLRMemoryNotificationCallback使宿主能够使用与 Win32 CreateMemoryResourceNotification 函数方法类似的方法报告内存压力情况。

ICLROnEventManager提供使宿主能够为 CLR 事件注册和注销回调的方法。

ICLRPolicyManager提供使宿主能够指定在出现故障和超时的情况下采取的策略操作的方法。

ICLRProbingAssemblyEnum提供方法,这些方法使宿主能够使用 CLR 内部的程序集标识信息来获取该程序集的探测标识,而无需创建或了解该标识。

ICLRReferenceAssemblyEnum提供方法,这些方法使宿主能够对文件或流通过 CLR 内部的程序集标识数据引用的一组程序集进行操作,而无需创建或了解这些标识。

ICLRSyncManager提供方法,以便让宿主在其同步实现中获取有关请求任务的信息并进行死锁检测。

ICLRTask提供方法,这些方法使宿主能够向 CLR 发出请求,或者向 CLR 提供与关联的任务有关的通知。

ICLRTaskManager提供方法,这些方法使宿主能够显式请求 CLR 创建一个新任务,获取当前正在执行的任务,以及设置该任务的地理语言和区域性。

ICLRValidator提供用于验证可移植可执行 (PE) 映像和报告验证错误的方法。

ICorConfiguration提供用于配置 CLR 的方法。

ICorThreadpool提供用于访问线程池的方法。

IDebuggerInfo提供用于获取调试服务状态信息的方法。

IDebuggerThreadControl提供方法,用于向宿主发出有关通过调试服务阻止和取消阻止线程的通知。

IGCHostControl提供使垃圾回收器能够请求宿主更改虚拟内存限制的方法。

IGCThreadControl提供用于参与线程调度的方法,以防止因阻塞而执行垃圾回收。

IHostAssemblyManager提供方法,这些方法使宿主能够指定应由 CLR 或宿主加载的多组程序集。

IHostAssemblyStore提供方法,这些方法使宿主能够独立于 CLR 加载程序集和模块。

IHostAutoEvent提供由宿主实现的自动重置事件的表示形式。

IHostCrst用做线程临界区的宿主表示形式。

IHostIoCompletionManager提供方法,这些方法使 CLR 能够与宿主提供的 I/O 完成端口进行交互。

IHostMAlloc提供一些方法,以便 CLR 可以请求从堆到宿主的细化分配。

IHostManualEvent提供宿主的手动重置事件的表示形式的实现。

IHostMemoryManager提供方法,以便 CLR 可以通过宿主而不是使用标准 Win32 虚拟内存函数来请求虚拟内存。

IHostPolicyManager提供一些方法,以便通知宿主 CLR 在中止、超时或失败时所执行的操作。

IHostSecurityContext使 CLR 能够维护由宿主实现的安全性上下文信息。

IHostSecurityManager提供允许访问和控制当前正在执行的线程的安全性上下文的方法。

IHostSemaphore提供由宿主实现的信号量的表示形式。

IHostSyncManager提供方法,以便 CLR 可以通过调用宿主而不是使用 Win32 同步函数来创建同步基元。

IHostTask提供使 CLR 能够与宿主通信以管理任务的方法。

IHostTaskManager提供方法,这些方法使 CLR 能够通过宿主而不是使用标准操作系统线程或线程函数来处理任务。

IHostThreadPoolManager提供一些方法,以便 CLR 可以配置线程池并对线程池中的工作项进行排队。

IManagedObject提供用于控制托管对象的方法。

IObjectHandle提供用于通过间接寻址打开按值封送对象的方法。

ITypeName提供用于获取类型名称信息的方法。(此接口支持 .NET Framework 基础结构,但不应在代码中直接使用。)

ITypeNameBuilder提供用于生成类型名称的方法。(此接口支持 .NET Framework 基础结构,但不应在代码中直接使用。)

ITypeNameFactory提供用于解构类型名称的方法。(此接口支持 .NET Framework 基础结构,但不应在代码中直接使用。)

IValidator提供用于验证可移植可执行 (PE) 映像和报告验证错误的方法。

1.2  MSCOREE.DLL接口层次模型

所有的CLR Hosting API提供的主要功能包括:CLR的启动和关闭、App Domain相关、自定义错误处理、编程模型的执行、对调试器的支持、Assembly的Load相关、CLR的内部事件、CLR Engine相关、内存管理和垃圾回收、Threading、同步和I/O的支持等。图1-4展示了MSCOREE.DLL中接口的层次模型。

图1-4 MSCOREE.DLL中的接口层次模型

和图1-4对应的是MSCOREE.DLL各个接口在应用过程中的功能模型,如图1-5所示。

图1-5  MSCOREE.DLL各个接口在应用过程中的功能模型

现在看代码清单1-8中ICLRRuntimeHost接口的定义里面的

HRESULT STDMETHODCALLTYPE SetHostControl方法和HRESULT STDMETHODCALLTYPE GetCLRControl方法。这两个方法根据功能实现者(CLR或Host)的不同对控制权进行了选择。将控制权交给CLR的起始是SetCLRControl方法。

注意  如果某一个特定的功能是由CLR来实现的,调用这个功能的相应的接口就用ICLR来开头,如果这个功能是Host实现的,就调用IHost开头的接口定义的函数。

1.3 加载CLR与执行代码实例

下面展示一下如何采用一个非托管的宿主来加载CLR并且执行里面的代码。请看代码清单1-11。

代码清单1-11  加载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);

代码清单1-11只是一个最简单的示例,在实际应用中可以实施更多的配置,由于篇幅所限,本书就不更多地去讲解相关内容了,可以明确的是,一切操作都不会脱离上述接口。

-----------------------注:本文部分内容改编自《.NET安全揭秘》

CLR寄宿(上) MSCOREE.DLL相关推荐

  1. Microsoft.NET框架程序设计--20 CLR寄宿、应用程序域、反射

    应用程序域是CLR提供的一种旨在减少内存使用.提高系统系能的新型机制.而反射使得我们可以很容易使用自己活着第三方的类型来增强应用程序的功能,从而帮助我们设计出可动态扩展的应用程序. 1.元数据:.NE ...

  2. Win11系统错误powershell找不到mscoree.dll的三种解决方法

    近期有用户在更新微软Win11最新补丁KB5013943后,出现了严重 Bug,其中包含了powershell.exe - 系统错误,并提示找不到mscoree.dll,这该怎么解决呢?下面小编就为大 ...

  3. C#实现从服务器上下载DLL文件

    C#实现从服务器上下载DLL文件,具体代码如下:   var fullQualifiedPathToDll = Server.MapPath("/") + "/bin/m ...

  4. 由于找不到mscoree.dll无法继续执行代码

    Windows终端可以说是Windows系统中非常重要的功能之一,可以帮助用户通过命令行的模式来调整电脑,但是最近不少用户在运行的时候却提示由于找不到mscoree.dll无法继续执行代码,下面小编就 ...

  5. 【.NET 基础】——CLR、BCL、DLL、Assembly

    .NET FrameWork是用于Windows的新托管代码编程模型,它包含CLR(Common Language Runtime)以及BCL(Base CLass Library)构成. 一.CLR ...

  6. Zoom 5.1.2及旧版本在 Win7 上的 DLL 劫持漏洞分析

     聚焦源代码安全,网罗国内外最新资讯! 一 漏洞原因 Zoom 5.1.2及之前版本尝试加载了一个名为 shcore.dll 的系统库文件来辅助设置显示 Dpi,但是这个库文件在 Win7 Syste ...

  7. 关于如何在Windows上生成dll库

    加上了自己的理解,和下面两位大神的作品,我也不知道自己是原创还是转载.仅是将自己的新的写出来,大家一起学习 鼓捣了一天半了,终于算是整明白了. 下面一一介绍流程,虽然网上已经有了好多教程,但是我这一版 ...

  8. 关于在Windows10上删除.dll文件的方法

    DLL文件是库文件,是我们软件运行时的必备文件,但是在有的时候我们删除了安装的软件,相对应的软件的DLL文件有可能没有删除,这里就分享一下删除DLL文件的方法. 1.按win+r键,会出现如下的界面: ...

  9. 教你打入clr内部: 配置windows上的windbg,linux上的lldb

    一:背景 1. 讲故事 前几天公众号里有位兄弟看了几篇文章之后,也准备用windbg试试看,结果这一配就花了好几天,(づ╥﹏╥)づ,我想也有很多跃跃欲试的朋友在配置的时候肯定会遇到这样和那样的问题,所 ...

  10. 教你配置windows上的windbg,linux上的lldb,打入clr内部这一篇就够了

    一:背景 1. 讲故事 前几天公众号里有位兄弟看了几篇文章之后,也准备用windbg试试看,结果这一配就花了好几天,(づ╥﹏╥)づ,我想也有很多跃跃欲试的朋友在配置的时候肯定会遇到这样和那样的问题,所 ...

最新文章

  1. 史上最全阿里云服务器上Docker部署Springboot项目 实战 每一步都带详细图解!!!
  2. CDN加速实现—varnish
  3. 2019 蓝桥杯省赛 B 组模拟赛(一) J. 程序设计:蒜厂年会 环形连续子序列求和问题
  4. Python 3.10 中的 6 个新特性,你体验了吗?
  5. 数据预处理:中英文印刷字体图片分类数据集生成
  6. 大数据架构及技术体系
  7. 个人打卡签到表html代码,考勤每日签到表模板
  8. 利用python随机生成姓名
  9. 解决学校邮箱屏蔽邮件的问题/收不到邮件
  10. 如何在word编辑文本框,教程来啦,怎样在word编辑文本框
  11. 可以使用ActualHeight来判断textblock是否已经trimming
  12. py征途3之填坑(pagerank个人详解)
  13. Ubuntu配置GPU版本pytorch环境(含NVIDIA驱动+Cuda+Cudnn)
  14. Django 文档 -- 记录我的Django学习之旅
  15. vue3+heightchart实现3D饼图,echarts3D饼图,3D饼图引导线实现
  16. 分享几个 Github 镜像网站(亲测可用)
  17. uniapp-上传图片、上传视频
  18. 【前端三分钟】利用Javascript实现打字效果
  19. 895计算机专业基础,北京工业大学2020年考研895计算机学科专业基础考试大纲
  20. HEP惠普SN3600B,H3C新华三CN3360B光纤交换机调试配置方法

热门文章

  1. C++ Primer 第三章 标准库类型 笔记
  2. HDU 6623 Minimal Power of Prime(思维)题解
  3. 2014年读过的那些书
  4. 1011. World Cup Betting (20)——PAT (Advanced Level) Practise
  5. 20款免费公司企业wordpress主题
  6. 两堆草前饿死的驴——选择永远是件痛苦的事
  7. R 语言之数据分析高级方法「GLM 广义线性模型」
  8. 计算机网络领悟摩尔定,计算机网络等133信息技术展望P15-P17.PPT
  9. VS2010+OpenCV2.4.9配置
  10. @RequestParam使用须知