欢迎来到.net学习网

欢迎联系站长一起更新本网站!QQ:879621940

您当前所在位置:首页 »  .NET本质论第一卷:公共语言运行库教程 » 正文

本教程章节列表

MarshalAs特性、RCW与CCW架构(二)

创建时间:2013年06月14日 22:28  阅读次数:(4421)
分享到:
与其他对非托管代码的调用(或者来自非托管代码的调用)类似,对RCW或者CCW的调用将触发模式转换。然而,与P/Invoke的情形相同,对RCW的调用也要强制进行安全请求,因为对非托管DLL的调用是一个特权操作.当你凋用RCW,方法参数所用到的类型转换规与与P/Invoke调用略有不同。尤其是,字符串默认为BSTR,布尔型默认为VARIANT_BOOL,并且PreserveSig被假定为false,而不是true。为了阻止HRESULT的自动转换出现异常,必须将System.Runtime.InteropServices.PreservieSig特性应用到相应的接口方法上。

对于通过RCW或者ccw跨P/Invoke边界的接口,CLR依靠在托管接口定义的一组批注,向底层封送层进行提示,告诉它如何转换这些类型。这些提示是那些用于P/Invoke的提示(如前面所述)超集。此外,需要定义的方面还包括uuID、vtable(虚函数表)对和分发对的双向映射,以及IDispatch是如何处理的,数组是如何解析的。使用Systerm.Runtime.InteropServices命名空间下的特性,就能将这些方面添加到托管的接口定义中。在这些特性不存在的情况下,CLR进行保守地猜测,以作为给定接口和方法的默认设置。对于全新的托管接口.如果你想让你的接口在CLR之外使用,显式地使用特性是十分有用的。
 
你能够手工地将本机的COM类型(例如,结构、接口等)翻译为CLR,并且,在某些情况下,这是必要的,尤其是当准确的TLB不存在的时候。在CLR中,用反射进行翻译类型定义是比较普遍,也是比较简单的。不过,这在一般情况下都是通过工具来实现,而不是手工完成的。CLR带有这个工具,能够提供足够精确的COM TLB。System.Runtime.InteropServices.TypeLibCoeverter能够在TLB与CLR之间进行转化。Conrvert AssemblyToTypeLib方法读取CLR程序集,并且发射包含对应COM类型定义的TLB。这个翻译过程的任何提示(例如,MarshalAs)必须以自定义特性的形式出现在源类型的接口、方法、字段和参数中。ConvertTypeLibToAssembly方法读取COM LIB,并且发射包含对应CLR类型定义的CLR程序集。SDK带有两个工具(TLBEXP.EXE和TLBIMP.EXE),它们通过NMAKE将这两个方法封装进来,并用命令行的形式体现出来。图10.6说明了两个工具之间的关系。
TLBIMP和TLBEXP

一般情况下,使用基于CLR的语言定义类型,然后再发射TLB,是比较容易的事情。如示例1O.1所示的c#代码。如果我们将这个代码当作“伪IDL”文件,那么,通过CSC.EXE和TLBEXE.EXE可以生成TLB,其功能上与示例10.2的“真IDL”是相同的。使用c#方式的好处是:类型定义是可扩展的,并且比较容易实现机器可读(machine-readable)。这些对TLB或者IDL文件而言,都是无法比拟的。

示例10.1 C#可以作为一个更好的IDL
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

[assembly:Guid(“4c5025ef-3ae4-4128-ba7b-db4fb6e0c532”)]
[assembly:AssemblyVersion(“2.1”)]

namespace AcmeCorp.MathTypes{
[Guid(“ddc244a4-c8b3-4c20-8416-1e7d0398462a”),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
]
public interface ICalculator{
double CurrentValue{get;}
void Clear();

void Add(double x);
void Subtract(double x);
void Multiply(double x);
void Divide(double x);
}
}

示例10.2 C#可以作为一个更好的IDL(生成TLB)
[uuid(4C5025EF-3AE4-4128-BA7B-DB4FB6E0C532),version(2.1)]
library AcmeCorp_MathTypes
{
importlib(“stdole2.tlb”);
[object,
uuid(DDC244A4-C8B3-4C20-8416-1E7D0398462A),
oleautomation,
custom({0F21F359-AB84-41E8-9°78-36D110E6D2F9},”AcmeCOrp.MathTypes.ICalculator”)]
interface ICalculator:IUnknown{
[propget]
HERSULT CurrentValue([out,retval] double* pRetVal);
HRESULT Clear();
HRESULT void Add([in] double x);
HRESULT void Subtract([in] double x);
HRESULT void Multiply([in] double x);
HRESULT void Divide([in] double x);
}
}

在理想情况下,给定类型可能只有一种定义。遗憾的是,实际安装的COM需要两个定义:一个在CLR的元数据中,另一个则在COM TLB中。如果COM TLB是类型的“权威”版本,那么,多个开发人员导入类型就会有风险。对于这些被导入的程序集,每个都与CLR不同;并且,这意味着对COM组件的对象引用无法在多个基于CLR组件间共享。列于共享的COM组件,例如,ActiveX数据对象ActiveX Data Objects. ADO),问题尤为严重,这是因为将ADO记录集作为参数传递,是20世纪90年代为VB开发人员定义的技术,对于每个类型库,为了确保只有一个导入的CLR程序集被使用,CLR支持主Interop程序集的概念。

你可以在COM注册表上注册主interop程序集(primary interop asscmbly),作为TLB的权威版本。当你为了COM TLB中的类型而加载CLR类型时,CLR将遵从主interop程序集中的类型定义。这样就确保了在内存中,只有一个给定COM类型的版本。你可以在TLBIMP.EXE的命令行上通过选项开关/primary设置主interop程序集。当管理员或者用户使用REGASM.EXE注册最终的程序集时,REGASM.EXE将附加的注册项存放于HKEY CLASSES_ROOT\TypeLib下,以标明导入的程序集是对于COM LIB的主interop程序集。为了维护一致性,被主interop程序集的TLB所引用的类型库,也必须有主interop程序集。当CLR被安装时,REGASM.EXE将为STDOLE.TLB创建主interop程序集,它会被所有的TLB引用。

关于P/Invoke的讨论说明了如何从基于CLR的程序中透明地访问传统Win32加载器。P/invoke的讨论忽略了另一种传统加载器,这种加载器主要流行于20世纪90年代,它就是COM的CoCreatelnstance。

COM加载器将位置无关的类型名转译为DLL,公开为DliGetClassObject入口点,你可吧通过一系列的API函数公开这个功能。然而,最常用的就是CoCreateInstance。尽管通过P/Invoke可以完全合法地调用CoCreatelnstance,但是大多数基下 CLR的程序都会使用System.Runtime.InteropServices.ComImport特性。

对于标记为ComImport特性的基于CLR的类,CLR将做特别处理。尤其是在CIL指令newobj在标记为ComImport特性的类型上执行时.CLR将从元数据中读取类型的全局惟一标识符(globally unique identifier,GUID),并且将newobj请求转译为对CoCreateInstance的调用。使用ComImport特件的类型必定使用System.Runtime.InteropServices.Guid特性,显式地控制类型的GUID。

下面的程序使用ComImport特性,将名为Excel的基于CLR的类型映射为Microsoft Excel的COM类。
using System;
using System.Runtime.InteropServices;
[ComImport]
[Guid(“00020812-0000-0000-C000-000000000046”)]
class Excel{}

class xx{
static void Main(){
Excel app=new Excel(); //调用CoCreateInstance
Console.ReadLine();
Console.WriteLine(app.ToString());
}
}

注意,对于TLB中的每一个coclass,CLR的TLB导入器将自动生成ComImport类型。

COM程序员经常使用标记(moniker)在客户端同目标类及对象之间设置间接的级别。程序员往往通过COM的CoGetObject或者VB的GetObject函数实现这种能力。基于CLR的程序能够使用Marshal.BindMoniker静态方法实现同样的功能。?ug魰 Y钀D
来源:.net学习网
说明:所有来源为 .net学习网的文章均为原创,如有转载,请在转载处标注本页地址,谢谢!
【编辑:Wyf】

打赏

取消

感谢您的支持,我会做的更好!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

最新评论

共有评论0条
  • 暂无任何评论,请留下您对本文章的看法,共同参入讨论!
发表评论:
留言人:
内  容:
请输入问题 99+54=? 的结果(结果是:153)
结  果: