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