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: 

passing complex structure array to a DLL

I am attempting to call an C++ function from an external dll using Call Library Function. One of the function parameters is a pointer to an array of structures. Each structure in the array includes and array of U16 elements. The structure of the external function is:

 

void extfunct(DWORD Param1, DWORD Param2, DWORD Param3, PVOID StrucArray)

 

and each element in the StrucArray has the structure:

 

typedef struct
{
DWORD Val1;
PVOID U16Array;
DWORD Val2;
DWORD Val3;
DWORD Val4;
DWORD Val5;
DWORD Val6;
DWORD Val7;
} StrucElement;

 

Here is the Front Panel/Block Diagram and Call Library Function Configuration:

 

Image 1.jpg

 

 

I have read a number of previous posts on the related subjects and the help files but still seem to be missing something. If I change the data formar for the StructArray to 'Handles by Values" or "Pointers to Handles" I get errors. "Array Data Pointer" works the best: it doesn't give me an error (1097) and the external function seems to recognize Param1-3 and Val1-7 but doesn't properly recognize the Int16Array.

 

(There is a simpler function in the same DLL, which accepts a pointer to a single U16 array, which I have working just fine.)

 

Any advice would really be greatly appreciated, thanks!!!

 

 

 

 

 

 

 

0 Kudos
Message 1 of 14
(5,905 Views)

This will almost work, except that you cannot embed an array in a cluster like that, because LabVIEW stores arrays differently than C.  When you pass an array directly, LabVIEW can do the necessary manipulation to make the types match, but will not do so when the array is inside a cluster.  To do this properly, you need to put a pointer-sized integer inside your cluster in place of the array.  You'll then need to allocate memory for each array inside the cluster by using DSNewPtr, assigning the output to the pointer-sized numeric value.  You then use MoveBlock to copy data from a LabVIEW array into that allocated space, or from that space into a LabVIEW array.  Both of these functions are documented in the LabVIEW help and you'll want to search for examples on this forum as well.

Message 2 of 14
(5,904 Views)

nathan

 

In theory your explanation works. In practice it fails, since LabVIEW does not know a numeric control with pointer sized integer size. LabVIEW always uses a 64 bit integer in its diagram and front panel controls to represent pointer sized integers, but then does the correct conversion on 32 bit systems to only pass the lower 32 bit to a DLL function and store a returned pointer into said lower 32 bit.

 

You can check that by creating a Call Library Node with a pointer sized integer parameter and then right click on that parameter and do "Create Control".LabVIEW will always create a 64 bit integer control. So if you want to avoid to write a wrapper DLL in C you have to start creating two different clusters one with a 64 bit integer and another one with a 32 bit integer for the two different platforms, create two according VIs and create a VI wrapper that calls the correct VI depending on the platform bitness. This definitely makes the LabVIEW diagram solution a magnitude more complicated and less maintainable than starting a C compiler and create that wrapper DLL in C.

 

But your explanation that the embedded array in the cluster doesn't work is absolutely right.

 

And on 64 Bit systems, the shown structure in the first post has potentially also padding problems, since by default the pointer would be aligned on a 64 bit boundery, adding 4 bytes of padding between the first parameter and the pointer.

Rolf Kalbermatter
My Blog
Message 3 of 14
(5,891 Views)

Rolf - thank you for that clarification.  Much of the code that I write is for one-off systems where I just need to get it working and do not need to worry about making it compatible on both 32- and 64-bit processors.  I should probably try harder to be aware of issues such as this, since we often replace older 32-bit systems with new 64-bit ones and my old code may some day break unexpectedly because of it.

0 Kudos
Message 4 of 14
(5,879 Views)

Thanks for your guys input. After your comments though, I am still not sure if what I want to do is possible. My initial attempt was based in part on this LabView example (which seems to work fine):

 

"Passing a Variety of Data Types from DLL to LabVIEW" (http://zone.ni.com/devzone/cda/epd/p/id/1288)

 

Image 2.jpg

 

Do you have any further suggestions for fixing my code?

 

Thanks!

 

 

 

0 Kudos
Message 5 of 14
(5,875 Views)

This example has an embedded LabVIEW array in the cluster and can therefore only work if the C code behind it is explicitedly written to handle that array as such.

 

A LabVIEW array is not a pointer to a memory area of (for the function) unknown size, like a C array, but a pointer to a pointer to a data structure that contains as first element a 32 Bit integer that specifies how many array alement follow. Any C code assuming this to be a simple pointer will badly crash. And if you want to do anything with the array but reading it's contents, you have to use the LabVIEW memory manager functions described in the External Code Reference Manual, to manipulate the size of that array, and this has to happen in a soecific way and order of steps to work reliably.

 

As to fixing your code you have only 2 choices:

 

1a) Go the way Nathan described and limit yourself to 32 bit or 64 bit platform only

1b) SAme as 1a) but create two different VIs and data structures for 32 bit and 64 bit platforms. Then create an additional VI that calls the according VI in a conditional compile structure depending on the platform.

2a) Create a C source code that accepts the parameters (at least the array) as individual function parameters and creates from them the required API structure and calls the function in question with that. Make a wrapper DLL from this code.

2b) Create a C source code that takes the LabVIEW data structure and converts it into the required API data structure and make a wrapper DLL from that.

 

The options are in order of complexity and in reverse order of my preferences. 2a and 2b are the only options that I consider a good long term maintainable solution.

 

 

Rolf Kalbermatter
My Blog
Message 6 of 14
(5,867 Views)

@rolfk wrote:

nathan

 

In theory your explanation works. In practice it fails, since LabVIEW does not know a numeric control with pointer sized integer size. LabVIEW always uses a 64 bit integer in its diagram and front panel controls to represent pointer sized integers, but then does the correct conversion on 32 bit systems to only pass the lower 32 bit to a DLL function and store a returned pointer into said lower 32 bit.

 

You can check that by creating a Call Library Node with a pointer sized integer parameter and then right click on that parameter and do "Create Control".LabVIEW will always create a 64 bit integer control.


Ah, so that's why that happens. I always assumed it simply defaulted to I64. Does this only apply to only those LabVIEW versions that actually had I64, or is this true even for earlier versions of LabVIEW that did not have an I64?

0 Kudos
Message 7 of 14
(5,855 Views)

Every version which supported the pointer sized integer, creates a 64 bit control if you select create control. Before that you had to use a 32 bit integer, as there was no pointer sized integer.

 

The reason is supposedly that if LabVIEW would adapt the control size to the bitness version of LabVIEW, flattened data streams containing such controls would start to have different sizes depending on which platform they were created. This is something LabVIEW tries to avoid at almost any cost.

Rolf Kalbermatter
My Blog
0 Kudos
Message 8 of 14
(5,850 Views)

Looks like DSNewPtr is only availabe in full development version... Guess I will have to go the route of trying to write a C wrapper for the DLL.

 

Thanks for your advice guys. I probably would have wasted a lot more time messing around with LabView without your input.

0 Kudos
Message 9 of 14
(5,830 Views)

@ctmiddle wrote:

Looks like DSNewPtr is only availabe in full development version... Guess I will have to go the route of trying to write a C wrapper for the DLL.

 

Thanks for your advice guys. I probably would have wasted a lot more time messing around with LabView without your input.


Why do you think DSNewPtr is not available in any version of LabVIEW? However I really would suggest you to take the C wrapper route anyways.

Rolf Kalbermatter
My Blog
0 Kudos
Message 10 of 14
(5,826 Views)