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

时间:2017-08-18 08:30:10

标签: c++ dynamic callback

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

我想知道如何在C ++中使用函数RegisterCallback的行为,因为它看起来非常有用,它可以将地址传递给回调函数。

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

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

例如,我可以使用我的函数调用> inverse = function (f, lower = -100, upper = 100) { function (y) uniroot((function (x) f(x) - y), lower = lower, upper = upper)[1] } > square_inverse = inverse(function (x) x^2, 0.1, 100) > square_inverse(4) [1] 1.999976 API,如下所示:

EnumWindows

提前致谢。

编辑:我会解释更多。

假设我有以下代码:

回调功能:

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

主要功能:

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

以上是任何人都可以使用API​​函数的常用方法。我希望它像这样被调用:

EnumWindows(EnumWindowsProc, NULL);

然后最终动态调用它:

LONGLONG CallbackAddress = <Some Function to get address>&EnumWindowsProc

ParamArray[1].type = VT_I8;
ParamArray[1].llval = CallbackAddress; // Address of `EnumWindowsProc`.

2 个答案:

答案 0 :(得分:1)

首先,您需要声明一个指针类型来保存回调的地址。函数的基本定义在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与函数对象,lambdas和常规函数指针兼容(见下文)。最适合使用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");

答案 1 :(得分:1)

阅读link,以下是关于回调地址的说明:

  

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

因此,您需要将指针地址转换为整数类型。从this answer中汲取灵感,以下代码应该为您提供如何在您的情况下进行转换的提示:

#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似乎是正确的数据类型。