Welcome to MSDN Blogs Sign in | Join | Help

CLR & Silverlight上海研发团队的Blog

专注底层技术

Syndication

News

如果你对CLR或Silverlight有什么意见或建议,欢迎通过导航条中的EMAIL给我们留言。有技术问题欢迎访问MSDN中文论坛进行提问(请参阅链接)。 贴子以"现状"提供且没有任何担保也没有授予任何权利。如果需要转载、录用文章,必须点击Email与博客管理员书面联系取得许可。
使用IDispatch::Invoke函数在C++中调用C#实现的托管类库方法
 

CLR Interop简而言之是让非托管代码与托管代码之间可以相互调用的技术。这项技术可以使开发人员重用已有的托管或非托管组建,并根据自己的需要,权衡托管代码的简易性与非托管代码的灵活性,选择适合自己实际情况的编程语言,而不用过多考虑重用的组件是用哪种语言开发的。Interop中文的意思是互通性,既然是互通,代码的调用就有两种不同的方向。本文所要讲述的是使用COM Interop技术在非托管代码方如何调用托管代码。

1. 创建托管服务器

首先让我们在Visual Studio 2008创建一个C#的Class Library(类库)项目,取名为MyManagedServer,在该项目中,我们要声明并实现一个接口。

接口声明代码如下:

为了说明简单,该接口中只有一个方法,用于打印一些信息。其中的ComVisible属性至关重要,当它的值为true时,该接口才对COM可见。

  1. using System;   
  2. using System.Runtime.InteropServices;   
  3.   
  4. namespace MyManagedServer   
  5. {   
  6.     [ComVisible(true),   
  7.      Guid("79EDDA1C-F243-47C5-8954-5DEF01FA3D44"),   
  8.      InterfaceType(ComInterfaceType.InterfaceIsDual)]        
  9.     public interface IManagedFooClass   
  10.     {   
  11.         [PreserveSig, DispId(1)]   
  12.         void PrintFoo();   
  13.     }   
  14. }  

接下来是实现该接口的类:

  1. using System;   
  2. using System.Runtime.InteropServices;   
  3.   
  4. namespace MyManagedServer   
  5. {   
  6.     [ComVisible(true),   
  7.      ClassInterface(ClassInterfaceType.AutoDual),   
  8.      ProgId("MyManagedServer.ManagedFooClass")   
  9.     ]   
  10.     public class CustomCOMClient : 
  11.                              IManagedFooClass, 
  12.                              IManagedBarClass   
  13.     {   
  14.         public CustomCOMClient()   
  15.         {   
  16.         }  
  17.           
  18.         #region IManagedFooClass Members   
  19.   
  20.         [DispId(1)]   
  21.         public void PrintFoo()   
  22.         {   
  23.             Console.WriteLine("in MyManagedServer: CustomCOMClient.PrintFoo()");   
  24.         }  
  25.         #endregion   
  26.     }   
  27. }   

这里我们给这个类的ProgId属性赋一个值。等会儿在注册组件的时候,注册表中将会增加一个键值,将ProgId和runtime为我们自动生成的CLSID关联起来。

2. 为COM Interop注册托管服务组件

注册组件可以用Visual Studio帮我们自动注册,也可以在命令行下手动输入命令。若要使用Visual Studio来帮我们注册组件,只需在项目属性页(鼠标右键项目名称,在下拉菜单中选择“Properties(属性)”)的Build标签页中把Register for COM Interop项打上勾,然后再build项目就可以了。如下图所示:

此外,我们也可以先build项目,然后通过命令行的方式注册组件。只需要使用regasm.exe在VS2008命令行下输入如下命令即可:

regasm assemblyname.dll /tlb /codebase

该命令会为我们注册组件,生成并注册对应的type library文件。其中assemblyname.dll是项目构建生成的程序集文件。

3. 创建非托管客户端

使用托管语言创建并注册了组建之后,我们就要使用非托管语言来尝试通过COM Interop调用组建中的方法了。首先,在Visual Studio 2008中创建一个Visual C++ Win32 Console Application,取名为MyNatvieClient,并将组建生成tlb文件拷贝至该项目的源代码目录中。然后在MyNativeClient.cpp中输入如下代码:

  1. #include "stdafx.h"  
  2. #import "mscorlib.tlb" no_namespace  
  3. #import "MyManagedServer.tlb" no_namespace   
  4.   
  5. int _tmain(int argc, _TCHAR* argv[])   
  6. {   
  7.   
  8.     ::CoInitialize(NULL);   
  9.   
  10.     // Get CLSID for CoCreateInstance   
  11.     const OLECHAR lpszProgID[] = OLESTR("MyManagedServer.ManagedFooClass");   
  12.     CLSID clsid;       
  13.     HRESULT hr = CLSIDFromProgID(lpszProgID, &clsid);   
  14.     if(SUCCEEDED(hr))   
  15.     {   
  16.         printf("CLSIDFromProgID Succeeded \n");   
  17.         IDispatch* ppv = 0;    
  18.         HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IDispatch, (void**)&ppv);   
  19.   
  20.         if(SUCCEEDED(hr))   
  21.         {   
  22.             printf("CoCreateInstance Succeeded \n");   
  23.   
  24.             // Get DispId for Invoke   
  25.             DISPID dispid;   
  26.             const LPOLESTR szMember = OLESTR("PrintFoo");   
  27.             HRESULT hr = ppv->GetIDsOfNames(IID_NULL, (LPOLESTR*)&szMember,1,LOCALE_SYSTEM_DEFAULT,&dispid);   
  28.             if(SUCCEEDED(hr))   
  29.             {   
  30.                 printf("GetIDsOfNames Succeeded \n");   
  31.   
  32.                 // There's no parameter to pass   
  33.                 DISPPARAMS dispParams = {0};   
  34.                 VARIANT vtResult;   
  35.                 UINT dwArgErr;   
  36.   
  37.                 HRESULT hr = ppv->Invoke(dispid,IID_NULL,NULL,DISPATCH_METHOD,
  38.                                                         &dispParams,&vtResult,NULL,&dwArgErr);   
  39.                 if(SUCCEEDED(hr))   
  40.                 {   
  41.                     printf("Invoke Succeeded \n");   
  42.                 }   
  43.             }   
  44.   
  45.             ppv->Release();   
  46.         }   
  47.     }   
  48.   
  49.     return 0;   
  50. }  

该代码主要做了以下几件事情:

a. 调用CoInitialize进行初始化。

b. 调用CLSIDFromProgId获得对象的CLSID,因为接下来的函数将通过CLSID来创建实例。

c. 通过CoCreateInstance创建对象实例。这里创建的是一个IDispatch的对象实例。

d. 调用IDispatch::GetIDsOfNames以获得将要调用的方法的DispID,供接下来的函数使用。

e. 使用IDispatch::Invoke来调用方法。

在import type library的时候我们不仅import了组建的tlb文件,同时还import了mscorlib.tlb以避免生成的临时的tlh文件中一些类型找不到的情况。(有关此方面的问题可以参考我们团队开发人员张羿撰写的《#import从.NET DLL生成的tlb的神秘错误》)

编译通过后运行结果,可看到命令行中打印出如下信息:

CLSIDFromProgID Succeeded
CoCreateInstance Succeeded
GetIDsOfNames Succeeded
in MyManagedServer: CustomCOMClient.PrintFoo()
Invoke Succeeded

注:本文所示代码只作为实例使用。本文作者不对因代码使用不当而造成的问题负责。v

Published Tuesday, July 22, 2008 1:51 PM by SilverlightShanghai

Filed under:

Comments

# Basketball Chat » Blog Archive » ??????IDispatch::Invoke?????????C++?????????C#??????????????????????????? @ Tuesday, July 22, 2008 3:47 AM

PingBack from http://www.basketballs-sports.info/basketball-chat/?p=1911

Basketball Chat » Blog Archive » ??????IDispatch::Invoke?????????C++?????????C#???????????????????????????

# 近期团队博客的摘要 8 @ Sunday, August 31, 2008 7:57 PM

VS2008 SP1正式发布: WCF工具的相关功能和贴图 VS2008 SP1正式发布了! 在本篇文章中,我将会用贴图的形式向大家介绍在VS2008 SP1中由我们团队开发的WCF Tools 新功能。

服务世界 开发未来

# 近期团队博客的摘要 @ Sunday, August 31, 2008 8:50 PM

VS2008 SP1正式发布: WCF工具的相关功能和贴图 VS2008 SP1正式发布了! 在本篇文章中,我将会用贴图的形式向大家介绍在VS2008 SP1中由我们团队开发的WCF Tools 新功能

技术产品

Anonymous comments are disabled
 
© 2008 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Page view tracker