VC 调用托管程序集(.Net Managed Assemblies)

23 篇文章 0 订阅
2 篇文章 0 订阅

前言

.Net 类库功能非常全面分装了大量应用级别的API,所以有时会有在VC 中调用托管程序集(.Net Managed Assemblies) 的需求。本文通过示例对该步骤进行说明,并提供一些参考。

前提

本文假设已有一个现成可用的托管程序集,代码如下:

// 媒体类:用来载入一个媒体文件,并解析出信息
public class Media
{
	// 内部处理
	private void DoSomething()
	{ ... }

	// 载入文件	
	public void LoadMedia(string filePath)
	{ ... }

	// 获取媒体信息
	public MediaInfo GetInfo()
	{ ... }
}

// 媒体信息类
public class MediaInfo
{
	// 内部标示
	private Guid id;
	// 文件名
	public property string Name { get; set; }
}

托管程序集(.Net Managed Assemblies)

1 追加 ComVisible Attribute

要使一个托管程序集能作为 COM 被 VC 调用首先需要添加 ComVisible Attribute。如果你比较懒可以在项目的 AssemblyInfo.cs 文件中进行全局声明:

[assembly: ComVisible(true)]
[assembly: Guid("2aa76d89-5faf-473e-8285-b9199fedd365")]

当程序集的公有类相当多的时候这是个好办法,但是并不建议这样做,因为调用方(VC)通常并不需要访问程序集中的所有公有类,并且过多的暴露程序集也不太好。这里我们就勤劳点,在每个类上进行声明。

[ComVisible(true)]
public class Media : IDisposable
{
	// 略
}

[ComVisible(true)]
public class MediaInfo
{
	// 略
}

2 追加 ClassInterface Attribute

参照一下 MSDN 该 Attribute 需要传入一个 ClassInterfaceType 枚举来决定生成接口的方式:

  • AutoDual 指示自动为类生成双重类接口并向 COM 公开。为该类接口生成类型信息并在类型库中发布。由于 ClassInterfaceAttribute 中描述的版本控制方面的限制,极力建议不要使用 AutoDual。
  • AutoDispatch 指示该类只支持 COM 客户端的后期绑定。在请求时,该类的调度接口将自动向 COM 客户端公开。类型 类型库导出程序 (Tlbexp.exe) 生成的类型库不包含调度接口的类型信息,以防止客户端缓存接口的 DISPID。由于客户端只能后期绑定到调度接口,因此该接口不会出现 ClassInterfaceAttribute 中所述的版本控制问题。
    这是 ClassInterfaceAttribute 的默认设置。
  • None 指示不为类生成类接口。如果未显式实现任何接口,则该类将只通过 IDispatch 接口提供后期绑定访问。这是 ClassInterfaceAttribute 的推荐设置。

既然官方推荐,那么我们就采用ClassInterfaceType.None,但是因为类接口不再自动生成,所以我们需要自己为 2 个类分别定义 2 个接口:

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IMedia))]
public class Media : IDisposable, IMedia
{
	// 略
}

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class MediaInfo : IMediaInfo
{
	// 略
}

[ComVisible(true)]
public interface IMedia
{	
	void LoadMedia(string filePath);
	MediaInfo GetInfo();
}

[ComVisible(true)]
public interface IMediaInfo
{
	property string Name { get; set; }
}

提示:

  • 接口只需要定义公开给 Com 的接口,即如果VC 不需要调用 LoadMedia 方法,那么IMedia 接口中可以不包含该方法
  • 接口本身也需要 ComVisible 声明
  • 当某个类实现了多个接口的情况下,需要使用 ComDefaultInterface 明确指定 COM 接口是哪一个

3 生成支持 COM 调用托管程序集(.Net Managed Assemblies)

使用 VS 的话相当简单,在工程的属性页面里“Register for COM interop”打个钩就一切搞定,编译后去 bin 下面就能找到 .NET 程序集,以及一个同名的 .tlb 文件。

当然你也需要了解下怎样使用命令行工具来实现,毕竟配布到用户机上时会用到。这里介绍下程序集注册工具 (Regasm.exe),VS 实际上帮我们运行了以下命令:

regasm myTest.dll /tlb:myTest.tlb

MSDN 官方描述为:注册 myTest.dll 中包含的所有公共类,并生成和注册类型库 myTest.tlb,该类型库包含 myTest.dll 中定义的所有公共类型的定义。

COM 类注册自然是更新注册表了,那配布的时候用户机上同样需要更新注册表,那么怎样知道需要更新哪些信息呢?Regasm.exe 会告诉你:

regasm myTest.dll /regfile:myTest.reg

这个命令会生成一个注册表文件,需要更新的内容都在里面了。好了该说 VC 怎样调用了。

VC 调用托管程序集

方便起见我们用空的一个命令行 VC 工程来说明步骤。

1 添加 import 声明,并生成 com 接口

在 .cpp 文件中添加以下内容并编译(生成物中会包含一个 .tlh 的头文件):

#import "myTest.tlb" named_guids raw_interfaces_only

PS:这里的文件路径可以是绝对路径,也可以是相对路径。

如果你还引用到一些 .NET 类库中的类型那么也需要追加 import 声明,譬如使用了 System.Drawing 中的类型,那么要追加:

#import "C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Drawing.tlb" named_guids raw_interfaces_only

2 代码调用

HRESULT hRes;
hRes = CoInitialize(NULL);	// 初始化 Com

MyTest::IMediaPtr pIMedia(__uuidof(MyTest::Media));		// 使用智能指针实例化对象

pIMedia->LoadMedia(::SysAllocString(L"d:\\aaa.png"));		// 调用方法,OK

MyTest::IMediaInfoPtr pIMediaInfo = pIMedia->GetInfo();		// 调用方法并返回值,NG

CoUninitialize();	// 终止初始化 Com

调用没有返回值的方法时一切正常,调用有返回值的方法时你会发现函数定义被改写了,这是什么情况?原来从 .NET 程序集转换为 com 时,如果有返回值,这个返回值将作为ref参数追加在该函数之后,而返回值会用来返回 HRESULT。这虽然没啥大问题,但用起来却大大的不爽,想用回原来调用方式的情看下文。

托管程序集(.Net Managed Assemblies)再修改

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IMedia))]
public class Media : IDisposable, IMedia
{
	// 略

	[PreserveSig]
	public MediaInfo GetInfo()
	{ ... }
}

PreserveSig 可以保证转换的 com 接口不改写原来的方法定义,详细请看http://msdn.microsoft.com/zh-cn/library/system.runtime.interopservices.dllimportattribute.preservesig(v=vs.80).aspx

现在我们再次编译 .NET ,打开 VC 工程,并确认两个方法都能正确调用了。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值