C++ 成员函数作为外部库的回调函数
C++ member function as callback function to external library
所以下面是我正在尝试做的事情的基本想法。
我有一个外部库,我想在现有项目中使用它。
我当然无法更改外部库中的任何内容或现有项目中的主要功能。
我面临的问题是如何将我在 class 中创建的回调函数作为函数指针传递给此外部函数。同时,这个回调函数必须能够访问 class 的成员,所以我不能简单地将其设为静态。我该怎么做?
Class ExternalClass //This I cannot mess with.
{
//somestuff
void ExternalFunc (void(* callback)(int, const void*), const void *);
}
Class MyClass
{
//somestuff
ExternalClass m_ExObj;
void Callback(int x, const void *p){
//dosomething
//How do I use this pointer ?
}
void MyFunc(){
m_ExObj.ExternalFunc(/*Some way to put MyClass::Callback() in here*/)
}
}
您显示的回调不允许将用户定义的值传递给它(否则您可以使用它来传递对象指针)。它需要一个独立的非 class 函数,因此您有两个选择:
1) 如果回调一次只调用一个对象,那么您可以将对象指针存储在全局或 static
变量中,然后使用独立函数(或 static
class 方法)作为回调并让它使用 global/static 指针调用您的 class 方法:
class MyClass
{
//somestuff
ExternalClass m_ExObj;
void Callback(int x)
{
//dosomething
}
static MyClass* objForCallback;
static void exObjCallback(int x) { objForCallback->Callback(x); }
void MyFunc()
{
objForCallback = this;
m_ExObj.ExternalFunc(&exObjCallback);
}
};
2) 如果您需要同时对多个对象进行回调,则必须将 class 方法包装在每个对象的 thunk 中,其中每个 thunk 都知道要调用哪个对象,并且然后你使用 thunks 作为回调。这是一种更高级的技术,需要了解 x86/x64 汇编和调用约定,因为您必须动态分配内存并使用汇编指令填充它,以便每个 thunk 在运行时执行。例如,至少在 Windows 32 位:
#pragma pack(push, 1)
struct MyThunk
{
unsigned char PopEAX_1; // POP the caller's return address off the stack
unsigned char PushThis; // PUSH the object 'this' pointer on to the stack
void *ThisValue;
unsigned char PushEAX_1; // PUSH the caller's return address back on to the stack
unsigned char Call; // CALL the callback function
__int32 CallAddr;
unsigned char PopEAX_2; // POP the caller's return address off the stack
unsigned char AddESP[3]; // Remove the object 'this' pointer from the stack
unsigned char PushEAX_2; // PUSH the caller's return address back on to the stack
unsigned char Return; // return to the caller
};
#pragma pack(pop)
typedef void (*CallbackType)(int);
class MyClass
{
CallbackType exObjCallback;
MyClass()
{
MyThunk *thunk = (MyThunk*) VirtualAlloc(NULL, sizeof(MyThunk), MEM_COMMIT, PAGE_READWRITE);
if (thunk)
{
thunk->PopEAX_1 = 0x58;
thunk->PushThis = 0x68;
thunk->ThisValue = this;
thunk->PushEAX_1 = 0x50;
thunk->Call = 0xE8;
thunk->CallAddr = reinterpret_cast<__int32>(Callback) - (reinterpret_cast<__int32>(&thunk->Call) + 5);
thunk->PopEAX_2 = 0x58;
thunk->AddESP[0] = 0x83;
thunk->AddESP[1] = 0xC4;
thunk->AddESP[2] = 0x04;
thunk->PushEAX_2 = 0x50;
thunk->Return = 0xC3;
DWORD dwOldProtect;
VirtualProtect(thunk, sizeof(MyThunk), PAGE_EXECUTE, &dwOldProtect);
FlushInstructionCache(GetCurrentProcess(), thunk, sizeof(MyThunk));
exObjCallback = (CallbackType) thunk;
}
}
~MyClass()
{
if (exObjCallback)
VirtualFree(exObjCallback, 0, MEM_RELEASE);
}
//somestuff
ExternalClass m_ExObj;
// NOTE: pCtx is the return address inside of ExternalFunc()
// where the callback is being called from. Because the
// callback is using the __cdecl calling convention, the
// thunk needs to remember this value and restore it after
// Callback() exits. Passing it as a parameter to Callback()
// is a quick-n-dirty way for the thunk to do that...
static void __cdecl Callback(void *pCtx, MyClass *pThis, int x)
{
//dosomething with pThis
}
void MyFunc()
{
if (exObjCallback)
m_ExObj.ExternalFunc(exObjCallback, ...);
}
};
当ExternalFunc()
调用它的回调时,它会调用thunk,执行它包含的指令。上面的 thunk 将对象的 this
指针作为 Callback()
的参数注入调用堆栈,就好像 ExternalFunc()
直接调用它一样。
更新: 代替有关实际上接受用户定义值的回调的新信息,这大大简化了事情:
class MyClass
{
//somestuff
ExternalClass m_ExObj;
static void Callback(int x, const void *p) {
MyClass *pThis = (MyClass*) p;
//dosomething with pThis
}
void MyFunc() {
m_ExObj.ExternalFunc(&Callback, this);
}
};
所以下面是我正在尝试做的事情的基本想法。 我有一个外部库,我想在现有项目中使用它。 我当然无法更改外部库中的任何内容或现有项目中的主要功能。
我面临的问题是如何将我在 class 中创建的回调函数作为函数指针传递给此外部函数。同时,这个回调函数必须能够访问 class 的成员,所以我不能简单地将其设为静态。我该怎么做?
Class ExternalClass //This I cannot mess with.
{
//somestuff
void ExternalFunc (void(* callback)(int, const void*), const void *);
}
Class MyClass
{
//somestuff
ExternalClass m_ExObj;
void Callback(int x, const void *p){
//dosomething
//How do I use this pointer ?
}
void MyFunc(){
m_ExObj.ExternalFunc(/*Some way to put MyClass::Callback() in here*/)
}
}
您显示的回调不允许将用户定义的值传递给它(否则您可以使用它来传递对象指针)。它需要一个独立的非 class 函数,因此您有两个选择:
1) 如果回调一次只调用一个对象,那么您可以将对象指针存储在全局或 static
变量中,然后使用独立函数(或 static
class 方法)作为回调并让它使用 global/static 指针调用您的 class 方法:
class MyClass
{
//somestuff
ExternalClass m_ExObj;
void Callback(int x)
{
//dosomething
}
static MyClass* objForCallback;
static void exObjCallback(int x) { objForCallback->Callback(x); }
void MyFunc()
{
objForCallback = this;
m_ExObj.ExternalFunc(&exObjCallback);
}
};
2) 如果您需要同时对多个对象进行回调,则必须将 class 方法包装在每个对象的 thunk 中,其中每个 thunk 都知道要调用哪个对象,并且然后你使用 thunks 作为回调。这是一种更高级的技术,需要了解 x86/x64 汇编和调用约定,因为您必须动态分配内存并使用汇编指令填充它,以便每个 thunk 在运行时执行。例如,至少在 Windows 32 位:
#pragma pack(push, 1)
struct MyThunk
{
unsigned char PopEAX_1; // POP the caller's return address off the stack
unsigned char PushThis; // PUSH the object 'this' pointer on to the stack
void *ThisValue;
unsigned char PushEAX_1; // PUSH the caller's return address back on to the stack
unsigned char Call; // CALL the callback function
__int32 CallAddr;
unsigned char PopEAX_2; // POP the caller's return address off the stack
unsigned char AddESP[3]; // Remove the object 'this' pointer from the stack
unsigned char PushEAX_2; // PUSH the caller's return address back on to the stack
unsigned char Return; // return to the caller
};
#pragma pack(pop)
typedef void (*CallbackType)(int);
class MyClass
{
CallbackType exObjCallback;
MyClass()
{
MyThunk *thunk = (MyThunk*) VirtualAlloc(NULL, sizeof(MyThunk), MEM_COMMIT, PAGE_READWRITE);
if (thunk)
{
thunk->PopEAX_1 = 0x58;
thunk->PushThis = 0x68;
thunk->ThisValue = this;
thunk->PushEAX_1 = 0x50;
thunk->Call = 0xE8;
thunk->CallAddr = reinterpret_cast<__int32>(Callback) - (reinterpret_cast<__int32>(&thunk->Call) + 5);
thunk->PopEAX_2 = 0x58;
thunk->AddESP[0] = 0x83;
thunk->AddESP[1] = 0xC4;
thunk->AddESP[2] = 0x04;
thunk->PushEAX_2 = 0x50;
thunk->Return = 0xC3;
DWORD dwOldProtect;
VirtualProtect(thunk, sizeof(MyThunk), PAGE_EXECUTE, &dwOldProtect);
FlushInstructionCache(GetCurrentProcess(), thunk, sizeof(MyThunk));
exObjCallback = (CallbackType) thunk;
}
}
~MyClass()
{
if (exObjCallback)
VirtualFree(exObjCallback, 0, MEM_RELEASE);
}
//somestuff
ExternalClass m_ExObj;
// NOTE: pCtx is the return address inside of ExternalFunc()
// where the callback is being called from. Because the
// callback is using the __cdecl calling convention, the
// thunk needs to remember this value and restore it after
// Callback() exits. Passing it as a parameter to Callback()
// is a quick-n-dirty way for the thunk to do that...
static void __cdecl Callback(void *pCtx, MyClass *pThis, int x)
{
//dosomething with pThis
}
void MyFunc()
{
if (exObjCallback)
m_ExObj.ExternalFunc(exObjCallback, ...);
}
};
当ExternalFunc()
调用它的回调时,它会调用thunk,执行它包含的指令。上面的 thunk 将对象的 this
指针作为 Callback()
的参数注入调用堆栈,就好像 ExternalFunc()
直接调用它一样。
更新: 代替有关实际上接受用户定义值的回调的新信息,这大大简化了事情:
class MyClass
{
//somestuff
ExternalClass m_ExObj;
static void Callback(int x, const void *p) {
MyClass *pThis = (MyClass*) p;
//dosomething with pThis
}
void MyFunc() {
m_ExObj.ExternalFunc(&Callback, this);
}
};