My c++ class is using WinSnmp to get integer value from device.
The session is initialized as follows:
snmpident::snmpident(const char* c, const char* si, const char* i,const char* o)
{...
sesshandle=SnmpCreateSession(NULL,NULL,snmpgetCB,this);
...}
So, i'm receiving address of class object in callback function, that is using it to call method of this object:
SNMPAPI_STATUS CALLBACK snmpgetCB(HSNMP_SESSION hSession,HWND hWnd, //callback func
UINT wMsg,WPARAM wParam,LPARAM lParam,LPVOID lpClientData)
{...
reinterpret_cast<snmpident*>(lpClientData)->recv(sv.value.sNumber); //recv is an "snmpident" class method
...}
As you can see, it can cause "access violation" if the user of the class (in this case I) removes the class object before the callback function completes its work.
How can i fix this?
Sleep() looks ugly. Array of {pointer;session id} structure looks bit intresting, but i'm not sure, that it's the best choice.
P.S. Please forgive me for my english.
Related
I am using PubSubClient.h, as defined here: https://pubsubclient.knolleary.net/api.html
The instance is created as the following:
WiFiClientSecure secureCl;
PubSubClient mqttCl(secureCl);
PubSubClient has method called setCallBack, which takes in the parameter callback.
PubSubClient setCallback (callback)
callback : a pointer to a message callback function called when a message arrives for a subscription created by this client.
The following signature is given for function callback:
void callback(const char[] topic, byte* payload, unsigned int length)
But, within the example code I were given, inside setup the callback function used is the receivedCallback function:
mqttCl.setCallback(receivedCallback); //called inside setup()
And the function is given as:
void receivedCallback(char* topic, byte* payload, unsigned int length) {
//some code here
}
Now, I can see that it has char* topic, instead of const char[] and I can't get my head around how it is not const char[], as defined by the signature. Could someone explain in simple terms why is it like that?
It seems a bug, in this context the problem is given by the const attribute e not by the [] or the *. In fact const char[] and const char* are equal, also char[] and char* are equal, but const char[] and char* can create something that is incompatible.
The difference is in the missing const of your callback.
Basically char* topic as an argument means that you are allowed to manipulate the content of topic as in topic[0] = 'A';.
On the other side const char* or const char[] guarantees that there will not be a change to the underlying string and so a pointer to an string from the read-only memory could be given.
mqttCl could for example do the following:
byte payload[10];
callback("test-topic", payload, 10);
And your recievedCallback is allowed to do:
topic[0] = 'b';
The compiler is allowed to store "test-topic" in the read-only memory of the process in which case the program would crash. To prevent this the const part in type signatures must match.
I'm wrapping a C API with a C++/CLI wrapper in order to consume it from a C# project.
The C API requires a callback for one of its functions. The callback signature takes a void* user data parameter.
I would like to pass in this as the void* user data parameter to the C API but I have been unsuccessful (failing to compile). Furthermore it's not as easy to pass in 'this' as this is referring to a managed class, so of course I'd have to pin the reference first prior to passing it in.
I'm also unsure how to cast the void* parameter back to a ManagedClass^ in the callback function.
Code:
public ref class ManagedClass
{
public:
static void __stdcall CallbackFunc( int, void* userData )
{
// How can I cast the void* parameter back to
// ManagedClass^ ??
}
void Open( )
{
// I know I need to pin the reference to myself
// but I'm unsure how to go about it. How can I store
// this in a pin_ptr??
ptr = this;
// Open the third party C-API passing in
// my callback function and a reference to
// myself.
thirdPartyApiOpen( &CallbackFunc, ptr );
}
private:
pin_ptr<ManagedClass^> ptr;
};
I have the following two methods in my class ObstaclesDetector and I'm trying to pass a callback using std::bind to the constructor of another class called ASLnetDecompressor, but once I call the Start() method which probably invokes the callback, the program crashes with an Access violation.
If I pass the reference to the static function (radarCallback) directly, it works.
It seems to me that the pointer does not address a globally accessible memory but I don't know how to fix it. Do you have any ideas?
The code:
include "obstacles_detector.h"
using namespace std::placeholders;
...
void ObstaclesDetector::detectObstacles(float range, ObstaclesDescriptor *desc) {
auto callback = std::bind(ObstaclesDetector::radarCallback, _1, _2, _3); // just to test it, does not make sense, of course
ASLnetDecompressor net((ASLnetDecoderCallback*) &callback, desc, 4096, 4378);
net.Start(); // Access violation executing location {address of callback}
}
...
void ObstaclesDetector::radarCallback(void *arg,const ReturnHeader *h, unsigned char *data) { // a static function (defined in header file)
printf("AAA\n");
}
Condition
I use a framework that has an custom type as bellow:
typedef log (*CustomType) (
int timeStamp,
const char* data,
int dataSize,
void* userData,
int dataType,
int viewId
)
and MyClass init method as bellow:
MyClass_Init (void **output, CustomType video, CustomType audio, void* userData)
Question
I used init method like bellow but always receive error (error content is not displayed because i use a framework). pls point me what is missed.
CustomType videoInput;
CustomType audioInput;
void *output = malloc(sizeof(void*);
void *userData = malloc(sizeof(void*));
long result = MyClass_Init(&output, videoInput, audioInput, userData);
A number of things wrong with this code:
You can't intermix function pointers and method pointers. What it boils down to is that the this for a method has to be included in the method call signature. Since the function pointer doesn't include the this pointer (it is a function, not a method pointer), the two can not match.
Most C-based API includes some sort of reference value (most frameworks call those refCon, context or userData), so what you can do is create an adapter function that calls your method. The userData parameter in your CustomType parameter list looks like it is one of those (consult the docs to be sure).
You can probably provide a userData wherever you set MyClass_Init as your callback now. So, if that function to provide a callback to the library was called set_callback( MyCustomType callback, void* userData ), do something like
MyClass *obj = new MyClass; // Or however you create your object
set_callback( MyClassCallbackAdapterFunction, obj );
with an adapter function like:
log MyClassCallbackAdapterFunction( int timeStamp, const char* data, int dataSize, void* userData, int dataType, int viewId )
{
MyClass *myThis = (MyClass*) userData;
// Here you can now call myThis->MyClass_Init( ... ) however you want to.
}
The malloc( sizeof(void*) ) statements look like you're misunderstanding return parameters (also called "side effects" by some teachers). I don't have the docs to whatever API/library you're using, but I'm pretty certain you're supposed to not just pass in buffers the size of a pointer. Either you'd just provide a pointer on the stack in which a buffer will be returned, or you provide a whole buffer (e.g. an array) and its size, and that is where the callback will write to or so.
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);