VC 调用托管程序集(.Net Managed Assemblies)
前言
.Net 类库功能非常全面分装了大量应用级别的API,所以有时会有在VC 中调用托管程序集(.Net Managed Assemblies) 的需求。本文通过示例对该步骤进行说明,并提供一些参考。
前提
本文假设已有一个现成可用的托管程序集,代码如下:
[csharp]
// 媒体类:用来载入一个媒体文件,并解析出信息
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; }
}
// 媒体类:用来载入一个媒体文件,并解析出信息
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 文件中进行全局声明:
[csharp]
[assembly: ComVisible(true)]
[assembly: Guid("2aa76d89-5faf-473e-8285-b9199fedd365")]
[assembly: ComVisible(true)]
[assembly: Guid("2aa76d89-5faf-473e-8285-b9199fedd365")]当程序集的公有类相当多的时候这是个好办法,但是并不建议这样做,因为调用方(VC)通常并不需要访问程序集中的所有公有类,并且过多的暴露程序集也不太好。这里我们就勤劳点,在每个类上进行声明。
[csharp] view plaincopyprint?[ComVisible(true)]
public class Media : IDisposable
{
// 略
}
[ComVisible(true)]
public class MediaInfo
{
// 略
}
[ComVisible(true)]
public class Media : IDisposable
{
// 略
}
[ComVisible(true)]
public class MediaInfo
{
// 略
}2 追加 ClassInte易做图ce Attribute
参照一下 MSDN 该 Attribute 需要传入一个 ClassInte易做图ceType 枚举来决定生成接口的方式:
AutoDual 指示自动为类生成双重类接口并向 COM 公开。为该类接口生成类型信息并在类型库中发布。由于 ClassInte易做图ceAttribute 中描述的版本控制方面的限制,极力建议不要使用 AutoDual。
AutoDispatch 指示该类只支持 COM 客户端的后期绑定。在请求时,该类的调度接口将自动向 COM 客户端公开。类型 类型库导出程序 (Tlbexp.exe) 生成的类型库不包含调度接口的类型信息,以防止客户端缓存接口的 DISPID。由于客户端只能后期绑定到调度接口,因此该接口不会出现 ClassInte易做图ceAttribute 中所述的版本控制问题。
这是 ClassInte易做图ceAttribute 的默认设置。
None 指示不为类生成类接口。如果未显式实现任何接口,则该类将只通过 IDispatch 接口提供后期绑定访问。这是 ClassInte易做图ceAttribute 的推荐设置。
既然官方推荐,那么我们就采用ClassInte易做图ceType.None,但是因为类接口不再自动生成,所以我们需要自己为 2 个类分别定义 2 个接口:
[csharp]
[ComVisible(true)]
[ClassInte易做图ce(ClassInte易做图ceType.None)]
[ComDefaultInte易做图ce(typeof(IMedia))]
public class Media : IDisposable, IMedia
{
// 略
}
[ComVisible(true)]
[ClassInte易做图ce(ClassInte易做图ceType.None)]
public class MediaInfo : IMediaInfo
{
// 略
}
[ComVisible(true)]
public inte易做图ce IMedia
{
void LoadMedia(string filePath);
MediaInfo GetInfo();
}
[ComVisible(true)]
public inte易做图ce IMediaInfo
{
property string Name { get; set; }
}
[ComVisible(true)]
[ClassInte易做图ce(ClassInte易做图ceType.None)]
[ComDefaultInte易做图ce(typeof(IMedia))]
public class Media : IDisposable, IMedia
{
// 略
}
[ComVisible(true)]
[ClassInte易做图ce(ClassInte易做图ceType.None)]
public class MediaInfo : IMediaInfo
{
// 略
}
[ComVisible(true)]
public inte易做图ce IMedia
{
void LoadMedia(string filePath);
MediaInfo GetInfo();
}
[ComVisible(true)]
public inte易做图ce IMediaInfo
{
property string Name { get; set; }
}提示:
接口只需要定义公开给 Com 的接口,即如果VC 不需要调用 LoadMedia 方法,那么IMedia 接口中可以不包含该方法
接口本身也需要 ComVisible 声明
当某个类实现了多个接口的情况下,需要使用 ComDefaultInte易做图ce 明确指定 COM 接口是哪一个
3 生成支持 COM 调用托管程序集(.Net Managed Assemblies)
使用 VS 的话相当简单,在工程的属性页面里“Register for COM interop”打个钩就一切搞定,编译后去 bin 下面就能找到 .NET 程序集,以及一个同名的 .tlb 文件。
当然你也需要了解下怎样使用命令行工具来实现,毕竟配布到用户机上时会用到。这里介绍下程序集注册工具 (Regasm.exe),VS 实际上帮我们运行了以下命令:
[plain]
regasm myTest.dll /tlb:myTest.tlb
regasm myTest.dll /tlb:myTest.tlbMSDN 官方描述为:注册 myTest.dll 中包含的所有公共类,并生成和注册类型库 myTest.tlb,该类型库包含 myTest.dll 中定义的所有公共类型的定义。
COM 类注册自然是更新注册表了,那配布的时候用户机上同样需要更新注册表,那么怎样知道需要更新哪些信息呢?Regasm.exe 会告诉你:
[plain]
regasm myTest.dll /regfile:myTest.reg
regasm myTest.dll /regfile:myTest.reg
这个命令会生成一个注册表文件,需要更新的内容都在里面了。好了该说 VC 怎样调用了。
VC 调用托管程序集
方便起见我们用空的一个命令行 VC 工程来说明步骤。
1 添加 import 声明,并生成 com 接口
在 .cpp 文件中添加以下内容并编译(生成物中会包含一个 .tlh 的头文件):
[cpp]
#import "myTest.tlb" named_guids raw_inte易做图ces_only
#import "myTest.tlb" named_guids raw_inte易做图ces_onlyPS:这里的文件路径可以是绝对路径,也可以是相对路径。
如果你还引用到一些 .NET 类库中的类型那么也需要追加 import 声明,譬如使用了 System.Drawing 中的类型,那么要追加:
[cpp]
#import "C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Drawing.tlb" named_guids raw_inte易做图ces_only
#import "C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Drawing.tlb" named_guids raw_inte易做图ces_only2 代码调用
[cpp]
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
补充:软件开发 , Vc ,