11-27-2017 02:39 PM
Hi all-
This is an extension of a solved problem, in trying to pass information from Labview to VB .NET via a DLL:
The new problem is passing an array back from the DLL to the .NET program.
This problem was originally mentioned and theoretically solved years ago:
https://forums.ni.com/t5/LabVIEW/Trying-to-use-a-Labview-dll-in-Vb-NET-2003/m-p/156020
Again, my colleague is using LV 8.5. So, they were able to download the .VI from above solution, and create a DLL. I'm able to load it up in my program, however, when I pass in the array (as defined in the code below), it seems to get squashed by the DLL somehow. If you break the program at the dSimplePass() call, the mdblArray shows up as 'Nothing' instead of the Double array.
So, something isn't quite right.
A bit more digging; the VS command line tool 'dumpbin' (dumpbin datapass.dll /exports) shows that the DataPassDll function is exported, so we know it exists.
On the VI side, my colleague sent me a screen shot of the DLL creation procedure (attached).
If I change the mLen to be called ByRef instead of ByVal, the array isn't vaporized(!) (but has only length 0), and the value becomes something bizarre (a huge value), so my gut tells me something is getting stomped on somehow in memoryland.
Now, in a fit of what-the-hell, (changing the array call to be ByVal (which is silly, because it has to be passed back))... it WORKS! So, what is going on here? If I'm passing an array in ByVal, shouldn't this be an issue, or am I getting something obvious wrong?
If I define the function (no byRef or ByVal):
Declare Sub dSimplePass Lib "datapass.dll" Alias "DataPassDll" (dIn As Double, dOut() As Double, mLen As Long)
... it works fine as well!
Declare Sub dSimplePass Lib "datapass.dll" Alias "DataPassDll" (ByVal dIn As Double, ByRef dOut() As Double, ByVal mLen As Long) Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Try Dim mDbl As Double Dim mdblArray(5) As Double Dim mLen As Long mLen = 5 mDbl = 1.5 Dim i As Integer Dim sMessage As String For i = 0 To mLen - 1 mdblArray(i) = i * 100 Next sMessage = "" sMessage = "Input:" + vbCrLf sMessage = "mDbl = " & mDbl.ToString + vbCrLf sMessage = "mLen = " & mLen.ToString + vbCrLf For i = 0 To mLen - 1 sMessage = sMessage & i.ToString & " " & mdblArray(i).ToString & vbCrLf Next MsgBox(sMessage) ' Call DLL dSimplePass(mDbl, mdblArray, mLen) sMessage = "" sMessage = "Output:" + vbCrLf sMessage = "mDbl = " & mDbl.ToString + vbCrLf sMessage = "mLen = " & mLen.ToString + vbCrLf For i = 1 To mLen sMessage = sMessage & i.ToString & " " & mdblArray(i).ToString & vbCrLf Next MsgBox(sMessage)
Catch ex As Exception MsgBox("Error = " & ex.Message) End Try End Sub
11-28-2017 07:47 AM
Your problem is likely because of the calling convention. From the attached image it would appear that your function is declared as cdecl. But the Visual Basic Declare syntax by default assumes stdcall. I'm not sure it is possible to make the Declare use cdecl instead. The recommended .Net way of doing things nowadays is to use the P/Invoke method with the DLLImport statement.
<DllImport("<dllname>", EntryPoint:="<actual exported function name>", CallingConvention:=CallingConvention.Cdecl)>
Public Shared Function YourVBFunctionName(ByVal a As Integer, ByVal b As Integer) As Integer
End Function
11-28-2017 08:59 AM
Interesting; trying that convention:
<DllImport("..\LabVIEW\DataPass.dll", CallingConvention:=CallingConvention.Cdecl, EntryPoint:="DataPassDll")> _ Private Shared Sub dSimplePass(ByVal a_input As Double, ByRef a_output() As Double, ByVal a_length As Long) End Sub
... gives me an error; changing the 'ByRef' to 'ByVal' for the a_output() array works again!
11-28-2017 12:13 PM - edited 11-28-2017 12:15 PM
Well arrays are reference types so even if you specify ByVal, VB.Net will put the pointer reference to the array on the stack. What probably happens when you specify ByRef for an Array Parameter is that VB .Net at least in the PInvoke interface will pass a reference to the pointer reference instead. The function expecting an array pointer instead will happily update what it thinks is an array but overwriting the pointer reference for the Visual Basic array instead. From here on anything can happen from a crash, to a eaten harddisk or a nuclear explosion
11-28-2017 01:40 PM
Basically what happens here is some kind of symbol overload. ByVal may have the effect you believe it should have when it is applied to a parameter to a function implemented in VB .Net. This is done by VB when compiling the function itself, where it probably creates a local copy of an array passed in as ByVal, whenever the function attempts to update the array. The parameter is still passed as an array pointer. For ByRef parameters the parameter is really passed as a pointer to an array pointer and the VB function does not make any attempts to create a local copy of the array for overwriting inside the function.
However in C you have nothing like a ByVal array or string (well sort of by declaring the parameter as const, then any attempt inside the function to write to that array parameter will be flagged during compilation as illegal). The DLL interface and therefore what LabVIEW creates when creating a DLL is however purely based on C syntax, so a function is free to modify the contents of the array passed in as a pointer, but not the pointer itself as that will not get passed back to the caller.
If a function is meant to modify the pointer itself (by re/allocating the array or freeing it) the parameter needs to be passed as a reference to the pointer (and the caller needs to be fully aware and prepared for that too).