最近项目涉及第三方接口调用。第三方是用C#实现的WCF服务。而我们的程序是使用的BCB6开发。因此,打算将与WCF的通讯包含在C#的类库中,给BCB6调用。BCB6 是无法直接调用C#的DLL,但可以通过C#编写一个COM组件,然后BCB调用这个COM组件来调用。
-
用C#编写COM
大体步骤是
- 新建一个类库项目
-
设置Assemblyinfo.cs
-
设置项目属性
- 编写代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; //1添加Rumtime.InteropServices 程序集引用 using System.Windows.Forms; //2为了演示,所以加入了forms引用 namespace ComImpTest { [Guid("3CD116D4-18A1-4504-921B-57C053BAD618")] //3主菜单->工具->创建GUID产生 public interface IComInterface //4必须先定义接口 { [DispId(1)] //5每一个函数或者属性都需要指定DispID void Hello(string name); [DispId(2)] int Add(int x,int y); [DispId(3)] string COMINFO{get;} } [Guid("1A8EB38C-E732-49B2-A897-B40FFD744E3D")] public class ComImp : IComInterface //6编写一个实现接口的类 { public void Hello(string name) { Console.WriteLine("你好"+name); MessageBox.Show("你好"+name,"C# dll 弹出界面"); } public int Add(int x, int y) { return x + y; } public string COMINFO { get { return "com 测试"; } } } } |
-
编译得到DLL 和 TLB
-
BCB编写调用
-
首先BCB的开发环境中需要安装.NET FrameWork ,这里都是用的.NET FrameWork 4.0,安装之后,
在如图的目录会得到RegAsm.exe(一定要用安装后得到的RegAsm 从其他地方复制的RegASM可能存在版本不兼容),
这个在注册COM的时候要用到。
- 建立一个BCB控制台工程
-
吧RegAsm.exe(我们是为了注册方便),前面编译得到的 ComImpTest.tlb 、ComImpTest.dll 复制到BCB工程的EXE
输出目录。(这也不是必须的,本质上COM只要能注册,然后BCB功能能找到TLB创建代理类即可)
-
用管理员启动命令行,注册ComImpTest.dll
其本质是吧这个COM类注册到了注册表中。向系统进行了发布。
-
用BCB 产生代理类
打开BCB 工程,选择主菜单 projectàimport type library
得到代理类
包含这个头文件,我们就可以调用了。
但是这里不知道是BCB的bug,还是兼容性的问题,BCB产生的ComImp代理类的GUID是错误的。
正确的GUID 应该是编写C#dll 时产生的Guid
-
[Guid("1A8EB38C-E732-49B2-A897-B40FFD744E3D")] |
我们只需要替换这个Guid即可,如果不这么做,调用时HRESULT会返回找不到实现接口。
-
BCB 调用COM的代码
- 在控制台工程中,加入代理类的头文件 ComImpTest_tlb.h
- 代码如下
//--------------------------------------------------------------------------- #pragma hdrstop #include <iostream> #include <vcl.h> //注意,iostream要在 vcl.h 前面定义,否则cout<<AnsiString 报错 #include "Comimptest_tlb.h" using namespace std; //--------------------------------------------------------------------------- #pragma argsused int main(int argc, char* argv[]) { HRESULT hr; //初始化COM CoInitialize ( NULL ); //创建智能指针命名空间在头文件Comimptest_tlb.h中可以查找到 //接口智能指针的定义也能查找到 Comimptest_tlb::IComInterfacePtr ptr; //创建实例,ComImp 的声明,也能在头文件中查找到。 hr = ptr.CreateInstance(__uuidof (Comimptest_tlb::ComImp));
if(hr == S_OK) { //调用ADD方法 cout << ptr->Add (1, 2)<<endl; //调用Hello方法,在bcb中Widestring 对应 c#的 string WideString name = "Zakk wylde 和奥兹"; ptr->Hello(name.c_bstr()); //调用COMINFO属性,这里COM进行了名字改变,这些函数都可以在头文件中查到原型定义。 BSTR outp = NULL; ptr->get_COMINFO(&outp); //因为是宽字符,所以要转换为AnsiString进行输出 cout<<AnsiString(outp)<<endl; } CoUninitialize (); system("pause"); return 0; } |
4 用vc调用COM
用VC调用C#的COM 就没有这么多事情。下面是VC调用的代码,
将ComImpTest.tlb 和 ComImpTest.dll 拷贝到 VC工程的debug目录下。然后用import指令(BCB 无法用import识别,估计是不兼容)
// testCOM3.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> #include <Windows.h> using namespace std; #import "../debug/ComImpTest.tlb" int _tmain(int argc, _TCHAR* argv[]) { HRESULT hr; //初始化COM CoInitialize ( NULL ); //创建智能指针 ComImpTest::IComInterfacePtr ptr; //创建实例 hr = ptr.CreateInstance(__uuidof (ComImpTest::ComImp)); if(hr == S_OK) { cout << ptr->Add (1,2)<<endl; ptr->Hello(L"VC++ 调用"); cout<<ptr->COMINFO<<endl; } CoUninitialize (); system("pause"); return 0; } //---------------------------------------------------------------------------