在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用户也看不了:缺点:很明显让别人知 ...
随机推荐
- 学习java 线程池-1: ThreadPoolExecutor
1. Executor 该接口内只有一个接口方法 :该方法的目的就是执行指定的 Runnable (但会不会执行,或者会不会立马执行,则不一定.因为要取决于整个线程池的状态) Executor 中文的 ...
- 【python设计模式-创建型】工厂方法模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻 ...
- CSS-flex|gird 布局
网页布局.css *{ box-sizing: border-box; } /* flex弹性布局 */ #flex-container { display: flex; flex-direction ...
- Jupyter PPT
安装 pip install jupyter pip install RISE jupyter-nbextension install rise --py --sys-prefix jupyter-n ...
- CentOS7 安装 Nexus
CentOS7 安装 Nexus 所需软件包 jdk-8u231-linux-x64.tar.gz nexus-3.24.0-02-unix.tar.gz 创建安装目录 mkdir -p /opt/n ...
- 文件上传Upload 漏洞挖掘思路
1:尽可能多的找出网站存在的上传点2:尝试使用如上各种绕过方法3:尝试 geshell4:无法上传webshel的情况下: 尝试上传html等,或可造成存储XSS漏洞 上传点构造XSS等,结合上传后的 ...
- [机器学习 ]PCA降维--两种实现 : SVD或EVD. 强力总结. 在鸢尾花数据集(iris)实做
PCA降维--两种实现 : SVD或EVD. 强力总结. 在鸢尾花数据集(iris)实做 今天自己实现PCA,从网上看文章的时候,发现有的文章没有搞清楚把SVD(奇异值分解)实现和EVD(特征值分解) ...
- Linux下如何高效切换目录?
Linux 下对于目录的切换,大家肯定会想到一个命令:cd 命令.这个是 Linux 下再基本不过的命令,如果这个命令都不知道的话,赶紧剖腹自尽去吧. cd 命令确实很方便,但如果需要频繁在下面的目录 ...
- Oracle用户授权
一.用户授权 1)普通权限 grant ${autoType1, autoType2, autoType3, ...} to ${userName} identified by ${password} ...
- Ubuntu 18.04 安装、配置和美化
本文章修改自inkss的博文 为什么要修改原文? 本来我是想自己写这么一篇的,但是没想到在百度一搜,居然一篇好文.我之前没看到,真是可惜. 这篇文章主要是帮助我记录一些东西,如题. 原文虽然不错,但是 ...