获取要在C++中动态调用的回调函数的地址

Get the address of a callback function to call dynamically in C++

本文关键字:回调 函数 地址 调用 动态 C++ 获取      更新时间:2023-10-16

我正在尝试构建一个可以根据用户输入动态调用任何 Win32 API 函数的应用程序。

我想知道如何在C++中RegisterCallback函数的行为,因为它似乎非常有用,并且可以获取回调函数的地址。

如何在C++中使用类似的函数实现相同的行为?

我已经成功地实现了一个可以调用任何动态库的函数,但卡在了这样的动态回调中。

例如,我可以使用我的函数调用EnumWindowsAPI,如下所示:

CallLibFunction(GetProcAddress(LoadLibrary(L"User32.dll"), "EnumWindows"), ParamArray, 2, ExepInfo);

提前谢谢。

编辑:我会解释更多。

假设我有以下代码:

回调函数:

BOOL CALLBACK EnumWindowsProc(__in HWND hWnd, __in LPARAM lParam)
{
return TRUE;
}

主要功能:

EnumWindows(EnumWindowsProc, NULL);

以上是任何人都可以使用 API 函数的常用方式。我希望它像这样称呼:

LONGLONG CallbackAddress = <Some Function to get address>&EnumWindowsProc
ParamArray[1].type = VT_I8;
ParamArray[1].llval = CallbackAddress; // Address of `EnumWindowsProc`.

最后动态调用它,如下所示:

CallLibFunction(GetProcAddress(LoadLibrary(L"User32.dll"), "EnumWindows"), ParamArray, 2, ExepInfo);

首先,您需要声明一个指针类型来保存回调的地址。 函数的基本定义在 c++ 中有点奇怪。 更复杂的是,在C++中,我们有函数、函数对象和模板类型。

该标准提供了一个基本的函数模板类型:std::function。此类型 保存的不是函数指针,而是可调用的对象。

#include <functional>

要声明特定的函数类型,请将其签名传递给 std::function 作为其模板参数。

typedef std::function<int(const char*)> StdFreeFunc;
// you can also define types for member functions, and define 
// member function pointers this way
struct A{};
typedef std::function<int A::*(const char*)> StdMemberFunc;
// member callable objects are called with this syntax:
// (obj.*callable)(args); or (obj->*callable)(args); 
// the parenthesis are often required to keep the compiler happy
// A callable object:
struct CallMe
{
int xy;
int operator()(const char*) { /*...*/ return xy; } 
};

std::function 与函数对象、lambda 和常规函数指针兼容(见下文(。最适合C++的东西。

struct AStruct
{
StdFreeFunc callback_;   // we hold a value, not a pointer
void setCallback(StdFreeFunc&& cb)  // pass by value, reference, const ref, 
{                                   // move... like any object type 
callback_ = cb; 
};
int callCallback(const char* str) 
{ 
if (callback_) // callable object has a bool() operator to check if valid !!
return (callback_)(str); 
// the parenthesis and is optional, so this is the same:
if (callback_)
return callback_(str):
}
};
// example with callable object:
AStruct as, ar;
as.setCallback(CallMe{42});   // we can pass an object with data
ar.setCallback(CallMe{84});   // to obtain different effects
as.callCallback("prints fourty two");
ar.callCallback("prints eighty four");

C 样式函数指针

在C++之前,有C。 这就是它在 C 中完成的方式,它确实可以编译。 C 样式函数指针的缺点是它们与函数对象不兼容。 另一方面,它们与C和许多其他语言(如PASCAL,VB等(兼容。

例如,将 const char* 作为参数并返回 int 的函数类型写为:

typedef int (CallBack)(const char*);

最常见的形式是声明一个指针,因为这是存储的内容。如:

typedef int (*CallBack)(const char*);
// you can specify extra call specifications
typedef int (__stdcall * CallBack)(const char*);  // uses PASCAL calling

// exmample:
struct YourStruct
{ 
//...
Callback callback_{nullptr};  // this is a pointer initialize to nullptr
//...
void setCallback(Callback cb)
{ 
// call as ys.setCallback(AFunction)
// or      ys.setCallback(&AFunction)
callback_ = cb; 
};
int callCallback(const char* str) 
{ 
if (callback_)   // always check if valid !!
return (*callback_)(str); 
// the parenthesis and the * are optional, so this is the same:
if (callback_)
return callback_(str):
}
};
int UserFunction(const char*) { /*...*/ return 0; }
YourStruct ys;
ys.setCallback(&UserFunction);
ys.callCallback("hello");

阅读您的链接,以下是关于回调地址的说法:

如果运行脚本的 exe 是 32 位,则此参数必须介于 0 和 4294967295 之间。如果 exe 为 64 位,则此参数可以是 64 位整数。

因此,您需要将指针地址转换为整数类型。从这个答案中汲取一些灵感,以下代码应该会给你一个提示,说明如何在你的情况下进行转换:

#include <iostream>
#include <cstdint>
bool f() {
return true;
}
int main() {
int iv = *((int*)(&f));
long lv = *((long*)(&f));
long long llv = *((long long*)(&f));
intptr_t ipv = *((intptr_t*)(&f));
std::cout << "int: " << iv << std::endl;
std::cout << "long: " << lv << std::endl;
std::cout << "long long: " << llv << std::endl;
std::cout << "intptr_t: " << ipv << std::endl;
}

对我来说,这打印:

int: -443987883
long: 82192552476362837
long long: 82192552476362837
intptr_t: 82192552476362837

请注意,此处的int要小以覆盖void*值。您也应该能够使用LONGLONG正确转换,否则intptr_t似乎是正确的数据类型。