浅尝辄止——在C++中调用C#的回调函数——COM方式

这种方式比较简单,给大家分享一下,同时讲一下SafeArray内定义结构体的方法

1. 需求描述

需求是这样的,C++代码和C#代码相互通信(C++一般做服务,C#做客户端),C++一侧准备好数据,然后发给C#端解析,这种需求还是比较常见的。

但是由于数据比较复杂,是一个数据结构,还可能数据结构里套用数据结构,总之数据很复杂(其实我觉得使用xml字符串传递比较理想,兼容性也比较好,但作为程序员有时候没有选择的权利)

2. 定义COM文件,idl文件

对于这个文件不熟悉的自己百度谷歌查查吧。

首先在文件里定义回调接口,也就是C#端需要继承的接口

[object, uuid, oleautomation,helpstring,...] // 内容自己填吧

interface IDataEvent : IUnknown{

  HRESULT OnDataEvent([in] SAFEARRAY(struct SData)* psaDataList);

};

其次定义C++导出接口,也就是C++端需要继承的接口

[object, uuid, dual, nonextensible,...]

interface IDataServer : IDispatch{

  ......

  [id(n)] InitCallback([in] IDataEvent* pCallback);  // 你也可以改为AddListener什么的

};

然后需要定义你的结构体和相关的其他信息

[uuid...]

library DataServer{

  enum EnumType{

    XXX_YYY = 0x01,......

  };

  [uuid...]

  struct SData{

    BSTR name;

    LONGLONG time;  // 使用longlong表示filetime,这个里边不仅有年月日十分秒,还有毫秒信息,更重要的是UTC时间

    VARIANT saValueList;  // 在C#端变成object,根据具体的类型再强转吧,或者在C#端一直保持object类型的状态,你也可以直接使用double或BSTR等类型

    SAFEARAY(xxxx) saList;  // 定义你自己的类型,别忘了xxxx需要定义好哦!

  };

  ......

};

这个文件编译时会生成tlb文件,交给C#直接添加reference就可以啦,如果添加失败,可以手动转换,使用.NET提供的编译工具“Tlbimp”,命令行参考如下

tlbimp myLib.tlb /out:myLib.dll

也可以直接在代码里import这个tlb文件,只不过使用tlb里导出的类型有点不太方便。

3. C#端处理

一般的处理过程是这样的

(1)启动部分,通过各自的手段获得C++端提供的服务对象,将其强转成导出接口对象IDataServer

(2)自己创建一个类用于接受数据 class DataClient : IDataEvent,调用IDataServer的注册回调的函数,将DataClient对象传给C++端

(3)在DataClient类中实现这个函数 public void OnDataEvent(ref Array psaDataList),解析数据吧,遍历Array,将内容强转成SData

这里需要注意的是,一般服务端返回数据时,由于数据量可能比较大,都是通过一个子线程完成的,所以C#端的这个OnDataEvent函数需要判断当前是否是UI线程,如果不是的话,不要直接改变UI,这可能导致UI锁住,正确的做法使用异步处理方式,比如将数据缓存起来,等待UI处理,或者使用UI.InvokeRequired方法与UI抢控制权,然后再处理UI。

4. C++端处理数据

C++端填充数据的过程是这样的

CComPtr<IRecordInfo> pRecord;
GetRecordInfoFromGuids(LIBID_xxx, 1, 0, ::GetUserDefaultLCID(), __uuid(SData), &pRecord);
......
SAFEARAY *psa = ::SafeArrayCreateEx(VT_RECORD, 1, &bound, pRecord);
::SafeArrayAccessData(psa, (void**)&pList);
...... // fill data
::SafeArrayUnaccessData(psa);

CSharpObj->OnDataEvent(&psa); // send to C# object

::SafeArrayAccessData(psa, (void**)&pList);
...... // release data
::SafeArrayUnaccessData(psa);
::SafeArrayDestory(psa);

原文地址:https://www.cnblogs.com/atlaser/p/6247612.html