在C++/CLI环境下,千万不要把普通全局函数当标准C/C++的函数指针传递给native的库使用
先上一个简单代码:
- #include <cstdlib>
- #include <cstdio>
- // native apis
- extern "C"
- {
- typedef void (*CallbackFunction)(int value);
- CallbackFunction GlobalFunction;
- __declspec(dllexport) void NativeSetCallback(CallbackFunction func)
- {
- GlobalFunction = func;
- }
- }
- // test cli
- namespace Project2
- {
- static void CliGlobalCallback(int value)
- {
- System::Console::WriteLine(System::String::Format("{0}", value));
- }
- extern "C"
- {
- static void StandardGlobalCallback(int value)
- {
- printf_s("%d", value);
- }
- }
- }
- extern "C"
- {
- // 这样才可以防止前面的Callback被strip
- __declspec(dllexport) void NativeTestAPI()
- {
- NativeSetCallback(Project2::CliGlobalCallback);
- NativeSetCallback(Project2::StandardGlobalCallback);
- }
- }
从直观感觉上来看,CliGlobalCallback和StandardGlobalCallback的函数签名是一样的,都传递给NativeSetCallback也不会报错,然而实际上这两个函数的签名并不相同。查看生成的二进制代码可以看到如下区别:
- .nep: ; =============== S U B R O U T I N E =======================================
- .nep:
- .nep:
- .nep: ; void __fastcall Project2::`anonymous namespace'::CliGlobalCallback(Project2::_anonymous_namespace_ *__hidden this, int)
- .nep: ?CliGlobalCallback@?A0x1736b8ca@Project2@@YAXH@Z proc near
- .nep: ; DATA XREF: .rdata:__unep@?CliGlobalCallback@?A0x1736b8ca@Project2@@$$FYAXH@Z↓o
- .nep: jmp short loc_18000502A
- .nep: ; ---------------------------------------------------------------------------
- .nep: ud2
- .nep: ; ---------------------------------------------------------------------------
- .nep: jmp cs:__m2mep@?CliGlobalCallback@?A0x1736b8ca@Project2@@$$FYAXH@Z
- .nep:000000018000502A ; ---------------------------------------------------------------------------
- .nep:000000018000502A
- .nep:000000018000502A loc_18000502A: ; CODE XREF: Project2::`anonymous namespace'::CliGlobalCallback(int)↑j
- .nep:000000018000502A jmp cs:__mep@?CliGlobalCallback@?A0x1736b8ca@Project2@@$$FYAXH@Z
- .nep:000000018000502A ?CliGlobalCallback@?A0x1736b8ca@Project2@@YAXH@Z endp
- .nep:000000018000502A
- .nep:
- .nep: ; =============== S U B R O U T I N E =======================================
- .nep:
- .nep:
- .nep: StandardGlobalCallback@0x1736b8ca proc near
- .nep: ; DATA XREF: .rdata:__unep@?StandardGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z↓o
- .nep: jmp short loc_18000503A
- .nep: ; ---------------------------------------------------------------------------
- .nep: ud2
- .nep: ; ---------------------------------------------------------------------------
- .nep: jmp cs:__m2mep@?StandardGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z
- .nep:000000018000503A ; ---------------------------------------------------------------------------
- .nep:000000018000503A
- .nep:000000018000503A loc_18000503A: ; CODE XREF: StandardGlobalCallback@0x1736b8ca↑j
- .nep:000000018000503A jmp cs:__mep@?StandardGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z
- .nep:000000018000503A StandardGlobalCallback@0x1736b8ca endp
- .nep:000000018000503A
CLI版本函数签名中,有一个隐藏的__this,这在调用时,应该是需要有参数来提供的,虽然不是用户手动提供。因此如果将CLI的这个函数作为普通的函数指针传递给Native的函数并作为普通的Native函数指针使用会导致CLI堆损坏,导致极其难查的崩溃,因为崩溃现场跟这里八竿子打不到一块儿去了。
正确的做法是使用extern "C"把它包起来,无论你在函数内部是否访问CLI的代码,都没关系。比如这样:
- // test cli
- namespace Project2
- {
- extern "C"
- {
- static void CliGlobalCallback(int value)
- {
- System::Console::WriteLine(System::String::Format("{0}", value));
- }
- }
- extern "C"
- {
- static void StandardGlobalCallback(int value)
- {
- printf_s("%d", value);
- }
- }
- }
然后再看一下汇编:
- .nep: ; =============== S U B R O U T I N E =======================================
- .nep:
- .nep:
- .nep: CliGlobalCallback@0x1736b8ca proc near ; DATA XREF: .rdata:__unep@?CliGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z↓o
- .nep: jmp short loc_18000502A
- .nep: ; ---------------------------------------------------------------------------
- .nep: ud2
- .nep: ; ---------------------------------------------------------------------------
- .nep: jmp cs:__m2mep@?CliGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z
- .nep:000000018000502A ; ---------------------------------------------------------------------------
- .nep:000000018000502A
- .nep:000000018000502A loc_18000502A: ; CODE XREF: CliGlobalCallback@0x1736b8ca↑j
- .nep:000000018000502A jmp cs:__mep@?CliGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z
- .nep:000000018000502A CliGlobalCallback@0x1736b8ca endp
- .nep:000000018000502A
- .nep:
- .nep: ; =============== S U B R O U T I N E =======================================
- .nep:
- .nep:
- .nep: StandardGlobalCallback@0x1736b8ca proc near
- .nep: ; DATA XREF: .rdata:__unep@?StandardGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z↓o
- .nep: jmp short loc_18000503A
- .nep: ; ---------------------------------------------------------------------------
- .nep: ud2
- .nep: ; ---------------------------------------------------------------------------
- .nep: jmp cs:__m2mep@?StandardGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z
- .nep:000000018000503A ; ---------------------------------------------------------------------------
- .nep:000000018000503A
- .nep:000000018000503A loc_18000503A: ; CODE XREF: StandardGlobalCallback@0x1736b8ca↑j
- .nep:000000018000503A jmp cs:__mep@?StandardGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z
- .nep:000000018000503A StandardGlobalCallback@0x1736b8ca endp
- .nep:000000018000503A
- .nep:
- .nep: ; =============== S U B R O U T I N E =======================================
- .nep:
- .nep:
这样再传递给Native就稳了。
在C++/CLI环境下,千万不要把普通全局函数当标准C/C++的函数指针传递给native的库使用的更多相关文章
- [PHP] PHP在CLI环境下的错误日志
1.display_errors = Off;//控制php是否输出错误;在生产环境中输出会泄露敏感信息;建议记录错误而不是将它们发送到STDOUToff :不显示任何错误;stderr :向STDE ...
- [PHP] cli环境下php设置进程名字
if (function_exists('cli_set_process_title')) { cli_set_process_title("superman php master proc ...
- 多线程编程之Linux环境下的多线程(一)
一.Linux环境下的线程 相对于其他操作系统,Linux系统内核只提供了轻量级进程的支持,并未实现线程模型.Linux是一种“多进程单线程”的操作系统,Linux本身只有进程的概念,而其所谓的“线程 ...
- 腾讯云CMQ消息队列在Windows环境下的使用
版权声明:本文由李少华原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/100 来源:腾云阁 https://www.qclo ...
- 在SAP云平台的CloudFoundry环境下消费ABAP On-Premise OData服务
我的前一篇文章 使用Java+SAP云平台+SAP Cloud Connector调用ABAP On-Premise系统里的函数介绍了在SAP云平台的Neo环境下如何通过SAP Cloud Conne ...
- PHP 命令行模式实战之cli+mysql 模拟队列批量发送邮件(在Linux环境下PHP 异步执行脚本发送事件通知消息实际案例)
源码地址:https://github.com/Tinywan/PHP_Experience 测试环境配置: 环境:Windows 7系统 .PHP7.0.Apache服务器 PHP框架:ThinkP ...
- Atititcmd cli环境变量的调用设置与使用
Atititcmd cli环境变量的调用设置与使用 1.1. Cgi 环境变量的调用设置与使用1 1.2. 环境变量vs 系统变量1 1.3. 环境变量的分类 A.与服务器相关的环境变量B ,与客户 ...
- php cli方式下获取服务器ip
(未整理....) (1)php cli方式下获取服务器ip [php] function getServerIp(){ $ss = exec('/sbin/ifconfig et ...
- linux环境下给文件加密/解密的方法
原文地址:linix环境下给文件加密/解密的方法 作者:oracunix 一. 利用 vim/vi 加密:优点:加密后,如果不知道密码,就看不到明文,包括root用户也看不了:缺点:很明显让别人知 ...
随机推荐
- 关于SqlServer那些事1(回归基础)
即将实习,回归基础总结,希望可以再好好打磨一下基础的一些东西 关于如何在重新修改表结构时该变其权限设置 步骤: 点击工具 进入选项 设计器 取消勾选阻止保存要求重新创建表的更改 关于创建创建数据库以及 ...
- python6.2类的封装
class Card(object): def __init__(self,num,pwd,ban): self.num=num#卡号 self.pwd=pwd#密码 self.__ban=ban#余 ...
- Hotspot GC研发工程师也许漏掉了一块逻辑
本文来自: PerfMa技术社区 PerfMa(笨马网络)官网 概述 今天要说的这个问题,是我经常面试问的一个问题,只是和我之前排查过的场景有些区别,属于另外一种情况.也许我这里讲了这个之后,会成为不 ...
- C语言学习笔记之进制之间的转换
这一篇主要是对进制之间转换的讲解,方便查看,以防忘记 二进制 逢二进一 八进制 逢八进一 以0开头, 0就是8进制的标志 十进制 逢十进一 ...
- Dubbo系列之 (一)SPI扩展
一.基础铺垫 1.@SPI .@Activate. @Adaptive a.对于 @SPI,Dubbo默认的特性扩展接口,都必须打上这个@SPI,标识这是个Dubbo扩展点.如果自己需要新增dubbo ...
- 【JAVA】java中int转成String位数不足前面补零例如:1->001
String.format("%03d", 1); 0代表前面要补的字符3代表字符串长度d表示参数为整数类型 测试完数据:循环了100次 截取了一部分:
- 初识HTML(二)
目录 HTML表格 HTML列表 HTML表格 表格主要显示.展示数据. 表格基本语法:<table>定义一个表格,<tr>定义表格中的一行,<td>定义一行中的某 ...
- C#LeetCode刷题之#598-范围求和 II(Range Addition II)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3881 访问. 给定一个初始元素全部为 0,大小为 m*n 的矩阵 ...
- JavaScript Babel说明
babel插件只是去唤醒 @babel/core中的转换过程 转换模块需要手动安装 npm install @babel/core 转换方式需要安装 @babel/preset-env babel默认 ...
- LeetCode 90 | 经典递归问题,求出所有不重复的子集II
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode专题第56篇文章,我们一起来看看LeetCode第90题,子集II(Subsets II). 这题的官方难度是Medi ...