先上一个简单代码:

#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的库使用的更多相关文章

  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. demo1 动态显示view或弹框 动态隐藏view或弹框

    实现界面如上所示: 有一个弹框,弹框上边有一个关闭按钮,点击按钮,可以关闭弹框.点击弹框的周围区域也可以关闭按钮. 点击上边的隐藏弹框也可以关闭按钮. 在实现功能的基础上,以动画的形式展示跟隐藏. 思 ...

  2. python 版本 jaeger-client 导入失败 jaeger-client-python

    环境为: OS: ubuntu18.04 Python: 3.6 问题原因: 尝试使用 jaeger-client-python,官方给出的示例(https://github.com/jaegertr ...

  3. QComboBox设置下拉item大小

    1.首先给ComboBox设置view: ui->comboBox->setView(new QListView()); 注意:这一句需要放在在所在界面的构造函数里,否则不生效. 2.给c ...

  4. Python Tricks —— 使用 pywinrm 远程控制 Windows 主机

    启用 WinRM 远程服务: winrm quickconfig 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不 ...

  5. 不能错过的分布式ID生成器(Leaf ),好用的一批!

    本文收录在个人博客:www.chengxy-nds.top,技术资料共享,同进步 不了解分布式ID的同学,先行去看<一口气说出 9种 分布式ID生成方式,面试官有点懵了>温习一下基础知识, ...

  6. Java Filter过滤器(拦截路径的配置+拦截方式的配置+生命周期+多个过滤器的先后执行顺序)

    Java Filter过滤器+Listen监听器 啥是过滤器 顾名思义即过滤掉一些东西,比如我们经历的高考中考都是过滤器,他过滤掉一些在学习这一方面不是很好的人,而那些成绩好的人则升入高中,大学. 但 ...

  7. SpringCloudAlibaba-服务容错Sentinel(入门)

    一:高并发带来的问题? 在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用,但是由于网络原因或者自身的原因,服务并不能保证服务的100%可用,如果单个服务出现问题,调用这个服务就 ...

  8. Webpack 定义process.env的时机

    定义 process.env的时机 如果已经提取了公共配置文件 webpack.common.js 分别定义了开发配置webpack.dev.js和生产配置webpack.prod.js 在webpa ...

  9. (数据科学学习手札93)利用geopandas与PostGIS进行交互

    本文完整代码及数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 PostGIS作为postgresql针对 ...

  10. Golang 解析Yaml格式

    Golang官方并没有提供Yaml解析包,所以需要使用第三方包.可用的第三方包有不少,这里选择的是 gopkg.in/yaml.v2,这个包在github上有不少的star,也的确挺好用.其使用的是A ...