C#程序与COM服务程序传递数组和字符串的方式
1. COM服务程序
创建ATL项目“MyComSvrArrAndStr”,添加ATL简单对象“SimpleDataObject”。
SimpleDataObject.h
1 …… 2 #include <atlstr.h> 3 #include <atlsafe.h> 4 …… 5 class ATL_NO_VTABLE CSimpleDataObject : 6 …… 7 { 8 public: 9 CSimpleDataObject() 10 : _message(NULL), _comSfArr(NULL) 11 { 12 } 13 …… 14 HRESULT FinalConstruct() 15 { 16 _message = new CString("final construct"); 17 _comSfArr = new CComSafeArray<LONG>; 18 return S_OK; 19 } 20 21 void FinalRelease() 22 { 23 delete _message; 24 delete _comSfArr; 25 } 26 27 public: 28 STDMETHODIMP ReceiveMessage(BSTR message); 29 STDMETHODIMP TransmitMessage(BSTR* message); 30 STDMETHODIMP CalcArrayValue(SAFEARRAY** arr); 31 32 private: 33 CString* _message; 34 CComSafeArray<LONG>* _comSfArr; 35 }; 36 ……
已经略去了模板原有的部分。注意两个私有成员_message和_comSfArr,它们就是用于存储字符串和数组的数据对象,类型CString来自于头文件atlstr.h,模板类型CComSafeArray来源于atlsafe.h。这里修改了模板中的两个函数FinalConstruct()和FinalRelease(),它们分别被自动调用于对象构造之后和析构之前,用于管理一些非托管资源。紧接着就是声明对外方法,这里有三个:ReceiveMessage(),接受外部字符串,存入_message中;TransmitMessage(),把_message内容发送到外部;CalcArrayValue(),接收一个来自外部的数组,并进行一些处理。
SimpleDataObject.cpp
1 STDMETHODIMP_(HRESULT __stdcall) CSimpleDataObject::ReceiveMessage(BSTR message) 2 { 3 *_message = message; 4 return S_OK; 5 } 6 7 STDMETHODIMP_(HRESULT __stdcall) CSimpleDataObject::TransmitMessage(BSTR* message) 8 { 9 *message = _message->AllocSysString(); 10 return S_OK; 11 } 12 13 STDMETHODIMP_(HRESULT __stdcall) CSimpleDataObject::CalcArrayValue(SAFEARRAY** arr) 14 { 15 _comSfArr->Attach(*arr); 16 ULONG size = _comSfArr->GetCount(); 17 _comSfArr->Add((LONG)0); 18 for (ULONG i = 0; i < size; ++i) { 19 _comSfArr->SetAt(i, _comSfArr->GetAt(i) + 1); 20 } 21 *arr = _comSfArr->Detach(); 22 return S_OK; 23 }
这里实现了对外方法。对字符串的操作是相对容易的,字符串通过类型BSTR来传递,传入时直接赋值即可,传出时则调用CString的AllocSysString()方法。数组是通过SAFEARRAY结构来传递,操作时需要用CComSafeArray代理。CalcArrayValue()的运算过程是:①在数组最后新增一个元素,赋值为0;②前面所有元素值+1。
MyComSvrArrAndStr.idl
1 interface ISimpleDataObject : IDispatch 2 { 3 [id(1)] HRESULT ReceiveMessage([in] BSTR message); 4 [id(2)] HRESULT TransmitMessage([out, retval] BSTR* message); 5 [id(3)] HRESULT CalcArrayValue([in, out] SAFEARRAY(LONG)* arr); 6 };
声明COM对象对外方法,这里注意SAFEARRAY需要指定数组类型,因为我需要int类型数组,所以写成了LONG,并且arr定义为一级指针,尽管它在定义时是二级指针。写到这里就可以编译项目了。
2. C#程序
创建C#控制台项目“CShCallingTest”,在引用中添加COM类型引用“MyComSvrArrAndStrLib”。
Program.cs
1 using System; 2 using MyComSvrArrAndStrLib; 3 4 namespace CshCallingTest 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 SimpleDataObject dataObj = new SimpleDataObject(); 11 Console.WriteLine(dataObj.TransmitMessage()); 12 dataObj.ReceiveMessage("test"); 13 Console.WriteLine(dataObj.TransmitMessage()); 14 Array arr = new int[0]; 15 string str = string.Empty; 16 for (int i = 0; i < 10; ++i) 17 { 18 dataObj.CalcArrayValue(ref arr); 19 Console.WriteLine("length: " + arr.Length.ToString()); 20 foreach (int num in arr) 21 { 22 str += num.ToString() + " "; 23 } 24 Console.WriteLine(str); 25 str = string.Empty; 26 } 27 Console.ReadLine(); 28 } 29 } 30 }
调用COM的对外方法。字符串直接用string就可以,而数组是需要用数组基类Array去定义。这里把同一个数组用CalcArrayValue运算了10次。
3. 输出结果
final construct
test
length: 1
0
length: 2
1 0
length: 3
2 1 0
length: 4
3 2 1 0
length: 5
4 3 2 1 0
length: 6
5 4 3 2 1 0
length: 7
6 5 4 3 2 1 0
length: 8
7 6 5 4 3 2 1 0
length: 9
8 7 6 5 4 3 2 1 0
length: 10
9 8 7 6 5 4 3 2 1 0
附:部分数据类型对照表
idl 接口 | Visual C++ | C# |
BYTE | unsigned char | byte |
SHORT | short | short |
USHORT | unsigned short | ushort |
LONG | long | int |
ULONG | unsigned long | uint |
LONG64 | long long | long |
ULONG64 | unsigned long long | ulong |