LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Passing a struct pointer in a function from a C DLL

Hi,

I'm working on a board that can spy communications on a FIP bus and I'm interfacing it with a C DLL provided by the manufacturer.

 

There is a function called 'fipcap_sess_report_get' with an argument that is a pointer to a big struct containing some values that I'm interested in.

Here is the documentation about it: https://www.exoligent.com/wiki/worldfip/fipcap/library.html#_sess_report_get (section 4.3.3)

 

The fipcap_sess pointer is stored in a Numeric that was returned by a previous function, no problem with that.

 

But I'm struggling with the fipcap_sess_report struct pointer, I think that in the input of the Call Library Function Node block, I have to wire a cluster that contains exactly the same elements than in the fipcap_sess_report struct with the correct type but as you will see in the documentation the report struct is pretty big and it contains some elements with types that I don't know how to deal with in Labview like char arrays and unions.

 

I thought about wrapping this function in a DLL or Labview can handle it?

 

 

0 Kudos
Message 1 of 7
(172 Views)

I did some research:

Apparently the data type in Labview that matches a char array in C is a cluster of U8 of the same size than the char array, is that correct?

 

Also,

caenem_0-1738275493093.png

struct fipcap_sess_node_info node[_FIPCAP_NODE_CNT_MAX] is an array of structs, so I should pass a cluster of 256 clusters that match the format of fipcap_sess_node_info?

 

In this same structure, there is a union:

caenem_1-1738275661350.png

I know that I will need to pass something with a size of 16 bits but the two elements in this union don't have the same type (I think) so what will I put in the cluster that represents fipcap_sess_node_info?

 

I found someone that had a similar situation but I didn't understand the solution: https://lavag.org/topic/23166-implementing-complex-structs-using-clusters/ 

0 Kudos
Message 2 of 7
(145 Views)

Your first guess about the structure and having to incorporate a cluster with 256 elements of a cluster is indeed true. However, the main structure contains also another structure fipcap_sess_ba_info that is in terms of that even worse. While they are all inlined and therefore no pointer magic is required, this really is a horrendous structure to try to emulate with LabVIEW directly.

 

Possible? Yes! Extremely painful to create? You absolutely bet!

 

The second problem is trivial. The ": 1" appendix to each of the lines indicates that the programmer wants a bitfield. Basically it means to only reserve as many bits of the datatype and as long as the next bitfield specifier fits into the datatype, to simply pack them into the same. If you add all the bitfield sizes together you simply get 16 bits, which is exactly the size of the uint16_t.

 

So that union basically says: This is either a raw uint16_t or an uint16_t that contains 10 individual bits plus the 6 remaining bits to fill it up to 16 bits. That remainder filler is not really necessary, but a nice indication that the API designer did think about the actual size. So in LabVIEW it is simply a 16 bit unsigned integer and you have to do the bit masking with AND and OR operators on the diagram.

Rolf Kalbermatter
My Blog
Message 3 of 7
(120 Views)

Thanks for the answer.

 

Yes these structs are definitly painful to deal with!

I understood that the programmer wanted a bitfield but I had no clue to translate this in a regular U16 int so thanks for that. 

 

Also, what about my statement about char arrays? Is that correct?

0 Kudos
Message 4 of 7
(114 Views)

@caenem wrote:

 

Also, what about my statement about char arrays? Is that correct?


Well, talking about just arrays is tricky. In C you have two types of arrays. The fixed size arrays and the pointer to an array. Fixed size arrays are of the form type name[constant_value]; while pointer arrays are just type *name;

 

Fixed size arrays are allocated at compile, respective load time of the application and are static. Pointer arrays need to be allocated dynamically at runtime.

 

If inside a structure, fixed size arrays are inlined in the structure and that means that you can indeed represent them in LabVIEW with a cluster with the number of array elements inside. This is the same for your structure containing a fixed size array of structures as it is for a structure containing a fixed size array of chars. And since a C char is the same as a LabVIEW unsigned 8bit integer (at least on the platforms LabVIEW runs on) you can indeed represent the fixed size char array as a cluster with the correct number of unsigned 8 bit integers.

Rolf Kalbermatter
My Blog
Message 5 of 7
(95 Views)

Ok, I get the difference.

 

There is another function that is bothering me:

caenem_1-1738326774352.png

structure fipcap_sess_cfg contains 4 function pointers

caenem_0-1738326644475.png

Since I'm on a 32 bits version of LV I guess my cluster should contain 32 bits int for the pointers?

 

My cluster corresponding to fipcap_sess_cfg looks like this:

caenem_3-1738327484684.png

argument type is set to "Adapt to Type" and data format to "Handles by Value"

returned pointer is stored in a U32 int.

 

On execution, LV doesn't crash but a custom error from the library is returned and tells me that error and reset handlers are mandatory which means that the DLL wasn't convinced by my cluster.

 

0 Kudos
Message 6 of 7
(83 Views)

You are running into the next problem. Data element alignment!

 

Each element in a structure is aligned to the smaller of the compiler alignment or its own natural size.

 

The compiler alignment is by default 8 byte nowadays on all major platforms but can be changed by the programmer through command line parameters to the compiler command or through #pragma declarations in the C code.

 

And LabVIEW 32-bit for Windows uses for historic reasons 1 byte alignment, while it uses 8-byte alignment for all other still supported platforms.

 

So what happens here?

 

Your DLL is most likely compiled with the default alignment of 8 bytes. That means your fipcap_dev pointer will be aligned to 4 bytes on 32-bit operation and 8 bytes on 64-bit operation.

So when you create a VI in LabVIEW 32-bit, you need to add an extra 16-bit integer between your padding and the dev pointer to make the LabVIEW cluster match the DLL structure.

 

To make the cluster match the 64-bit structure however, you need to change the dev integer to be a 64-bit integer. No explicit padding is needed here since LabVIEW also uses 8 byte alignment on this platform so will match the 8 byte alignment of your DLL.

 

There is NO pointer sized data type in LabVIEW. LabVIEW uses a strict datatype system without such ambiguities and requires all data types to have a known size independent of the platform it is running on.

 

So if you want to support both 32-bit and 64-bit for this function you will have to create two distinctive clusters for each mode, with taking care about adding extra filler bytes in the 32-bit case, and use conditional compile to call the DLL function with the respective cluster. Or you finally bite in the sour apple and create an intermediate translation DLL anyhow to translate between your painful C data structures and more LabVIEW friendly datatypes.

 

For the function pointers there is not functional translation at all. If you can't leave them cleared out to NULL values, then you absolutely have to start looking into an intermediate DLL. Function pointers are simply not translatable to LabVIEW functionality directly. 

Rolf Kalbermatter
My Blog
Message 7 of 7
(62 Views)