0

I have a working example in single file with its spapidllm64.h but wanna split into separate files for easier coding. I have tried to reduce it into a minimal version as follows:

// spapidll.h
#define SPDLLCALL __stdcall
typedef void (SPDLLCALL *LoginReplyAddr)(char *user_id, long ret_code, char *ret_msg);
typedef void (SPDLLCALL *p_SPAPI_RegisterLoginReply)(LoginReplyAddr addr);
typedef int  (SPDLLCALL *p_SPAPI_Login)();
//main.cpp
void SPDLLCALL LoginReply(char *user_id, long ret_code, char *ret_msg); 
p_SPAPI_RegisterLoginReply SPAPI_RegisterLoginReply;
p_SPAPI_Login              SPAPI_Login;
int main(void)
{
    HINSTANCE hInst = LoadLibrary("spapidllm64.dll");
    SPAPI_RegisterLoginReply = (p_SPAPI_RegisterLoginReply)GetProcAddress(hInst, "SPAPI_RegisterLoginReply");
    SPAPI_RegisterLoginReply(LoginReply);
    SPAPI_Login = (p_SPAPI_Login)GetProcAddress(hInst, "SPAPI_Login");
    
    int rc = SPAPI_Login();
    printf("\nSPAPI_Login: %d\n", rc);
    Sleep(3000);  
    FreeLibrary(hInst);
    return 0;
}
void SPDLLCALL LoginReply(char *user_id, long ret_code, char *ret_msg)
{
    printf("\nLoginReply[%s]: Ret code: %d, Ret msg: %s",user_id, ret_code, (ret_code == 0)?"OK":ret_msg);
}

now I want to put those codes into a class like this:

// test.h
class ApiTester
{
public:    
    HINSTANCE hInst;
    ApiTester(void){
        hInst = LoadLibrary("spapidllm64.dll");
    }
    ~ApiTester(void){
        FreeLibrary(hInst);
    }
    void RegisterDLL();

    int Login();

    void SPDLLCALL LoginReply(char *user_id, long ret_code, char *ret_msg); 
    void SPDLLCALL ConnectingReply(long host_type,  long con_status);
    p_SPAPI_RegisterLoginReply               SPAPI_RegisterLoginReply;
    p_SPAPI_RegisterConnectingReply          SPAPI_RegisterConnectingReply;

    p_SPAPI_Login              SPAPI_Login;
};
// test.c
#include "tester.h"
void ApiTester::RegisterDLL()
{   
    SPAPI_RegisterLoginReply = (p_SPAPI_RegisterLoginReply)GetProcAddress(hInst, "SPAPI_RegisterLoginReply");
    SPAPI_RegisterLoginReply(LoginReply); // Error  E0167 C3867
    SPAPI_Login = (p_SPAPI_Login)GetProcAddress(hInst, "SPAPI_Login");
}

int ApiTester::Login()
{
    int rc = SPAPI_Login();
    printf("\nSPAPI_Login: %d\n", rc);
}

void SPDLLCALL ApiTester::LoginReply(char *user_id, long ret_code, char *ret_msg){
    printf("\nLoginReply[%s]: Ret code: %d, Ret msg: %s",user_id, ret_code, (ret_code == 0)?"OK":ret_msg);
}

Then Windows VS C++ shows this error:

Error (active)  E0167   argument of type "void (__stdcall ApiTester::*)(char *user_id, long ret_code, char *ret_msg)" is incompatible with parameter of type "LoginReplyAddr"

Error   C3867   'ApiTester::LoginReply': non-standard syntax; use '&' to create a pointer to member

Not sure why it works in single file but couldn't work in this way. I thought it should be straight-forward(Hopefully, there is no typo). I'd be appreciate if there is any help.

Simon
  • 411
  • 6
  • 11

1 Answers1

0
typedef void (SPDLLCALL *LoginReplyAddr)(char *user_id, long ret_code, char *ret_msg);
typedef void (SPDLLCALL *p_SPAPI_RegisterLoginReply)(LoginReplyAddr addr);

According to the definition above, LoginReplyAddr is a function pointer, not a class member function pointer.

void ApiTester::RegisterDLL()
{   
    SPAPI_RegisterLoginReply = (p_SPAPI_RegisterLoginReply)GetProcAddress(hInst, "SPAPI_RegisterLoginReply");
    SPAPI_RegisterLoginReply(LoginReply); // Error  E0167 C3867
...

In this code, LoginReply is class member function pointer.

void (__stdcall ApiTester::*)(char *user_id ...

I guess that is the problem.

If you want to use the class member function as a callback, you also have to pass the 'this' of the class. Or you can use the static member function.

The link below could help you.

Using a C++ class member function as a C callback function

bruno
  • 36
  • 2
  • sounds like this is the problem. would it be hard if you can show how to pass `this` to register callback? – Simon Nov 08 '21 at 10:07
  • If you want to pass 'this', you have to use std::bind. But I don't know if std::bind can be used in the _stdcall case. I think using the static member function can be a simple solution if possible. Please refer to link: https://embeddedartistry.com/blog/2017/02/01/improving-your-callback-game/ – bruno Nov 09 '21 at 01:25