02-06-2019 04:50 PM
Hello all,
in my last post, I got the C callback working in LabVIEW. LabVIEW calls a function, which in turn calls PostLVUserEvent() to send data to an event structure in LabVIEW. However, this is not, what I want to do in my finished application. I want to register Event callbacks in my C dll, which execute when certain things happen (up to here, I got it working) and then use PostLVUserEvent() from those callbacks to tell LabVIEW something has happened.
As the title says, I'm wondering how to pass the LVUserEventRef from the function, where I set up the callback to the callbacks.
1) store it in a global. I don't know if that's possible within a dll, but it's probably not a good idea to begin with
2) using the context parameter that the setcallback function provides. This could look something like this:
void connlost(void *context, char *cause) { PostLVUserEvent(*context, <data>); } int init(LVUserEventRef *Ref) { setcallback(*Ref, cbfunction); }
The function is defined like this: int MQTTClient_setCallbacks(MQTTClient handle, void* context, MQTTClient_connectionLost* cl, MQTTClient_messageArrived* ma, MQTTClient_deliverycomplete* dc) Documentation here.
Is it possible to somehow pass the reference that way?
Does a 3rd way exist, that I haven't thought of?
Thanks in advance for any pointers on how to sort these pointers!
Solved! Go to Solution.
02-06-2019 05:01 PM - edited 02-06-2019 05:02 PM
OK, I just tested option 1) and it works:
LVUserEventRef* GlobalRef; void callback (void* context, char* cause) { PostLVUserEvent(*GlobalRef, <data>); } void init (LVUserEventRef* ref) { GlobalRef = ref;
setCallback(NULL, callback); }
I still would prefer a solution that does not involve globals. Also if someone with more knowledge of this topic could share some insight, I'd be excited to hear it.
02-07-2019 08:30 AM
A good callback design usually allows to register a user data value (normally an opaque pointer value) together with the callback function. The function then is called with this user data value as one of its parameters. Looking at your code I suspect (but can't be sure) that the context parameter of the callback function is actually this user data value. And the first value to setCallback() where you pass in NULL might be the according parameter to pass in this context.
So if you really just care about the user event refnum you could pass it in there (casting it to the void* pointer) and inside the callback routine you cast it back to a user event refnum and use it. Usually you tend to need more than just the refnum. In that case you have to allocate a memory area for the structure that will hold all the settings you want to pass into the callback function when registering the callback, and then deallocate that memory pointer when deregistering the callback.
02-07-2019 09:09 AM
The "context" that gets passed to your callback is the same context that you pass to the MQTTClient_setCallbacks method. You can choose to pass it anything you like, including a struct containing all your LVUserEventRef's. You might be able to use a Labview cluster for that, but I would be more comfortable using a wrapper dll and managing the memory for that cluster/struct myself.
02-07-2019 09:19 AM - edited 02-07-2019 09:23 AM
@cordm wrote:
The "context" that gets passed to your callback is the same context that you pass to the MQTTClient_setCallbacks method. You can choose to pass it anything you like, including a struct containing all your LVUserEventRef's. You might be able to use a Labview cluster for that, but I would be more comfortable using a wrapper dll and managing the memory for that cluster/struct myself.
Don't use a LabVIEW cluster! The memory for that cluster will only be guaranteed to be valid for the duration of the Call Library Node call and that means it will be likely invalid at the time your callback routine is called. -> Bum, instant crash!
02-07-2019 12:21 PM
Thanks for answering. It's good to know that I wasn't completely on the wrong path.
Now all I need to do is figure out the correct casts. Here's a minimal example that I tried.
void delivered(void *context, MQTTClient_deliveryToken dt){} int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message){} void connlost(void *context, char *cause) { PostLVUserEvent((LVUserEventRef) context,(void *)<Data>); } int MQTT_ConnectLocal(MQTTClient* client, LVUserEventRef *LVEvRef) { MQTTClient_setCallbacks(*client, (void*) LVEvRef, connlost, msgarrvd, delivered); return 0; }
How do I correctly reference and dereference the positions in red?
02-07-2019 03:48 PM
@PeterFoerster wrote:
Thanks for answering. It's good to know that I wasn't completely on the wrong path.
Now all I need to do is figure out the correct casts. Here's a minimal example that I tried.
void delivered(void *context, MQTTClient_deliveryToken dt){} int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message){} void connlost(void *context, char *cause) { PostLVUserEvent((LVUserEventRef) context,(void *)<Data>); } int MQTT_ConnectLocal(MQTTClient* client, LVUserEventRef *LVEvRef) { MQTTClient_setCallbacks(*client, (void*) LVEvRef, connlost, msgarrvd, delivered); return 0; }How do I correctly reference and dereference the positions in red?
Hmmm, you need of course to make sure that the reference stays the same before and after the cast. The way you do it you pass the reference to the refnum as context but in the callback you cast that reference into the refnum itself, bad bad bad!
02-07-2019 04:07 PM
@rolfk wrote:
Hmmm, you need of course to make sure that the reference stays the same before and after the cast. The way you do it you pass the reference to the refnum as context but in the callback you cast that reference into the refnum itself, bad bad bad!
I'm, not sure I follow completely.
I did some more trial and error and with this setup it works:
void delivered(void *context, MQTTClient_deliveryToken dt){} int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message){} void connlost(void *context, char *cause) { PostLVUserEvent(*(LVUserEventRef*) context,(void *)<Data>); } int MQTT_ConnectLocal(MQTTClient* client, LVUserEventRef *LVEvRef) { MQTTClient_setCallbacks(*client, (void*) LVEvRef, connlost, msgarrvd, delivered); return 0; }
So, half of my earlier post was OK...
*(LVUserEventRef *) context... Guess I'll never be a C programmer
02-07-2019 04:24 PM
@PeterFoerster wrote:
@rolfk wrote:
Hmmm, you need of course to make sure that the reference stays the same before and after the cast. The way you do it you pass the reference to the refnum as context but in the callback you cast that reference into the refnum itself, bad bad bad!I'm, not sure I follow completely.
I did some more trial and error and with this setup it works:
void delivered(void *context, MQTTClient_deliveryToken dt){} int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message){} void connlost(void *context, char *cause) { PostLVUserEvent(*(LVUserEventRef*) context,(void *)<Data>); } int MQTT_ConnectLocal(MQTTClient* client, LVUserEventRef *LVEvRef) { MQTTClient_setCallbacks(*client, (void*) LVEvRef, connlost, msgarrvd, delivered); return 0; }So, half of my earlier post was OK...
*(LVUserEventRef *) context... Guess I'll never be a C programmer
Half is never good enough in programming!
But you did indeed understand where the problem was. This should not be something you need to find out with trial and error though. C programming with trial and error is a disaster waiting to happen for sure.
02-07-2019 04:30 PM
I'll have to agree.
It would be a lot easier if I could set breakpoints in the dll and have an expression window...
The problem in this was that I am already fairly unfamiliar with using pointers in C. For most of the standard cases I manage pretty well by now, but *(type *) variable... At least admit that that is not what they teach you on the first day!
Thanks again for all your help along the way!