先上一个简单代码:

#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. 3月21日考试 题解(数据结构+区间DP+贪心)

    前言:T3写挂了,有点难受. --------------- T1 中位数 题意简述:给你一段长度为$n$的序列,分别输出$[1,2k-1]$的中位数$(2k-1\leq n)$. --------- ...

  2. spring data jap的使用 1

    最近一直在研究Spring Boot,今天为大家介绍下Spring Data JPA在Spring Boot中的应用,如有错误,欢迎大家指正. 先解释下什么是JPA JPA就是一个基于O/R映射的标准 ...

  3. 当你的系统依赖于某个bug...

    标题粗略看是有点违反常识的,bug通常是指某些代码存在问题导致系统没有按照期望方式工作,应该是需要尽可能被修复的,这样系统才会正常工作.但是,开发实践中会发现在某些情况下,本来功能没有问题,在你信心满 ...

  4. 【Python笔记】2020年7月22日练习=[定义一个函数quadratic(a, b, c),接收3个参数,返回一元二次方程的两个解]

    学习教程:廖雪峰-Python教程-函数-函数定义 学习记录:[定义一个函数quadratic(a, b, c),接收3个参数,返回一元二次方程的两个解] 学习心得: 1.对问题进行判断分析后再下手. ...

  5. 08 vi全屏文本编辑器

    打开文件vim /path/to/somefilevim +# :打开文件,并定位于第#行(如下的 vi +33 profile) vim +:打开文件,定位至最后一行vim +/PATTERN : ...

  6. java Eclipse刷新报错 Feature 'taglib' not found.

    刷新工程报错:org.eclipse.emf.ecore.xmi.FeatureNotFoundException: Feature 'taglib' not found. 错误原因:tomcat7, ...

  7. java List接口一

    一 List接口概述 查阅API,看List的介绍.有序的 collection(也称为序列).此接口的用户可以对列表中每个元素的 插入位置进行精确地控制.用户可以根据元素的整数索引(在列表中的位置) ...

  8. 【期外】(三)Linux下集成开发环境Geany

    今天小编发现了一个很好的软件,它的名字就叫做Geany. 这是Linux系统中的开发工具,相当的好用. Linux与windows最大的不同正是不是集成开发环境,所以写起代码来总是用文档写好后,然后再 ...

  9. 【NOIP必备攻略】 基本noilinux使用方法

    现在linux系统已经成为了NOIP竞赛的一大操作系统,如果连最基础的操作都不会,那就更别提怎么得分了,万一操作失误,可就爆零了.所以小编特意发这样一篇博客,教你快速上手noilinux! ▎ 常用操 ...

  10. python 01 print input int

    学过c语言与c语言的数据结构与算法后再来学习python,感觉编程的核心内容没有变,但每个编程语言都有自己的特点.本次学习的目标是理解python的特点与用法,把学过的bif(内置函数)用法记录下来, ...