这种方式比较简单,给大家分享一下,同时讲一下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);

浅尝辄止——在C++中调用C#的回调函数——COM方式的更多相关文章

  1. 关于as中的事件与回调函数

    对于Observer模式, 在as中object(被观察者)既可以用事件(event),也可以用回调函数(caller)来通知观察者(observer).那在实际的开发中到底应该选择用event还是用 ...

  2. C语言中的回调函数(Callback Function)

    1 定义和使用场合 回调函数是指 使用者自己定义一个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数.函数是你实现 ...

  3. [转]理解与使用Javascript中的回调函数

    在Javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用.既然函数实际上是对象:它们能被“存储”在变量中,能作为函数参数被传递,能在函数中被创建,能从函数中返回. 因 ...

  4. C++中回调函数(CallBack)的使用

    如果试图直接使用C++的成员函数作为回调函数将发生错误,甚至编译就不能通过. 其错误是普通的C++成员函数都隐含了一个传递函数作为参数,亦即“this”指针,C++通过传递this指针给其成员函数从而 ...

  5. 【JavaScript】理解与使用Javascript中的回调函数

    在Javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用.既然函数实际上是对象:它们能被“存储”在变量中,能作为函数参数被传递,能在函数中被创建,能从函数中返回. 因 ...

  6. C语言中的回调函数

    C语言中通过函数指针实现回调函数(Callback Function) ====== 首先使用typedef定义回调函数类型 ======  typedef void (*event_cb_t)(co ...

  7. 理解 JavaScript 回调函数并使用

    JavaScript中,函数是一等(first-class)对象:也就是说,函数是 Object 类型并且可以像其他一等对象(String,Array,Number等)一样使用.它们可以"保 ...

  8. C++ 回调函数的定义与用法

    一回调函数 我们经常在C++设计时通过使用回调函数可以使有些应用(如定时器事件回调处理.用回调函数记录某操作进度等)变得非常方便和符合逻辑,那么它的内在机制如何呢,怎么定义呢?它和其它函数(比如钩子函 ...

  9. 通过修改i8042prt端口驱动中类驱动Kbdclass的回调函数地址,达到过滤键盘操作的例子

    同样也是寒江独钓的例子,但只给了思路,现贴出实现代码 原理是通过改变端口驱动中本该调用类驱动回调函数的地方下手 //替换分发函数 来实现过滤 #include <wdm.h> #inclu ...

随机推荐

  1. Scrum Meeting ——总结

    冲刺总结 0*.燃尽图 迟来的燃尽图,别看它是最后一天掉了一堆,感觉很假,像是人为的把issues都关闭掉.其实不然,很多功能是大家平时做好,但是没整合在一起,所以没燃掉,在最后几天的整合中,通过测试 ...

  2. winform中messageBox七个参数的使用(转载)

    private void button1_Click(object sender, EventArgs e) { MessageBox.Show(" 1 个参数 ”); } private ...

  3. curl+openssl编译

    curl不支持openssl的静态库,所以编译openssl的时候,应该加上shared 参数,记录一下我亲手编译的参数: ./configure --prefix=/usr/local/openss ...

  4. E. Santa Claus and Tangerines 二分答案 + 记忆化搜索

    http://codeforces.com/contest/752/problem/E 首先有一个东西就是,如果我要检测5,那么14我们认为它能产生2个5. 14 = 7 + 7.但是按照平均分的话, ...

  5. java 接收 char字符型

    import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.Scanner; public clas ...

  6. 【转载】PMC/PEC Boundary Conditions and Plane Wave Simulation

    原文链接 PMC/PEC Boundary Conditions and Plane Wave Simulation (FDTD) OptiFDTD now has options to use Pe ...

  7. Train Problem I 分类: HDU 2015-06-26 11:27 10人阅读 评论(0) 收藏

    Train Problem I Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  8. 高精度计算的类(BigInteger和BigDecimal)

    这两个类 在Java中没有对应的基本类型.不过,这两个类包含的方法,提供的操作与对基本类型所能执行的操作差不多. 也就是说,能对基本类型 int float 等的操作,也同样能作用于这两个类,只不过必 ...

  9. 237. Delete Node in a Linked List(C++)

    237. Delete Node in a Linked Lis t Write a function to delete a node (except the tail) in a singly l ...

  10. VB.net 连接池

    上篇博客介绍了临时表的使用,以及它的生命周期和连接池的关系.那么为了能更好的把握临时表的产生和消亡,本篇博客就介绍Vb.net连接池.在打开和关闭数据库连接时的耗费的资源是非常高的.那么在程序需要频繁 ...