From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, 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: 

Calling Labview DLL from c#, passing array as argument.

Solved!
Go to solution

Hey,

 

So I'm trying to do a very basic thing. 

 

I have a VI that takes a 2d array as input, calculates the mean, and returns it.

 

After importing the DLL into C# and doing the proper build, I can call simple methods like addition and subtraction where arguments (x,y) are added and the sum is returned. That works perfectly fine. However, when i try to do it with a string or an array (of any dimension) of doubles, it gives me a system Access Violation or NAN. 

 

I have tried looking through MANY of the examples here but cannot find a reason why i can't get this to work. When looking at the build options in labview, the method prototype is 

double Mean(DoubleArray *testarray)

 

however, if i give it an argument of an array double[,] array, it returns NAN, if i say ref double[,] array, it gives me the access violation so its clearly something to do with the passing of the argument that i'm missing. I've been trying to figure this out for days as it seems like it should be basic but have gotten nowhere.

 

I cannot include the files as they're company property.

 

0 Kudos
Message 1 of 14
(4,085 Views)

If you pass arrays or strings, the memory of the returned value is allocated by LabVIEW. The moment the dll unloads, the memory is released, and .NET is accessing invalid memory. So you get a crash. Scalar values are usually passed on the stack, that is owned by the caller. So, this is no problem. Strings and arrays pass pointers on the stack. Pointers to memory. This memory is released, as it's managed by LabVIEW.

 

This is why most dll functions dealing with strings and arrays make you pass a buffer and size. This way, the memory of the result is allocated by the caller, not the function.

 

So, pass a result buffer and it's size to LabVIEW.

 

Or, pass .NET strings and arrays to the dll. Let LabVIEW read them, and perhaps if LabVIEW creates output strings or arrays as .NET objects (allocated in the .NET framework) it won't crash.

0 Kudos
Message 2 of 14
(4,026 Views)

@Jewsus wrote:

 

I cannot include the files as they're company property.


I can’t accept that as a valid excuse! Nobody asks you to post your company confidential project design but expecting us to show you a solution if you can’t even be bothered to make a small sample project that shows what you try to do (and only that) and that you for sure can post here is a bit lame!

You want help here so do some effort to facilitate the people here to help you.

If your argumentation is that you can’t post anything as it was made on your employers computer with his LabVIEW license, that applies to most here and under such s rule we would not be allowed to post the solution for you here either!

Rolf Kalbermatter
My Blog
Message 3 of 14
(4,023 Views)

That's fair. i will put together a small program of what I'm doing and post it along with the c# code im using to access it. 

0 Kudos
Message 4 of 14
(4,007 Views)

Attached are the project tester... and all the VIs in that project. 

 

I have also attached the C# program.

 

Please let me know if I'm missing anything, I think that is everything needed, but im unsure. 

 

What i really need to know is both how to pass arrays, and strings (which are just char arrays so the same thing) i have 1 VI for each. 

 

Thank you in advance, any and all help is mucho appreciated

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

The build spec is a crucial part of the problem.

 

The VI doesn't specify the call protocol, the build spec does.

0 Kudos
Message 6 of 14
(3,997 Views)

ignore this one

0 Kudos
Message 7 of 14
(3,991 Views)

Oh, you mean how Im doing the build. When i tried to upload my project, it wouldnt allow lvproj extension to be uploaded.

 

Im creating a DLL (normal, not .NET). Im sourcing the above mentioned VI's, and when i look at the source files and look at the function prototype; it shows this....

 

double Mean(DoubleArray *testarray) as it just returns the value of the mean when called on.

 

In my mind, this translates to double Mean(ref double[,] testarray) but i think this is incorrect. I don't understand where to pass the value of the length of the array, especially if im only returning a double.

 

The issue i really come across is that when that "ref" keyword is not there, the function executes but returns NAN. When it is there, it creates a accessViolation Exception. 

0 Kudos
Message 8 of 14
(3,988 Views)
Solution
Accepted by Jewsus

To get around the problem of blocked file endings (very smart way of security by the way 😁, NOT) you usually pack the whole sample project in a ZIP file and attach that. Also much easier to download than a whole list of individual files.

 

Then there is immediately one big question: IF you want to call this LabVIEW code from C# why do you want to create a normal DLL instead of a .Net assembly?

 

The actual LabVIEW code inside the DLL is the same in both cases but the wrapper LabVIEW creates is pretty different.

 

For a normal DLL it makes a C entry wrapper with the usual C calling limitations (rather unlimitations as C allows almost anything and everything to be done but that comes with its problems). 

 

For a .Net assembly, LabVIEW creates a .Net managed interface wrapper which takes care of translating any parameters between the .Net managed environment and the LabVIEW managed VI environment. Both are managed but the rules of how memory and things are managed are pretty different and require quite a bit of interface logic that LabVIEW creates for you behind the scenes and includes it in the assembly.

 

If you absolutely want to go the classic DLL path (for masochistic or other reasons 😀 ) you have to create an Interop definition in your C# code that interfaces to the unmanaged DLL interface. That means that you generally need to preallocate any memory buffer in the caller and pass its unmanaged pointer to the DLL parameter and for output buffers often need to copy the contents from the unmanaged pointer into a managed variable to use further. While other methods are possible for performance reasons, they generally involve even more .Net unmanaged interface vodoo.

 

You should absolutely NOT define the DLL interface to export native LabVIEW handles as parameters. That option is meant for DLLs that are later called from LabVIEW, or to some lesser extend but still possible with some low level knowledge, C code. .Net has no easy way to deal with the doube referenced memory structure that LabVIEW uses to represent array and string handles.

 

Instead you need to define the interface to pass these variables as C array data pointers, which automatically will also add an extra int32 value for each dimension of the array. Then before calling the function in C# you need to allocate a memory buffer in C#, and pass its underlaying pointer to the DLL parameter and also pass the actually allocated size in the extra length variables. And yes that also applies to output arrays that the DLL needs to write into.

 

I hear you say but I don't know the size of that array beforehand! That may be, but that is the problem about calling standard DLL functions that use arrays as parameters. C is unmanged, there is absolutely no official agreement about who allocates memory, where and how. Everything is possible but if the caller and callee don't exactly agree you run in a huge problem. 

 

The most simple way is to require that the caller always allocates any memory buffer and also deallocates it afterwards. That solves the problem about that the callee might allocate memory through the Visual C 2013 runtime library malloc() function while the caller was compiled with Visual C 2015 and tries to free the memory with its own free() function call with more or less instant memory corruptoin and fatal termination as result. Things can get even hairier when you involve different types of compilers.

 

If you choose to use LabVIEW handles, LabVIEW will add extra functions to the DLL to allocate and deallocate the according handle. BUT: the fact remains that accessing the data in the handle from .Net is a pretty tricky exercise. You can not let .Net manage that handle in any way. The only way to allocate and deallocate them is by also calling these extra unmanaged functions in the LabVIEW DLL.So while you want to basically treat them as IntPtr when passing to the DLL functions. you then need to treat them as a pointer to a pointer to a memory structure containing an int32 per dimension directly followed by the actual data to be able to access those data elements.

 

So the conclusion after this long essay: Why oh way do you want to create a normal DLL for this?????

Rolf Kalbermatter
My Blog
Message 9 of 14
(3,959 Views)

@Jewsus wrote:

Oh, you mean how Im doing the build. When i tried to upload my project, it wouldnt allow lvproj extension to be uploaded.


Zip it.

0 Kudos
Message 10 of 14
(3,937 Views)