LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Call library function node - C DLL with structs

Solved!
Go to solution

Hi, 

I am currently struggling using the CFN for a 3rd-party instrument driver DLL. This is the function of the DLL:

MDO_0-1756904930355.png

and this are the struct definitions:

MDO_1-1756905006183.png

I played around with the "AdaptToType" function and passing the according cluster without any success. In an old post I found a hint of @rolfk that it should be possible to define a parameter for each element of the struct that should be passed by value. (https://forums.ni.com/t5/LabVIEW/Interface-dll-function-with-struct-in-LabVIEW/td-p/1490848). Unfortunatelly without success, but I assume that the Response-struct pointer causes the pain...

I would be very grateful for any further tips.

 

It is an old driver currently used with LV2019 32-bit.

 

Matthias

 

 

 

0 Kudos
Message 1 of 8
(439 Views)

Disclaimer: I never had to pass structs by value, so all of this may be wrong.

 

Two pitfalls are here: passing a struct by value on a 32 bit platform and having a struct with a fixed size array in it. Those come up relatively often, this is from yesterday: https://forums.ni.com/t5/LabVIEW/Passing-HPKFPI-OPENPARAM-Struct-to-DLL-via-Call-Library-Function/m-...

 

The first struct AASiMbRequestType is passed by value. So going by the linked thread, for each struct member you need to create one function parameter. That extends to the Data field, since it is of fixed size and will be inlined. Adding one parameter for each element, you end up with 39 parameters for the input cluster alone.

 

The AASiMbResponseType struct you have to pass by reference. It has a fixed size array in it, again. You can emulate that using a fixed size cluster or replace the entire cluster with one u8 array.

Either construct the cluster manually or using the Array to cluster node. In this case, it is easier to just pass a 39-element byte array instead of a cluster.

 

snip_fixed_size_array_dll.png

 

 

See if that works.

Message 2 of 8
(410 Views)

Hi, 

many thanks for your reply! I tried to call the function in that way. Looks very nice 🙂 

MDO_0-1757315849883.png

Unfortunately LV still crashes. I think there still some kind of memory leak! 

0 Kudos
Message 3 of 8
(346 Views)

Try adding another dummy u8 parameter between request and response. 

Sorry, I suppose this has to do with stack alignment, but my knowledge about that is virtually nonexistent.

 

Stuff like this is also the reason why the general recommendation is to create wrapper libraries. Then you can have the compiler take care of the really low level stuff and provide a more LabVIEW-friendly interface that might also work for different bitness. This here definitely will not work for 64 bit.

 

0 Kudos
Message 4 of 8
(319 Views)

I would try making the request and response both the 39 byte array types. I have done this for value and reference struct types and did not break it up into multiple parameters, did all the binary formatting on the LabVIEW side.

0 Kudos
Message 5 of 8
(272 Views)

That works on 64 bit Windows, because larger structs must be passed by reference, regardless of the function prototype.

 


__m128 types, arrays, and strings are never passed by immediate value. Instead, a pointer is passed to memory allocated by the caller. Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed as if they were integers of the same size. Structs or unions of other sizes are passed as a pointer to memory allocated by the caller. For these aggregate types passed as a pointer, including __m128, the caller-allocated temporary memory must be 16-byte aligned.

https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170#parameter-passing


I think that implies a temporary copy is created. Otherwise the calle could modify the original data.

0 Kudos
Message 6 of 8
(244 Views)
Solution
Accepted by topic author M-DO

@M-DO wrote:

Hi, 

many thanks for your reply! I tried to call the function in that way. Looks very nice 🙂 

MDO_0-1757315849883.png

Unfortunately LV still crashes. I think there still some kind of memory leak! 


The principle is ok, but you are treating each char as an inidividual parameter. A structure in C is however flattened in memory. That means that a parameter can contain 4 bytes in 32-bit LabVIEW and 8 bytes in 64-bit LabVIEW.

Your structure therefore needs to be converted into a number of 32-bit or 64-bit values that are then passed as individual parameters to the function.

 

So for 32-bit LabVIEW you end up with 36 + 3 bytes distributed over 10 32-bit integers and in 64-bit LabVIEW it will be in 5 64-bit values. To make matters even more interesting, you have to account for endianess and possible alignment. So the DatLen goes into the LSB of the first 32-bit parameter. Then follows the Opcode and Circuit. Since the Data is byte sized, I'm fairly positive that it directly follows the 3 first bytes, so the Data[0] element would end up in the MSB of the first 32-bit parameter. But I'm not 100% positive about this, so you may have to experiment. Then continue for the remainder of the Data in the same way, filling up unused elements with 0.

 

ExpResLen would seem to be a shortcut for "expected response len" so might indicate the length of the last parameter but if you are positive that -1 works too, then why not.

 

The 39 bytes for the last parameter should be ok, but I would generally round it always up to the next multiple of 32-bit or 64-bit, so in this case 40. That's what most modern compilers nowadays do as well. It should not crash as the DLL has no business trying to access that last filler byte, but I had situations where it did for some reason and actually crashed.

 

And as cordm mentions, the Win64 bit calling convention specifies that parameters that do not fit in a 64-bit register are always passed by reference, even if the C code specifies by value. So there you would pass the request parameter just as you do the response parameter, as an array pointer of 39/40 bytes. Or you could create a cluster with the 3 initial values followed by a second cluster with 36 U8 and pass it as Adapt to Type. But for 32-bit you have to do the "flatten into 32-bit values" to pass individually for the first structure.

 

Considering the technique and syntax, it is clear that it is a rather old API, possibly originally developed in some other language than C such as Pascal or Delphi and the muling about 64-bit above is most likely useless as there is a good chance that this library was never ported to 64-bit.

 

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 7 of 8
(205 Views)

Hi everyone

After implementing the latest hints from @rolfk everything works! The -1 for the response length was defined by the DLL-developer for "unknown". 

Thanks a lot to all of you guys for the fast and good support!

0 Kudos
Message 8 of 8
(134 Views)