My WlanRegisterNotification callback is only working when the callback is static - c++

I have been struggling for days to figure out the probably obvious reason why i cant get my code to compile.
I have a class (based on wxThread) where the callback is defined:
-- Header file --
class TestClass : public wxThread
{
private:
static void WlanNotification(WLAN_NOTIFICATION_DATA *wlanNotifData, VOID *p);
};
-- Code file --
I call the WlanRegisterNotification function, that needs the above callback function as a parameter:
dwResult = WlanRegisterNotification(hClient, WLAN_NOTIFICATION_SOURCE_ALL, true, (WLAN_NOTIFICATION_CALLBACK) WlanNotification, this, 0, &dwPrevNotif);
This compiles and works fine, but the problem is the function is marked as static, so i cant access my non static stuff from the callback (which i need for other reasons).
I have tried every single combination i can think of to pass in the callback as non static:
-- Header file --
void WINAPI WlanNotification(PWLAN_NOTIFICATION_DATA data, PVOID context);
-- Code file --
dwResult = WlanRegisterNotification(hClient, WLAN_NOTIFICATION_SOURCE_ALL, true, (WLAN_NOTIFICATION_CALLBACK)WlanNotification, this, 0, &dwPrevNotif);
i just get:
error C2660: 'WlanRegisterNotification' : function does not take 6
arguments
error C2440: 'type cast' : cannot convert from 'overloaded-function'
to 'WLAN_NOTIFICATION_CALLBACK'
I'm thinking its related to the typedef somehow:
typedef VOID (WINAPI *WLAN_NOTIFICATION_CALLBACK) (PWLAN_NOTIFICATION_DATA, PVOID);
I have tried googling for examples of using the WlanRegisterNotification function, but none of the examples i could find is calling it from a class, which is what seems to be an issue here, so i'm really lost.

A non-static class method has a hidden this parameter that the callback is not expecting let alone know how to fill in. That is why you cannot use it as a callback unless you either 1) use static to remove that parameter, or 2) create a thunk to use as the actual callback and then have it internally delegate to a non-static class method. Remember that the Windows API is designed for C, not C++. There are no classes or implicit this pointers in C.
In this case, a static callback can access non-static members of your class because you are explicitly passing the object's this pointer as the pCallbackContext of WlanRegisterNotification(), which is then passed as-is to the context of the callback:
class TestClass : public wxThread
{
private:
static VOID WINAPI WlanNotification(PWLAN_NOTIFICATION_DATA wlanNotifData, PVOID context);
};
VOID WINAPI TestClass::WlanNotification(PWLAN_NOTIFICATION_DATA wlanNotifData, PVOID context)
{
TestClass *pThis = (TestClass*) context;
// use pThis-> to access non-static members as needed..
}
// get rid of the typecast when passing the callback. If it does
// not compile, then it is not declared in a compatible manner...
dwResult = WlanRegisterNotification(hClient, WLAN_NOTIFICATION_SOURCE_ALL, TRUE, &WlanNotification, this, 0, &dwPrevNotif);

Related

Can't access object variables passed in a std callback

I have a callback function (ouside my class) which i'm passing a BassMusicPlayer class object as parameter. If do a breakpoint inside it, i can see all the variables and methods in 'self'.
If i try to read/print those values, it won't compile and will give error C2027: use of undefined type 'BassMusicPlayer'. I also added static to the class definition as suggested by others. I could use global variables as a workaround but i really want to avoid doing that.
This is what i have in my class file:
class BassMusicPlayer;
void __stdcall LoopSyncProc(HSYNC handle, DWORD channel, DWORD data, void *user) {
BassMusicPlayer * self = reinterpret_cast<BassMusicPlayer*>(user);
//printf("%d\n", self->loop_start);
//if (!BASS_ChannelSetPosition(channel, self->loop_start, BASS_POS_BYTE))
if (!BASS_ChannelSetPosition(channel, 0, BASS_POS_BYTE)) // try seeking to loop start
BASS_ChannelSetPosition(channel, 0, BASS_POS_BYTE); // failed, go to start of file instead
}
class BassMusicPlayer {
private:
std::string m_filename;
int m_filetype;
int m_music_handle;
BassMusicPlayer* m_music;
HWND m_handle;
DWORD m_sample_rate;
SYNCPROC* m_callback_proc;
public:
QWORD loop_start, loop_end;
// constructor
BassMusicPlayer(HWND handle, DWORD sample_rate, SYNCPROC* callback_proc) {
m_handle = handle;
m_sample_rate = sample_rate;
m_callback_proc = LoopSyncProc;
}
bool OpenMusicFile(std::string filename, QWORD seek_start, QWORD file_length, bool start_playing, bool loop, QWORD loopstart, QWORD loopend) {
loop_start = loopstart;
loop_end = loopend;
m_music_handle = BASS_MusicLoad(false, filename.c_str(), 0, 0, BASS_MUSIC_POSRESET, m_sample_rate);
BASS_ChannelSetSync(m_music_handle, BASS_SYNC_END | BASS_SYNC_MIXTIME, 0, m_callback_proc, this);
}
};
Why is my BassMusicPlayer class not being recognized only when i want to access its variables?
Your BassMusicPlayer cannot be recognized because you have this:
class BassMusicPlayer;
This is called a forward declaration. It tells the compiler that the class BassMusicPlayer exists, but it doesn't tell it what that class is, how much space its objects require, or what members it has. Because pointers are just integers and it doesn't matter what they point to until you try to do something with them, the compiler can use pointers just fine, but the moment you attempt to dereference one of them (such as by accessing its functions or variables with the -> operator), the compiler fails because it doesn't know how to do this. It doesn't find out until the class is actually declared later, after your callback.
To fix this, move your callback to after the declaration of the class. Because you have to know about the function in your class, you should forward declare the function instead, like this:
void __stdcall LoopSyncProc(HSYNC handle, DWORD channel, DWORD data, void *user);
The overall order of your code should be:
Forward declare the LoopSyncProc function
Declare the class
Define the LoopSyncProc function
At that point in your code where you define LoopSyncProc, you have merely declared BassMusicPlayer. You've told the compiler: "there exists a class called BassMusicPlayer", nothing more.
You can take a pointer to a forward-declared class, but you can't access the object itself - at that point the compiler doesn't know anything about the object's class, or how to access it.
You need to move LoopSyncProc below BassMusicPlayer. For that to work, you need to forward-declare LoopSyncProc:
void __stdcall LoopSyncProc(HSYNC handle, DWORD channel, DWORD data, void *user);
class BassMusicPlayer {
...
};
void __stdcall LoopSyncProc(HSYNC handle, DWORD channel, DWORD data, void *user) {
BassMusicPlayer * self = reinterpret_cast<BassMusicPlayer*>(user);
//printf("%d\n", self->loop_start);
//if (!BASS_ChannelSetPosition(channel, self->loop_start, BASS_POS_BYTE))
if (!BASS_ChannelSetPosition(channel, 0, BASS_POS_BYTE)) // try seeking to loop start
BASS_ChannelSetPosition(channel, 0, BASS_POS_BYTE); // failed, go to start of file instead
}
The problem is that at the point of compilation of that function, the type is not fully defined. The bare declaration (class BassMusicPlayer;) is not a full definition.
The solution is to move the function definition so that the whole class definition is already in scope.

Is it possible to pass a member function to QueueUserAPC?

is there a possibility or a workaround to pass a member function to the Windows API function QueueUserAPC()?
Okay, I could pass a static member function.
But then I won't have full access to local member variables...
So is there a possibility to combine both, passing as member function and full access to non-static member variables?
I tried to work out a solution related to this but without any success yet.
Hopefully someone got an idea to solve this.
This is a kind of standard pattern to use when having C-style callbacks call your C++ functions.
You create a free function (or static member) that forwards the call ...
VOID CALLBACK ForwardTo_MyClass_func( _In_ ULONG_PTR dwParam )
{
auto* p = (MyClass*)dwParam;
p->func();
}
... and you then set it up by passing the instance pointer as the third parameter to QueueUserAPC:
QueueUserAPC( ForwardToMyClass_func, hThread, (ULONG_PTR)pMyClass );
If you need further arguments, you will have to create some kind of structure to hold both the instance pointer and the arguments.
The answer is no.
Windows API has a C interface, and therefor cannot handle name mangled signatures, such as C++ member functions. The function you pass must be a C style free function.
By the way, nesting it in a namespace is acceptable, if less scalable:
namespace apc_stuff
{
static MyStruct some_static_data;
static void __stdcall MyApcFunc(ULONG_PTR data); // PAPCFUNC
};
using namespace apc_stuff;
MyClass::DoSomething(...)
{
auto my_data = new MyData(...);
auto data = reinterpret_cast<ULONG_PTR>(my_data);
QueueUserAPC(MyApcFunc, hThread, data)
}
/*static*/ void __stdcall apc_stuff::MyApcFunc(ULONG_PTR data)
{
auto my_data = reinterpret_cast<MyData *>(data);
//
// use my_data
// use some_static_data
//
}

How to pass callback function to libusb bulk transfer function in C++ Class

I am using libusb to interact with a usb device. I have created a Class and a member function will receive the input from the device and process it. I am using Asynchronous api. Now the libusb_fill_bulk_transfer() function call throws a compilation error:
void MyDeviceClass::on_connectButton_clicked()
{
int r; //for return values
ssize_t cnt; //holding number of devices in list
r = libusb_init(&ctx); //initialize the library for the session we just declared
if(r < 0) {
qDebug()<<"Init Error "<<r<<endl; //there was an error
return;
}
...
...
...
libusb_fill_bulk_transfer( transfer_in, dev_handle, USB_ENDPOINT_IN,
inBuffer, LEN_IN_BUFFER,readDataFromBuffer,NULL,0);
libusb_submit_transfer(transfer_in);
QtConcurrent::run (this,&MyDeviceClass::eventThread);
}
The compiler suggests using &MyDeviceClass::readDataFromBuffer as a function pointer but that still throws an error. I tried using static members, and even static non-member functions but all in vain. Please help me in passing the callback to this function. I am using Qt, and probably won't like to use boost libraries.
Please note that libusb_fill_bulk_transfer(...) is invoked inside a member function of MyDeviceClass.
I made the member function static and prototype needed to be modified:
static void LIBUSB_CALL readDataFromBuffer(struct libusb_transfer *);
what is the compile error when you use a non member function?
Edit: this should be LIBUSB_CALL:
void LIBUSB_CALL BulkTransferCallback(struct libusb_transfer *transfer) {
// This assumes that you set transfer::user_data to be a pointer to
// an instance of the MyDeviceClass class.
MyDeviceClass* mdc = reinterpret_cast<MyDeviceClass*>(transfer->user_data);
mdc->onBulkTransferCallback(transfer);
}
Then when you set the pointer, use:
transfer.callback = &BulkTransferCallback;
As you answered yourself, actually it should be __stdcall which is defined by LIBUSB_CALL. Their documentation should include this in the function pointer typedef to remove the ambiguity.
The lib_usb api defines the function libusb_fill_bulk_transfer with:
static void libusb_fill_bulk_transfer (struct libusb_transfer *transfer, libusb_device_handle *dev_handle, unsigned char endpoint, unsigned char *buffer, int length, libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout)
It is possible to add a static class function to your class and call the non-static member-function
of the class within this static function.
You this Object would be at the place of void *user_data.
In the static class function you would have to cast the member of
transfer->user_data correctly to your class.

Error of argument type with _beginthreadex

To define my thread I have in my Header file:
class HttpClient
{
public:
...
unsigned int __stdcall PerformLogin(void*);
...
};
Then in my cpp file I have:
unsigned int __stdcall PerformLogin(void*){
...
}
And to call this thread I use
hThread = (HANDLE)_beginthreadex( NULL, 0, &PerformLogin, NULL, 0, &threadID );
But i Have an error on the &PerformLogin saying that:
the args of type unsigned int (__stdcall HttpClient::)(void) is not compatible with the param unsigned int (__stdcall*)(void*).
I understand the error, but I really don't know how to fix this!
A possible way to fix this would be to make the member function static, though this means PerformLogin() does not have a this pointer and would have no access to non-static members of HttpClient.
Another is to move PerformLogin() out of HttpClient altogether, and make it a free function.
What I usually to is to add 'this' as the void* parameter to the static function - you can then call methods on it in the static function with a bit of casting..
Member functions get this pointer implicitly as a first parameter.
So if you want to start a thread with a class member function, you should explicitly pass a pointer to a class instance in your call to _beginthreadex.
So, remove explicit argument:
class HttpClient
{
public:
...
unsigned int __stdcall PerformLogin();
...
};
And call _beginthreadex while passing this as an argument:
hThread = (HANDLE)_beginthreadex( NULL, 0, &PerformLogin, this, 0, &threadID );
It's worth mentioning that this is a bit hacky. C++ FAQ advises against it.
I still prefer this approach. Of course I just use boost::thread usually.

C++ OOP registering callback function (typedef issue)

Basically I am trying to pack OpenNI Kinect into RT-middleware framework.
The OpenNI Kinect uses callback and has to be registered.
To do this, I could declare the callback functions and relevant variables in global without problem.
// -----------------------------------------------
// Global
// ----------------------------------------------
// Declare OpenNI nodes
xn::Context g_Context;
xn::ScriptNode g_scriptNode;
xn::DepthGenerator g_DepthGenerator;
xn::UserGenerator g_UserGenerator;
// ... more codes
// Define callback functions
// Callback: New user was detected
void User_NewUser(xn::UserGenerator& generator, XnUserID nId, void* pCookie)
{
// ... some codes
if (g_bNeedPose)
{
g_UserGenerator.GetPoseDetectionCap().StartPoseDetection(g_strPose, nId);
}
// ... some codes
}
// Callback: An existing user was lost
void User_LostUser(xn::UserGenerator& generator, XnUserID nId, void* pCookie)
{
// ... some codes
}
// -----------------------------------------------
// Member
// ----------------------------------------------
// Register callback functioins in the RT-middleware class (OOP)
RTC::ReturnCode_t rtc_kinect::onActivated(RTC::UniqueId ec_id)
{
// ... some codes
nRetVal = g_UserGenerator.RegisterUserCallbacks(User_NewUser, User_LostUser, NULL, hUserCallbacks);
// ... some codes
}
The above worked. However, using global variables and functions, I was told, is inappropriate.
I tried to move the callback functions and related variables as member of the middleware class, however been unsuccessful. The codes are:
// ----------------------------------------
// private members
// ----------------------------------------
// Declare OpenNI nodes as member variables
xn::Context g_Context;
xn::ScriptNode g_scriptNode;
xn::DepthGenerator g_DepthGenerator;
xn::UserGenerator g_UserGenerator;
// ... more codes
// Define callback functions as member function
// Callback: New user was detected
void XN_CALLBACK_TYPE rtc_kinect::User_NewUser(xn::UserGenerator& generator, XnUserID nId, void* pCookie)
{
// ... some codes
if (g_bNeedPose)
{
g_UserGenerator.GetPoseDetectionCap().StartPoseDetection(g_strPose, nId);
}
else
{
g_UserGenerator.GetSkeletonCap().RequestCalibration(nId, TRUE);
}
}
// Callback: An existing user was lost
void XN_CALLBACK_TYPE rtc_kinect::User_LostUser(xn::UserGenerator& generator, XnUserID nId, void* pCookie)
{
// ... some codes
}
// Register callback functioins in the RT-middleware class
RTC::ReturnCode_t rtc_kinect::onActivated(RTC::UniqueId ec_id)
{
// ... some codes
nRetVal = g_UserGenerator.RegisterUserCallbacks(&rtc_kinect::User_NewUser, &rtc_kinect::User_LostUser, NULL, hUserCallbacks);
// ... some codes
}
The following errors were reported by the compiler:
error C2664: 'xn::UserGenerator::RegisterUserCallbacks' : cannot convert parameter 1 from 'void (__stdcall rtc_kinect::* )(xn::UserGenerator &,XnUserID,void *)' to 'xn::UserGenerator::UserHandler'
error C2664: 'xn::PoseDetectionCapability::RegisterToPoseDetected' : cannot convert parameter 1 from 'void (__stdcall rtc_kinect::* )(xn::PoseDetectionCapability &,const XnChar *,XnUserID,void *)' to 'xn::PoseDetectionCapability::PoseDetection'
.... and many more
This is one example of the definition of the callback registration function:
inline XnStatus RegisterUserCallbacks(UserHandler NewUserCB, UserHandler LostUserCB, void* pCookie, XnCallbackHandle& hCallback)
{
// ... some codes
}
This an example of the typedef of the parameter 1:
typedef void (XN_CALLBACK_TYPE* UserHandler)(UserGenerator& generator, XnUserID user, void* pCookie);
My development platform is MS Visual Studio 2008.
Being novice, I cannot resolve the problem. Hopeful for any help.
The callbacks are meant to be pointers-to-functions. You're trying to use pointers-to-member-functions. They aren't the same thing. (From the comp.lang.c++ FAQ: Is the type of "pointer-to-member-function" different from "pointer-to-function"?). Member functions need to operate on an object of that class.
You can resolve your compilation error if you make your functions static class methods instead.
Incidentally, you didn't really do much to avoid using global variables. That is the real problem; it doesn't matter so much whether your callback functions are in the global scope or not (especially since you could put them in an anonymous namespace or use the static keyword to limit them to only that file's compilation unit). Note that using static member variables isn't really any different from using globals.
I'm not familiar with this SDK, but you probably should pack whatever information you need into a struct or class and presumably pass around pointer to it through that void* pCookie parameter.