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 Cluster Containing Strings in C DLL as Struct

Solved!
Go to solution

Hello everyone. 

 

I have a C DLL that I am calling inside a Labview application.

 

Some of the C functions use structs as inputs. These structs contain strings. The C DLL expects the normal char* type of string.

 

Currently I build a cluster that matches the struct and then pass that into the DLL. This seems to work fine for everything except the strings. I think it has something to do with how the Call Library Function works. Normally you can select the string format for strings as parameters. But since you must use the adapt to type setting for the cluster, you seem to lose that control.

 

I'm not exactly sure what I need to do to create the C String the DLL requires from the labview string. 

 

Any help would be appreciated.

 

 

0 Kudos
Message 1 of 16
(6,317 Views)

Can you post an example of the actual struct definition? Is the C string a pointer to a string of unknown length, or a fixed-length array of char? Based on your note that it's a char* type string, the explanation below assumes it's the former. If it's the latter, then there's a different way to handle it. With the actual struct definition I can provide more specific help.

 

A LabVIEW string and a C string are different, so, as you've already found, it won't work to put a LabVIEW string inside a cluster and pass it as though it were a struct. Instead, you need to create an element in the cluster that will store a pointer to the string, which requires a pointer-sized integer. On a 32-bit platform that's 4 bytes and on a 64-bit platform that's 8 bytes. If you need this to work in both 32- and 64-bit environments, then I don't know of a single generic way to do it; you'll need to handle each case individually.

 

You use DSNewPtr, MoveBlock, and DSDisposePtr to allocate the string in LabVIEW, copy the contents into that allocated space, and dispose of it when you're finished. Here's an example of passing an array of strings to a DLL as an array of memory addresses stored in integers; your case would be similar but with cluster elements instead of an array. Also note that you might have to add some padding to the cluster to get proper alignment. http://lavag.org/topic/14642-passing-array-of-string-to-c-dll/#entry87758

0 Kudos
Message 2 of 16
(6,311 Views)
typedef struct
{
    char* a;
    char* b;
    char* c;
    int* numbers

} input;

 The struct above is an example of the input to one of the functions. 

 

This is how I am currently doing the conversion from labview string to c string. 

 

Convert_To_C_string.PNG

 

I then bundle that up in the cluster that I feed to the dll. In order to get this to work, I changed the data format of the struct input in the Call Library Function to "Pointers to Handles". However, this appears to have broken the arrays of ints that also live in the struct.

 

 

0 Kudos
Message 3 of 16
(6,305 Views)

It would help to see a bit more of your LabVIEW code, and how you configured the Call Library Function Node. If it's set to Adapt to Type and you're passing in a cluster, the data format doesn't matter - it will always pass a pointer to the cluster.

 

A LabVIEW array is also not the same as a C array, so your conversion to an array won't work. The DLL function will get a pointer, or possibly a handle, to the LabVIEW array, and it won't get the right data.

 

Both the strings and the array need to be passed the way it's explained in the post I linked. That is, for the struct that you show, you need to create 4 pointer-sized elements. Use DSNewPtr to allocate the correct amount of space for them, and use MoveBlock to copy the array or string data into that newly-allocated space. For the strings, make sure you add a byte for the terminating null. Bundle the memory addresses into the cluster, and pass that cluster to the DLL. DSNewPtr, MoveBlock, and DSDisposePtr are functions that are built into LabVIEW. They are documented in the help. You call them using a Call Library Function Node with the library name set to "LabVIEW".

 

If you can't get it working between these comments and the linked post on LAVA, please attach a VI showing what you're doing, along with the function prototype of the function you're trying to call and the definition of any structs that are used in that prototype.

0 Kudos
Message 4 of 16
(6,302 Views)

You are suggesting I do this correct?

 

Capture.PNG

I then bundle all the points into a cluster and pass it into the DLL. The DLL appears to think that the string points are null when I do this. Though it could be a DLL error.

0 Kudos
Message 5 of 16
(6,299 Views)
Please upload your VI instead of using screen shots. Otherwise there is no way to know how you configured the call library functions. What you have there could be right, but I can't tell. Is the destination parameter to MoveBlock a pointer or a value? It should be passed by value, because the value itself is the target memory address.
0 Kudos
Message 6 of 16
(6,290 Views)

Attached is an example. I take in a string. Attempt to create a C string pointer out of it. Bundle it into a cluster. Then pass that cluster into a DLL that accepts a struct.

0 Kudos
Message 7 of 16
(6,279 Views)

Are you using a 32- or 64-bit version of LabVIEW? This is correct for a 64-bit version, but if it's 32-bit, then you need to change the value that you bundle into a 32-bit integer. As I mentioned earlier, there's no single pointer-sized integer type on LabVIEW, so if you want your code to work correctly on both platforms, then you'll need to handle the two cases separately. For a 32-bit system, replace the pointer-sized integers with 32-bit integers (in the call library function configuration) and it should work.

Message 8 of 16
(6,276 Views)

Ok that does seem to work. Thanks so much.

 

For the int arrays I'll need to do the same thing I am assuming?

0 Kudos
Message 9 of 16
(6,272 Views)

KeenanJohnson wrote:

For the int arrays I'll need to do the same thing I am assuming?


Yes, that's correct, but the int arrays pose an additional problem in that LabVIEW uses the opposite endianness from Windows. If you do byte-by-byte copying, you'll need to byte- and word-swap all the int values before passing them to the DLL. If you configure the source for MoveBlock as an array of ints, LabVIEW will handle this swapping for you automatically, but then don't forget that the number of bytes to copy will be 4 times the array length since each array element will be 4 bytes.

 

EDIT: also, you should have a way for the DLL function to know the length of the integer array, since the array won't be terminated the way a string is. And don't forget to dispose of the pointers, using DSDisposePtr, when you're done with them.

0 Kudos
Message 10 of 16
(6,267 Views)