• 使用IDispatch::Invoke函数在C++中调用C#实现的托管类库方法


    使用IDispatch::Invoke函数在C++中调用C#实现的托管类库方法

    原贴地址:http://blogs.msdn.com/silverlightshanghai/default.aspx

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

    1. 创建托管服务器

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

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

    接口声明代码如下:

     1using System;    
     2using System.Runtime.InteropServices;    
     3   
     4namespace 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 : IManagedFooClass
    11     {    
    12         public CustomCOMClient()    
    13         {    
    14         }          
    15   
    16         [DispId(1)]    
    17         public void PrintFoo()    
    18         {    
    19             Console.WriteLine("in MyManagedServer: CustomCOMClient.PrintFoo()");    
    20         }   
    21     }    
    22 }  

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

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

    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文件中一些类型找不到的情况。

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

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

  • 相关阅读:
    数组的复制
    二维数组的切片和索引
    一维数组的切片和索引
    is判断函数
    其他方式创建数组
    ndarray对象属性
    创建随机数组
    arange创建数组
    SmartBinding与kbmMW#3
    SmartBinding与kbmMW#2
  • 原文地址:https://www.cnblogs.com/tedzhao/p/1249845.html
Copyright © 2020-2023  润新知