在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就稳了。

 

posted @ 2020-08-27 10:40  bodong  阅读(225)  评论(0编辑  收藏  举报