2

I am trying to build an application that can dynamically call any Win32 API function according to user input.

I am wondering how can I have behavior of the function RegisterCallback in C++, because it seems very useful and it can get the address to a callback function.

How can I achieve a same behavior with a function like it in C++?

I already successfully implemented a function which can call any dynamic library, but stuck in such dynamic callbacks.

For example I can call EnumWindows API with my function like below:

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

Thanks in advance.

EDIT: I will explain more.

Assume I have following code:

Callback function:

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

Main Function:

EnumWindows(EnumWindowsProc, NULL);

Above is the usual way anyone can use the API function. I want it to be called like this:

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

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

And then finally call it dynamically like:

CallLibFunction(GetProcAddress(LoadLibrary(L"User32.dll"), "EnumWindows"), ParamArray, 2, ExepInfo);
Blueeyes789
  • 543
  • 6
  • 18
  • 1
    `RegisterCallback` maybe only works with script scope. – GTAVLover Aug 18 '17 at 09:02
  • Do you have a concrete example of where you'd like to use the callback? Function pointers exist in C++, if that is what you mean of "Get the address of a callback...". – pingul Aug 18 '17 at 09:04
  • @pingul to use in C++. – Blueeyes789 Aug 18 '17 at 09:05
  • 1
    Yes, but _please provide an example_, as in, code. What do you want to achieve? – pingul Aug 18 '17 at 09:11
  • 1
    The question title does not reflect the question content. It looks like you don't have problems with `GetProcAddress` (except for no error handling). If you want to be able to push arbitrary parameters then you'll need to implement passing all of them. – user7860670 Aug 18 '17 at 09:13
  • @pingul updated with what I want. – Blueeyes789 Aug 18 '17 at 09:17
  • 1
    Thanks. What about your example does not work? To get the raw address of a function, you need to cast it to `void*`: try to do `std::cout << (void*)&EnumWindowsProc << std::endl;` and you should see the address. – pingul Aug 18 '17 at 09:44
  • @pingul Thanks a lot! using it like `reinterpret_cast(&EnumWindowsProc)` worked fine! I couldn't believe my eyes even it compiled! :-) – Blueeyes789 Aug 18 '17 at 10:01
  • @Blueeyes789 I'm glad it compiles. The `reinterpret_cast` looks a little scary though -- if the `void*` is not of type/format as `LONGLONG` you're going to be in trouble. Does `static_cast` not work? – pingul Aug 18 '17 at 10:09
  • @pingul It gives "invalid type conversion". – Blueeyes789 Aug 18 '17 at 10:13
  • 1
    My guess is that the reinterpret cast wont work as well in the case when you run it. I've added an answer that hopefully helps. – pingul Aug 18 '17 at 10:29

2 Answers2

1

First, you need to declare a pointer type to hold the address of your callback. The basic definition of a function is a bit odd in c++. There is a further complication that in C++ we have functions, function-objects, and template types.

The standard provides a basic function template type:std::function. This type holds not a function pointer, but a callable object.

#include <functional>

To declare a specific function type, pass its signature to std::function as its template parameter.

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 is compatible with function objects, lambdas and regular function pointers (see below). Works best with C++ only stuff.

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-style function pointers

Before C++, there was C. THis is how it's done in C, and it does compile. The disadvantage with C-style function pointers is that they are not compatible with function objects. On the other hand they are compatible with C, and many other languages such as PASCAL, VB, etc..

For example, the type a function taking a const char* as a parameter and returning an int is written as:

typedef int (CallBack)(const char*);

The most usual form is to declare a pointer, since that's what is stored. As in:

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");
Michaël Roy
  • 6,338
  • 1
  • 15
  • 19
  • I do not fully understand this answer. Are you answering "What is a callback function?", or the specific case that OP asks about? – pingul Aug 18 '17 at 10:11
  • I am answering this question: _"I am wondering how can I have behavior of the function RegisterCallback in C++, because it seems very useful and it can get the address to a callback function."_ – Michaël Roy Aug 18 '17 at 10:12
1

Reading your link, the following is said about the callback address:

If the exe running the script is 32-bit, this parameter must be between 0 and 4294967295. If the exe is 64-bit, this parameter can be a 64-bit integer.

So, you need to convert your pointer address to an integer type. Drawing some inspiration from this answer, the following code should give you a hint how to do the conversion in your case:

#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;
}

For me this prints:

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

Note here that an int is to small to cover the void* value. You should be able to convert properly with LONGLONG as well, otherwise intptr_t seems to be the correct data type.

pingul
  • 3,351
  • 3
  • 25
  • 43
  • What I thought is right. `LONGLONG` prints garbage when used in 32-bit systems. I might need a preprocessor block to jump to `LONG` in such cases. :-) – Blueeyes789 Aug 18 '17 at 10:32
  • 1
    @Blueeyes789 I think `intptr_t` is platform independent, and should be 32-bits on a 32-bit system, which is why it is suggested in this case. – pingul Aug 18 '17 at 10:36