LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

How to use*callbackData

Solved!
Go to solution

i am not an expert c programmer which is most likely why this question arises...

 

What is the proper way of using the *callbackData variable??

 

For instance when creating an async callback or in my current instance, a menu Callback.  I am trying to pass a string through this but am having trouble casting.

 

if (checked == 0) {

if (FindPattern(file_name,0,-1,".xml",0,0) > 0) {

device_handle[i] = NewMenuItem (5, MENU_LOAD, file_name, -1, 0, LoadDeviceFile, &file_name);

i++;

} //else not a .xml file

while (GetNextFile(file_name) == 0) {

if (FindPattern(file_name,0,-1,".xml",0,0) > 0) {

device_handle[i] = NewMenuItem (5, MENU_LOAD, file_name, -1, 0, LoadDeviceFile, &file_name);

i++;

} //else not a .xml file

}

 

void CVICALLBACK LoadDeviceFile (int menuBar, int menuItem, void *callbackData,int panel) {

//????how to cast?

 

I know I could technically take the menuItem number and find out, but that another function call, and am mainly looking on how to do the process 

 

In general, are there any examples of this anywhere in CVI?  I would be interested in knowing how to do this with any data type.

 

Thanks 

 

 

0 Kudos
Message 1 of 4
(3,496 Views)
Solution
Accepted by topic author ngay528

The problem in using callbackData parameter is that it actually is a pointer to something, so you must be sure that when the callback executes, callbackData points to a valid object in memory; in particular, this means that you cannot pass the address of a variable local to a function (which can be the case of your 'file_name' variable), which is likely to be no more valid once the callback executes since the originating function has probably ended.

In your case, you should first malloc some space for the string and then pass this address in callbackData parameter, casting it to a char * inside the menu callback.

Without forgetting the need to free the allocated memory when it is no longer needed...

 

This is valid for any data type you want to pass through callbackData, with the only exception of int variables which can simply be passed as (void *)variable and used by casting them to an int (actually pointers are of type int, so you can use it to pass int values without need to point to a real memory block).



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
Message 2 of 4
(3,490 Views)

Great technical background, Roberto, thanks.

 

Looking at just how complicated it is to pass non-int variables into callback functions, one would have to seriously consider just using a global variable instead.  Particularly since the use-cases would tend to be more local to the callback function. 

 

For instance, say a user clicks some button and in that callback function, a flag and some double data needs to be sent to the panel as callbackData.  If this is malloced here, it's lost by the time the user does the callback action to the panel where the data can be used, i.e., the data would be freed too early back in the button callback.

0 Kudos
Message 3 of 4
(2,347 Views)

Using a global variable surely is the easiest way to solve these problems, but the programmer must consider it carefully since the global variable could be modified elsewhere in the program before the function that uses it actually runs.

 

Just as an example, my usual situation is as follows: I have a struct variable that holds status information for a test in progress, and a function (let we call it the test handler) that processes the test and in some case modifies some status value (e.g. the number of cycles executed); when the function needs to pass to the new cycle, it issues a deferred callback to record previous cycle results, next actualizes process values. In that deferred callback I could not use the global status variable, since when it executes that variable has been changed by the test handler, so I malloc some memory, copy there the status info and call the deferred callback passing the dynamic memory handle. The deferred, asynchronous callback records (historic) results to disk read from the dynamic memory and then frees it.

 

That is to say, a global variable may not be the optimal solution.

 

 

(Note: there are tons of pages on "global variables are bad" refrain that I'm sure you well know. I'm not as radical as to completely ban them from the code, but I understand the risks of using them; for this reason, if a way exists to accomplish a task without using globals that requires a reasonable effort to follow it, I normally go that way)



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
Message 4 of 4
(2,340 Views)