• 在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:0000000180005020 ; =============== S U B R O U T I N E =======================================
    .nep:0000000180005020
    .nep:0000000180005020
    .nep:0000000180005020 ; void __fastcall Project2::`anonymous namespace'::CliGlobalCallback(Project2::_anonymous_namespace_ *__hidden this, int)
    .nep:0000000180005020 ?CliGlobalCallback@?A0x1736b8ca@Project2@@YAXH@Z proc near
    .nep:0000000180005020                                         ; DATA XREF: .rdata:__unep@?CliGlobalCallback@?A0x1736b8ca@Project2@@$$FYAXH@Z↓o
    .nep:0000000180005020                 jmp     short loc_18000502A
    .nep:0000000180005022 ; ---------------------------------------------------------------------------
    .nep:0000000180005022                 ud2
    .nep:0000000180005024 ; ---------------------------------------------------------------------------
    .nep:0000000180005024                 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:0000000180005030
    .nep:0000000180005030 ; =============== S U B R O U T I N E =======================================
    .nep:0000000180005030
    .nep:0000000180005030
    .nep:0000000180005030 StandardGlobalCallback@0x1736b8ca proc near
    .nep:0000000180005030                                         ; DATA XREF: .rdata:__unep@?StandardGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z↓o
    .nep:0000000180005030                 jmp     short loc_18000503A
    .nep:0000000180005032 ; ---------------------------------------------------------------------------
    .nep:0000000180005032                 ud2
    .nep:0000000180005034 ; ---------------------------------------------------------------------------
    .nep:0000000180005034                 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:0000000180005020 ; =============== S U B R O U T I N E =======================================
    .nep:0000000180005020
    .nep:0000000180005020
    .nep:0000000180005020 CliGlobalCallback@0x1736b8ca proc near  ; DATA XREF: .rdata:__unep@?CliGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z↓o
    .nep:0000000180005020                 jmp     short loc_18000502A
    .nep:0000000180005022 ; ---------------------------------------------------------------------------
    .nep:0000000180005022                 ud2
    .nep:0000000180005024 ; ---------------------------------------------------------------------------
    .nep:0000000180005024                 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:0000000180005030
    .nep:0000000180005030 ; =============== S U B R O U T I N E =======================================
    .nep:0000000180005030
    .nep:0000000180005030
    .nep:0000000180005030 StandardGlobalCallback@0x1736b8ca proc near
    .nep:0000000180005030                                         ; DATA XREF: .rdata:__unep@?StandardGlobalCallback@?A0x1736b8ca@@$$J0YAXH@Z↓o
    .nep:0000000180005030                 jmp     short loc_18000503A
    .nep:0000000180005032 ; ---------------------------------------------------------------------------
    .nep:0000000180005032                 ud2
    .nep:0000000180005034 ; ---------------------------------------------------------------------------
    .nep:0000000180005034                 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:0000000180005040
    .nep:0000000180005040 ; =============== S U B R O U T I N E =======================================
    .nep:0000000180005040
    .nep:0000000180005040

               这样再传递给Native就稳了。

  • 相关阅读:
    安装django(使用国内镜像)
    adb工具安装与配置
    mysql
    Charles抓包(iOS的http/https请求)
    selenium+python第一个自动化脚本
    selenium+python环境搭建
    python,你好!
    tjkd-html
    asp
    uglifyjs压缩批处理
  • 原文地址:https://www.cnblogs.com/bodong/p/13570047.html
Copyright © 2020-2023  润新知