C#程序与COM服务程序传递数组和字符串的方式

发布时间 2023-10-12 17:15:44作者: 永恒月华

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