当前位置:编程学习 > VC++ >>

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 追加 ClassInterface Attribute
参照一下 MSDN 该 Attribute 需要传入一个 ClassInterfaceType 枚举来决定生成接口的方式:

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

[csharp]
[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; } 

[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 实际上帮我们运行了以下命令:

[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_interfaces_only 

#import "myTest.tlb" named_guids raw_interfaces_onlyPS:这里的文件路径可以是绝对路径,也可以是相对路径。

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

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

#import "C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Drawing.tlb" named_guids raw_interfaces_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 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,