在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用户也看不了:缺点:很明显让别人知 ...
随机推荐
- 2017面向对象程序设计(Java)第五周工作总结
时光如逝,岁月如梭,不知不觉已经开学五个星期了.在代老师的带领下,我们一步一步走近Java,也渐渐的适应了翻转课堂的个性化教学,此时此刻相信同学们对Java也有了更加深入的了解.下面我对第五周的助教工 ...
- GPS位置显示在地图上
源码分析博客地址:https://blog.csdn.net/wuquan_1230/article/details/79614974 工具下载地址:http://download.csdn.net/ ...
- “随手记”开发记录day11
今天,我们团队针对每个团队对我们项目的意见或建议进行了探讨,决定了接下来需要改进的地方: (1)背景图片和颜色搭配: (2)增加修改功能: (3)对功能进行更明显划分,让使用者能够更方便的使用.
- PyQt QML
- JDBC的开发步骤
一.JDBC概述 JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问, 它由一组用Jav ...
- Win10系统Jmeter+maven+Jenkins接口自动化环境搭建(一)
Jmeter+maven+Jenkins实现接口自动化,需要使用idea或eclipse配置maven项目,这里我使用的是idea.具体步骤如下: 1.安装jmeter+jdk jmeter安装之前需 ...
- 数据结构C++使用邻接表实现图
定义邻接表存储的图类.[实验要求] (1)创建一个邻接表存储的图:(2)返回图中指定边的权值:(3)插入操作:向图中插入一个顶点,插入一条边:(4)删除操作:从图中删除一个顶点,删除一条边:(5)图的 ...
- P1616疯狂的采药 完全背包
题目背景 此题为纪念 LiYuxiang 而生. 题目描述 LiYuxiang 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师.为此,他想拜附近最有威望的医师为师.医师为了判断他的资质,给他出了 ...
- C#LeetCode刷题之#566-重塑矩阵( Reshape the Matrix)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3720 访问. 在MATLAB中,有一个非常有用的函数 resha ...
- 《JavaScript语言入门教程》记录整理:面向对象
目录 面向对象编程 实例对象与 new 命令 this关键字 对象的继承 Object对象的方法 严格模式(strict mode) 本系列基于阮一峰老师的<JavaScrip语言入门教程> ...