From 04:00 PM CDT – 08:00 PM CDT (09:00 PM UTC – 01:00 AM UTC) Tuesday, April 16, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

How to pass the LV Event refnum to a C callback function

Solved!
Go to solution

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!



Remember Cunningham's Law
0 Kudos
Message 1 of 11
(2,936 Views)

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.



Remember Cunningham's Law
0 Kudos
Message 2 of 11
(2,930 Views)
Solution
Accepted by topic author PeterFoerster

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.

Rolf Kalbermatter
My Blog
Message 3 of 11
(2,876 Views)

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.

Message 4 of 11
(2,870 Views)

@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!

Rolf Kalbermatter
My Blog
Message 5 of 11
(2,865 Views)

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?



Remember Cunningham's Law
0 Kudos
Message 6 of 11
(2,839 Views)

@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!

Rolf Kalbermatter
My Blog
Message 7 of 11
(2,819 Views)

@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 Smiley Very Happy



Remember Cunningham's Law
0 Kudos
Message 8 of 11
(2,813 Views)

@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 Smiley Very Happy


Half is never good enough in programming! Smiley Very Happy

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.

Rolf Kalbermatter
My Blog
0 Kudos
Message 9 of 11
(2,808 Views)

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!



Remember Cunningham's Law
0 Kudos
Message 10 of 11
(2,806 Views)