LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Memory management with C/C++ DLL

Solved!
Go to solution

Hi there, 

sorry, I suspect this is a FAQ or explained *somewhere* but I was unable to find it. I am very new to LabView, my task is to write a C/C++ library to be used from a LabView VI. 

 

I found this helpful tutorial about managing pointers received from DLLs 

 

What I am trying to figure out is how memory allocated inside the DLL and passed to a LabView event is handled.

 

In the tutorial, I found:

 

int * ReturnAValue_PointerToInteger(void)

{

     int *x = (int *) malloc(sizeof(int));

     *x = 5;

     return x;

}

 

The DLL allocates the pointer and passes it as a return value. It will be used after the function exits, so compellingly the VI is responsible for deleting it.

 

However we need to pass some more complex structures that are ideally compound (C structs):

 

struct MessageAccessStruct
{
   unsigned short messageHandle;
   LStrHandle accessAddress;
   LStrHandle accessValue;
   unsigned short accessResult;
};

 

So inside the DLL, such a struct is allocated with DSNewPtr, then the two LStrHandles are allocated with DSNewHandle and filled with data. After everything is set up, the struct will be passed using PostLVUserEvent()

 

As a C++ developer it makes me feel a bit uncomfortable to allocate memory and expect "someone else" to free it. However, I did not observe any crashes when de-allocating all pointers I allocated inside the DLL immediately after PostVLUserEvent returns. This may be a race condition or the way it is designed (i.e. PostVLUserEvent creates a copy of the data that is passed to the VI).

 

So my general question is: How does LabView determine which pointers passed from the DLL are in the responsibility of the VI/LabVIew environment and which are expected to be managed by the DLL?

 

0 Kudos
Message 1 of 8
(634 Views)

anyone?

0 Kudos
Message 2 of 8
(572 Views)

Handling the labview data structures inside C code is a nice challange. Its no task for beginners.

 

I recomment, that you stay inside the C world. Write your C functions like you normally do. Use only the basic data types, which makes it easy to call your C functions from labview: Do not pass complex values (avoid union, avoid struct). And please: all functions should be thread save.

 

As long a you stay in the C world: you are allways responsible to allocate and free the memory. Labview knows nothing about malloc() and free() of your C/C++ compiler.

 

0 Kudos
Message 3 of 8
(566 Views)

Thanks a lot Martin,

 

my challenge is that the functionality provided by the DLL does not allow me to "stay in the C world". The DLL has an active part to receive messages through a TCP based protocol. The LabView app needs to be notified about newly received messages. For this, I need to form a LabView event, but the data is trivial (just an integer which serves as kind of a handle to the message)

 

Then LV will need to read contents from the message, and for each attribute there must be

  • LV -> DLL: The message handle (int)
  • LV -> DLL: The attribute to be retrieved (string)
  • DLL -> LV: The attribute value (string)
  • DLL -> LV: The status code (whether the message handle was valid, the attribute was found, ...)

Ideally, this could be encapsulated in a single struct and handled in a synchronous call. Otherwise (the DLL->LV communication would be an LV event), LV would have to do bookkeeping about pending access operations.

But even in the asynchronous case, it is necessary to have the string (attribute value) and the access status in a single structure sent as a LV event. 

 

If we just consider the latter: The DLL cannot know when LV has "consumed" the event, but someone has to take care of de-allocating the memory of the string which inevitably needs to be allocated in the DLL.

 

This is the background of my question, and I would be thankful for answers beyond "keep your hands off that" 😄

0 Kudos
Message 4 of 8
(564 Views)
0 Kudos
Message 5 of 8
(548 Views)

Thank you very much again, Martin,

 

but what I am currently capable of doing is beyond what the example shows. The example is trivial as can be. LV triggers the DLL to submit an event with a loop count as "payload". 

My question refers to memory management where more complex payload is needed.

 

Anyway, I am please that someone is taking care of my question at all.

0 Kudos
Message 6 of 8
(543 Views)
Solution
Accepted by topic author Holladiewaldfee

PostLVUserEvent() does a full (deep) copy of all the data you pass to it. To do that it uses the typedef information that is inherent to the event refnum. This means you better make damn sure that the data structures you pass to it are EXACTLY matching the typedef you used to Create the user event in the LabVIEW diagram. There is no safeguard at all, LabVIEW does not know what datatype you pass to the function as C has no concept of runtime type safety. They simply have to magically match and it is your task to make sure you pass data to the function that matches the datatype the user event refnum was created with. This not only applies to the exact datatype's and memory manager functions to use to allocate the different data elements (for handles at least, the pointer for the structure in your example could be allocated with any memory management function including malloc() as LabVIEW will not try to do anything with it but reading its content), but you also must make sure to use the LabVIEW native alignment. For most platforms this is nowadays the compiler default but for Windows 32-bit LabVIEW always used full byte packing and still does. The easiest way to make sure your alignment is correct is to wrap the typedef declaration of your structures that need to be interpreted by LabVIEW by the include files "lv_prolog.h" and "lv_epilog.h" that you can find in the cintools directory in your LabVIEW folder.

 

Since PostLVUserEvent() does a deep copy, you are not only allowed to deallocate all memory in the data after the function returns control but you are even required to do so if you can't stash it somewhere for reuse with the next event. And stashing data somewhere has its own challenges with multithreading safety and multiple different events needing potentially different data structures.

Rolf Kalbermatter
Averna BV
Message 7 of 8
(538 Views)

Perfect, Rolf. This is all I wanted to know. I am aware that it is not easy to fit complex structures into the handling of LV, and we are currently exploring the possibilities. Agreed, this is a lot of trial and error.

But my was referring to the memory management, and "PostVLUserEvent creates a copy" is exactly how it seemed to be. I am pleased to have a confirmation that my deletions in the DLL are not only working by incident (race condition).

 

Thank you very much!

0 Kudos
Message 8 of 8
(533 Views)