LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Error in running example of returning a simple struct by calling DLL

Hi, all.

 

I downloaded the LabVIEWWrapper.zip from this place:https://decibel.ni.com/content/docs/DOC-9079

 

And I run the following VI in "LabVIEWWrapper_PassingDataSampleDLL.lvlib"

 

"Returning A Value Simple Struct Added.vi"aaa.JPG

I did not change anything in this VI, and when I run this VI, the labview just collapses and exits without any warning or error code.

 

Could anyone tell me what's wrong with this?

 

Thanks and Regards

0 Kudos
Message 1 of 10
(3,127 Views)

Did you read the Excel sheet that was included in the document? According to that the 

Returning A Value Simple Struct Added.vi

and

Returning A Value Pointer To Simple Struct Added.vi

 

where not generated whatever that means. At least this document seems to state that various of the test cases where either not yet tested or were working incorrectly at the time the document was created.

Rolf Kalbermatter
My Blog
0 Kudos
Message 2 of 10
(3,079 Views)

So do you mean that this VI could not run correctly and actually this VI has not been tested before released?

0 Kudos
Message 3 of 10
(3,048 Views)

@pound wrote:

So do you mean that this VI could not run correctly and actually this VI has not been tested before released?


It was tested and found to be incorrect, as explained in the web page. That's why there is also a corrected version provided in the library.

0 Kudos
Message 4 of 10
(3,028 Views)
Actually there is no correction for this VI and for the same with a complex struct. We are talking about the 2 functions which return a struct, not the ones using a struct as a byref param.
0 Kudos
Message 5 of 10
(2,385 Views)

@smi38 wrote:
Actually there is no correction for this VI and for the same with a complex struct. We are talking about the 2 functions which return a struct, not the ones using a struct as a byref param.

There are a few problems with that example. The simpleStruct example is already problematic.

 

How the SimpleStruct is exactly compiled by the compiler is very compiler specific. If an operand is larger than a register the compiler may choose to either return a reference anyhow or a hidden pointer parameter that the compiler will assign the return value to.

 

typedef struct 
{
   int val1;
   int val2;
   int val3;
} Int3Struct;

Int3Struct MyStructReturn(void)
{
    Int3Struct str = { 1, 2, 3};
    return str;
}

/*
 Since the 3 integers do not fit into a register (also not on 64-bit) this will
 be compiled into one of these two forms, depending on the compiler */

Int3Struct *MyStructReturn(void)
{
    Int3Struct str = { 1, 2, 3};
    return &str;
}

void MyStuctReturn(Int3Struct *str)
{
    *str = { 1, 2, 3};
)

 

This obviously will cause problems if the caller and callee are not compiled by the same compiler. And since LabVIEW is not only not the same compiler but a completely different environment, things get even more complicated. The first variant is in fact equal to the Return Pointer to SimpleStruct but if this is what Visual C does is up to discussion (and disassembly of the DLL).

 

The bigger problem in all these variants but the last variant is however that the struct is allocated on the stack and then returned. However as soon as the function returns, the stack is not valid anymore so what is in the memory at the time you try to read that in LabVIEW is random at best. The variant that returns explicitly a pointer to that struct is making it not better as now both the pointer and the struct are on said stack. You now have the chance that the pointer itself is destroyed. BUMM.

 

This has absolute nothing to do with LabVIEW but is basic C programming trouble that you have to deal with first and fully understand before you can even hope to understand how to interface to such code from LabVIEW. C was never meant as a cross platform, cross compiler and cross programming interface but it is all that is available when calling shared libraries. It's not a deficiency of LabVIEW but a fundamental problem about calling C code in general and shared libraries in special. No LabVIEW tutorial can and should even attempt to try to explain you such problems. If you want to deal with non scalar variables at all, you are bound to first learn all the C trouble they come with!

 

The ComplexStruct does not seem to allocate the struct on the stack, most likely as debugging has shown the returned struct and the contained pointers to be destroyed sometimes. Instead it malloc() the struct and the internal pointer, BUT it is never calling free() on them. Welcome in memory leak land.

 

And the use of malloc() in the DLL does prevent the use of free() in the caller to the DLL. Since the malloc() is really implemented in the version specific C runtime library that is linked to the DLL. the free() has to be called from the same version specific C runtime. Not the C runtime that LabVIEW was compiled with and not any other C runtime either. So you would have to know what Visual C compiler version was used for the compilation of the DLL and then locate the specific C runtime DLL and call the free() from that DLL That is if the programmer building the DLL didn't choose to include the C runtime into the DLL at the cost of increasing the DLL size. Then you have no way of locating the correct C runtime functions at all. The only useful solution for this to also export special deallocations functions from your DLL too that call internally the free() functions and instruct your caller to use these special deallocation routines for any value that is allocated in your DLL (and of course document also which parameters and value that concerns!!!). This is the only way to make sure to call the correct memory manager functions from the right C runtime library.

 

Basically the examples in that document go way to far as they touch areas that can't even be generally described, since they go into C compiler specific details. With such things, it is totally pointless to even try to document what should be done to call such code from an environment like LabVIEW. It also runs into problems with stack variables being returned which is a total nogo. The alternative of declaring the according variables as static so they are not allocated on the stack but rather as global variables avoids the problem of the struct variables going out of scope as soon as the function returns but gives a very bad example as such code is anything but multithreading safe and suffers from race conditions. The only proper solution is consequent dynamic allocation of such variables but that creates new problems about having to do proper memory management by the code and all its callers. That includes LabVIEW, which for most LabVIEW programmers is a very foreign concept and therefore goes most of the times wrong.

Rolf Kalbermatter
My Blog
0 Kudos
Message 6 of 10
(2,374 Views)

Thanks for your answer.

 

Since the original sources and article has been written by NI, it leads to some questioning about the quality of what is inside.

 

My initial request was that you rewrite the article, because the initial purpose is good, since it try to go thought the most common caveats and recommendations with DLL interfacing with LabVIEW.

 

That would avoid a lot of cloned posts on the forums and much less duplicated answers fr you 🙂

If you could rewrite the article with propper sources and wrappers, covering most common cases, plus alerts on specific cases, that would be great for the LV Community ^^.

 

0 Kudos
Message 7 of 10
(2,358 Views)

Do you have any idea how much time that would take? How much money are you willing to spend on that? 😀

 

But honestly I have a lot of other things to do! The basics are covered well in many posts. If anyone wants to go into the tricky stuff he will have to learn the nitty gritty details anyhow. To describe this topic even reasonably well you could write a whole book and still not have covered the special cases, corner situations and compiler intricacies.

 

And it would just prompt more noobs thinking they can interface to a complicated API without even understanding basic C principles. You can’t, no matter how many wizards you throw at the problem!

Rolf Kalbermatter
My Blog
0 Kudos
Message 8 of 10
(2,345 Views)

So the least would be that NI remove the 2 dummy VIs...

0 Kudos
Message 9 of 10
(2,319 Views)

@smi38 wrote:

So the least would be that NI remove the 2 dummy VIs...


That's what I would do if I did anything, and remove the according C code in the example together with some other stuff. No sense in showing things that only work when you put your finger on the tip of your nose and at the same time say "cheese", (or require 10 pages of documentations about caveats why this only works while standing on one foot and should not be used as a guideline for any code writing).

 

And there are other problems in several examples with not accounting for 64-bit.

 

The problem about calling external code is that it is fundamentally hard and the fact that the Call Library Node interfaces to code that follows C logic only makes it even harder. You may have heard about the Dunning-Kruger effect. Aside from the observation that intelligent people tend to underestimate their abilities and dumbos generally overestimate their abilities, it also uses a different graphic shown here about the progress in grasping difficult problems:

Djr1ILzX0AEl8Ek.jpg

Most people dealing with external code reach about the first peak. They started with no knowledge, read some posts and documents and have their first success with some code that doesn't crash instantly and then think they know it all. But in fact they don't know 10% of what is to know about the topic to be really able to do a proper job and even less than that from being an expert.

 

Rolf Kalbermatter
My Blog
0 Kudos
Message 10 of 10
(2,312 Views)