先上一个简单代码:

  1. #include <cstdlib>
  2. #include <cstdio>
  3.  
  4. // native apis
  5. extern "C"
  6. {
  7. typedef void (*CallbackFunction)(int value);
  8.  
  9. CallbackFunction GlobalFunction;
  10. __declspec(dllexport) void NativeSetCallback(CallbackFunction func)
  11. {
  12. GlobalFunction = func;
  13. }
  14. }
  15.  
  16. // test cli
  17. namespace Project2
  18. {
  19. static void CliGlobalCallback(int value)
  20. {
  21. System::Console::WriteLine(System::String::Format("{0}", value));
  22. }
  23.  
  24. extern "C"
  25. {
  26. static void StandardGlobalCallback(int value)
  27. {
  28. printf_s("%d", value);
  29. }
  30. }
  31. }
  32.  
  33. extern "C"
  34. {
  35. // 这样才可以防止前面的Callback被strip
  36. __declspec(dllexport) void NativeTestAPI()
  37. {
  38. NativeSetCallback(Project2::CliGlobalCallback);
  39. NativeSetCallback(Project2::StandardGlobalCallback);
  40. }
  41. }

从直观感觉上来看,CliGlobalCallback和StandardGlobalCallback的函数签名是一样的,都传递给NativeSetCallback也不会报错,然而实际上这两个函数的签名并不相同。查看生成的二进制代码可以看到如下区别:

  1. .nep: ; =============== S U B R O U T I N E =======================================
  2. .nep:
  3. .nep:
  4. .nep: ; void __fastcall Project2::`anonymous namespace'::CliGlobalCallback(Project2::_anonymous_namespace_ *__hidden this, int)
  5. .nep: ?CliGlobalCallback@?A0x1736b8ca@Project2@@YAXH@Z proc near
  6. .nep: ; DATA XREF: .rdata:__unep@?CliGlobalCallback@?A0x1736b8ca@Project2@@$$FYAXH@Z↓o
  7. .nep: jmp short loc_18000502A
  8. .nep: ; ---------------------------------------------------------------------------
  9. .nep: ud2
  10. .nep: ; ---------------------------------------------------------------------------
  11. .nep: jmp cs:__m2mep@?CliGlobalCallback@?A0x1736b8ca@Project2@@$$FYAXH@Z
  12. .nep:000000018000502A ; ---------------------------------------------------------------------------
  13. .nep:000000018000502A
  14. .nep:000000018000502A loc_18000502A: ; CODE XREF: Project2::`anonymous namespace'::CliGlobalCallback(int)↑j
  15. .nep:000000018000502A jmp cs:__mep@?CliGlobalCallback@?A0x1736b8ca@Project2@@$$FYAXH@Z
  16. .nep:000000018000502A ?CliGlobalCallback@?A0x1736b8ca@Project2@@YAXH@Z endp
  17. .nep:000000018000502A
  18. .nep:
  19. .nep: ; =============== S U B R O U T I N E =======================================
  20. .nep:
  21. .nep:
  22. .nep: StandardGlobalCallback@0x1736b8ca proc near
  23. .nep: ; DATA XREF: .rdata:__unep@?StandardGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z↓o
  24. .nep: jmp short loc_18000503A
  25. .nep: ; ---------------------------------------------------------------------------
  26. .nep: ud2
  27. .nep: ; ---------------------------------------------------------------------------
  28. .nep: jmp cs:__m2mep@?StandardGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z
  29. .nep:000000018000503A ; ---------------------------------------------------------------------------
  30. .nep:000000018000503A
  31. .nep:000000018000503A loc_18000503A: ; CODE XREF: StandardGlobalCallback@0x1736b8ca↑j
  32. .nep:000000018000503A jmp cs:__mep@?StandardGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z
  33. .nep:000000018000503A StandardGlobalCallback@0x1736b8ca endp
  34. .nep:000000018000503A

CLI版本函数签名中,有一个隐藏的__this,这在调用时,应该是需要有参数来提供的,虽然不是用户手动提供。因此如果将CLI的这个函数作为普通的函数指针传递给Native的函数并作为普通的Native函数指针使用会导致CLI堆损坏,导致极其难查的崩溃,因为崩溃现场跟这里八竿子打不到一块儿去了。

正确的做法是使用extern "C"把它包起来,无论你在函数内部是否访问CLI的代码,都没关系。比如这样:

  1. // test cli
  2. namespace Project2
  3. {
  4. extern "C"
  5. {
  6. static void CliGlobalCallback(int value)
  7. {
  8. System::Console::WriteLine(System::String::Format("{0}", value));
  9. }
  10. }
  11.  
  12. extern "C"
  13. {
  14. static void StandardGlobalCallback(int value)
  15. {
  16. printf_s("%d", value);
  17. }
  18. }
  19. }

然后再看一下汇编:

  1. .nep: ; =============== S U B R O U T I N E =======================================
  2. .nep:
  3. .nep:
  4. .nep: CliGlobalCallback@0x1736b8ca proc near ; DATA XREF: .rdata:__unep@?CliGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Zo
  5. .nep: jmp short loc_18000502A
  6. .nep: ; ---------------------------------------------------------------------------
  7. .nep: ud2
  8. .nep: ; ---------------------------------------------------------------------------
  9. .nep: jmp cs:__m2mep@?CliGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z
  10. .nep:000000018000502A ; ---------------------------------------------------------------------------
  11. .nep:000000018000502A
  12. .nep:000000018000502A loc_18000502A: ; CODE XREF: CliGlobalCallback@0x1736b8caj
  13. .nep:000000018000502A jmp cs:__mep@?CliGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z
  14. .nep:000000018000502A CliGlobalCallback@0x1736b8ca endp
  15. .nep:000000018000502A
  16. .nep:
  17. .nep: ; =============== S U B R O U T I N E =======================================
  18. .nep:
  19. .nep:
  20. .nep: StandardGlobalCallback@0x1736b8ca proc near
  21. .nep: ; DATA XREF: .rdata:__unep@?StandardGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Zo
  22. .nep: jmp short loc_18000503A
  23. .nep: ; ---------------------------------------------------------------------------
  24. .nep: ud2
  25. .nep: ; ---------------------------------------------------------------------------
  26. .nep: jmp cs:__m2mep@?StandardGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z
  27. .nep:000000018000503A ; ---------------------------------------------------------------------------
  28. .nep:000000018000503A
  29. .nep:000000018000503A loc_18000503A: ; CODE XREF: StandardGlobalCallback@0x1736b8caj
  30. .nep:000000018000503A jmp cs:__mep@?StandardGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z
  31. .nep:000000018000503A StandardGlobalCallback@0x1736b8ca endp
  32. .nep:000000018000503A
  33. .nep:
  34. .nep: ; =============== S U B R O U T I N E =======================================
  35. .nep:
  36. .nep:

这样再传递给Native就稳了。

在C++/CLI环境下,千万不要把普通全局函数当标准C/C++的函数指针传递给native的库使用的更多相关文章

  1. [PHP] PHP在CLI环境下的错误日志

    1.display_errors = Off;//控制php是否输出错误;在生产环境中输出会泄露敏感信息;建议记录错误而不是将它们发送到STDOUToff :不显示任何错误;stderr :向STDE ...

  2. [PHP] cli环境下php设置进程名字

    if (function_exists('cli_set_process_title')) { cli_set_process_title("superman php master proc ...

  3. 多线程编程之Linux环境下的多线程(一)

    一.Linux环境下的线程 相对于其他操作系统,Linux系统内核只提供了轻量级进程的支持,并未实现线程模型.Linux是一种“多进程单线程”的操作系统,Linux本身只有进程的概念,而其所谓的“线程 ...

  4. 腾讯云CMQ消息队列在Windows环境下的使用

    版权声明:本文由李少华原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/100 来源:腾云阁 https://www.qclo ...

  5. 在SAP云平台的CloudFoundry环境下消费ABAP On-Premise OData服务

    我的前一篇文章 使用Java+SAP云平台+SAP Cloud Connector调用ABAP On-Premise系统里的函数介绍了在SAP云平台的Neo环境下如何通过SAP Cloud Conne ...

  6. PHP 命令行模式实战之cli+mysql 模拟队列批量发送邮件(在Linux环境下PHP 异步执行脚本发送事件通知消息实际案例)

    源码地址:https://github.com/Tinywan/PHP_Experience 测试环境配置: 环境:Windows 7系统 .PHP7.0.Apache服务器 PHP框架:ThinkP ...

  7. Atititcmd cli环境变量的调用设置与使用

    Atititcmd cli环境变量的调用设置与使用 1.1. Cgi 环境变量的调用设置与使用1 1.2. 环境变量vs  系统变量1 1.3. 环境变量的分类 A.与服务器相关的环境变量B ,与客户 ...

  8. php cli方式下获取服务器ip

    (未整理....) (1)php cli方式下获取服务器ip [php]  function getServerIp(){          $ss = exec('/sbin/ifconfig et ...

  9. linux环境下给文件加密/解密的方法

      原文地址:linix环境下给文件加密/解密的方法 作者:oracunix 一. 利用 vim/vi 加密:优点:加密后,如果不知道密码,就看不到明文,包括root用户也看不了:缺点:很明显让别人知 ...

随机推荐

  1. 关于SqlServer那些事1(回归基础)

    即将实习,回归基础总结,希望可以再好好打磨一下基础的一些东西 关于如何在重新修改表结构时该变其权限设置 步骤: 点击工具 进入选项 设计器 取消勾选阻止保存要求重新创建表的更改 关于创建创建数据库以及 ...

  2. python6.2类的封装

    class Card(object): def __init__(self,num,pwd,ban): self.num=num#卡号 self.pwd=pwd#密码 self.__ban=ban#余 ...

  3. Hotspot GC研发工程师也许漏掉了一块逻辑

    本文来自: PerfMa技术社区 PerfMa(笨马网络)官网 概述 今天要说的这个问题,是我经常面试问的一个问题,只是和我之前排查过的场景有些区别,属于另外一种情况.也许我这里讲了这个之后,会成为不 ...

  4. C语言学习笔记之进制之间的转换

    这一篇主要是对进制之间转换的讲解,方便查看,以防忘记 二进制      逢二进一 八进制      逢八进一                以0开头, 0就是8进制的标志 十进制      逢十进一 ...

  5. Dubbo系列之 (一)SPI扩展

    一.基础铺垫 1.@SPI .@Activate. @Adaptive a.对于 @SPI,Dubbo默认的特性扩展接口,都必须打上这个@SPI,标识这是个Dubbo扩展点.如果自己需要新增dubbo ...

  6. 【JAVA】java中int转成String位数不足前面补零例如:1->001

    String.format("%03d", 1); 0代表前面要补的字符3代表字符串长度d表示参数为整数类型 测试完数据:循环了100次 截取了一部分:

  7. 初识HTML(二)

    目录 HTML表格 HTML列表 HTML表格 表格主要显示.展示数据. 表格基本语法:<table>定义一个表格,<tr>定义表格中的一行,<td>定义一行中的某 ...

  8. C#LeetCode刷题之#598-范围求和 II​​​​​​​(Range Addition II)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3881 访问. 给定一个初始元素全部为 0,大小为 m*n 的矩阵 ...

  9. JavaScript Babel说明

    babel插件只是去唤醒 @babel/core中的转换过程 转换模块需要手动安装 npm install @babel/core 转换方式需要安装 @babel/preset-env babel默认 ...

  10. LeetCode 90 | 经典递归问题,求出所有不重复的子集II

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode专题第56篇文章,我们一起来看看LeetCode第90题,子集II(Subsets II). 这题的官方难度是Medi ...