通过具有 Run-Time Check Failure #0 错误的 DLL 函数指针的回调函数

Callback function with function pointer through DLL with Run-Time Check Failure #0 error

我有两个由 C/C++ 编写的项目。 项目 1 输出为 exe 文件并命名为 MyProject。 项目 2 输出是 dll 文件并命名为 Bridge。 我尝试让 Bridge 执行 MyProject 中的函数。该功能似乎有效,但我遇到错误

"Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention."

我怀疑根本原因可能发生在__cdecl __stdcall约定中,但仍然不知道如何解决。项目所有的Calling convention设置都是__cdecl(/Gd),VSIDE是VS 2013.

DLL 项目(桥)代码

Header

 typedef void(*sfcTrace)(const char *logLevel, const char *logMessage);

    class OnRequestHandler
    {
    public:
        virtual void writeLog(sfcTrace callback) = 0;
    };

    class GenericInfoHandler : public OnRequestHandler
    {
    public:

        GenericInfoHandler();
        ~GenericInfoHandler() { delete this; };
        void writeLog(sfcTrace callback);

    };

    extern "C" __declspec (dllexport) OnRequestHandler* __cdecl oneBridgeCallBack()
    {
        return new GenericInfoHandler;
    }

CPP

void GenericInfoHandler::writeLog(sfcTrace callback)
{
    const char *level = "DEBUG";
    const char *message = "TEST";
    callback(level, message);
}

MyProject源代码:

    typedef OnRequestHandler* (__cdecl *test)();
    HINSTANCE getDLL = LoadLibrary("Bridge.dll");

    if (!getDLL)
    {
        cout << "Cannot not load DLL." << endl;
    }

    test func = (test)::GetProcAddress(getDLL, "oneBridgeCallBack");

    if (!func)
    {
        cout << "Cannot not locate the function." << endl;
    }

    OnRequestHandler* instance = func();
    instance->writeLog(&MyProject::TestCallBack); <----- Error Occurs here

MyProject 中的函数实现

void MyProject::TestCallBack(const char *level, const char *message)
{
    if (strcmp(level, "INFO") == 0){
        // do something
    }
    else if (strcmp(level, "DEBUG") == 0){
        // do something
    }

}

Header:

typedef void(MyProject::*TestCallBack)(const char *logLevel, const char *logMessage);

    class OnRequestHandler
    {
    public:
        virtual void writeLog(sfcTrace callback) = 0;
    };

    class GenericInfoHandler : public OnRequestHandler
    {
    public:

        GenericInfoHandler();
        ~GenericInfoHandler() { delete this; };
        void writeLog(sfcTrace callback);

    };

尝试以下操作:(我假设 MyProject::TestCallback 不是静态方法,它被用作导致堆栈损坏的普通函数)

更新 typedef void(*sfcTrace)(const char *logLevel, const char *logMessage); 接受一个额外的 void * 参数,该参数将与回调一起提供。即:

typedef void(*sfcTrace)(void *cb_arg, const char *logLevel, const char *logMessage);
                        ^^^^^^^^^^^^

然后更新 OnRequestHandler::writeLog(sfcTrace callback) 及其覆盖以从调用者那里获取额外的参数。即,

virtual void OnRequestHandler::writeLog(sfcTrace callback, void *cb_arg) = 0;
                                                           ^^^^^^^^^^^^
void GenericInfoHandler::writeLog(sfcTrace callback, void *cb_arg);
                                                     ^^^^^^^^^^^^

更新 writeLog 的实现以将此新参数传递给回调:

void GenericInfoHandler::writeLog(sfcTrace callback, void *cb_arg)
{                                                    ^^^^^^^^^^^^
    const char *level = "DEBUG";
    const char *message = "TEST";
    callback(cb_arg, level, message);
}            ^^^^^^

现在我们可以编写一个新的非成员回调函数,它能够调用 MyProject 对象的方法,只要通过 cb_arg 传递 MyProject 对象:

void myprj_method_callback(void *arg, const char *logLevel, const char *logMessage) {
    MyProject *mp = (MyProject *)arg;
    mp->TestCallBack(level, message);
}

最后,我们可以更新调用以传递新的回调函数和参数:

instance->writeLog(myprj_method_callback, (void *)this);

顺便说一句,让回调供应商传递一个参数,在回调被调用时,该参数将依次传递给回调,这通常是所有回调的良好做法。这让回调机制的用户可以将相关数据结构传递给回调函数,而不必将它们存储在全局变量中。