LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Can't pass strings between a Labview-written DLL and Visual Basic 2010 .NET

I can't seem to pass strings between a DLL written in Labview 2010 and MS Visual Studio .MET 2010.  I'm able to pass doubles directly (byVal) and as pointers (byRef), but I get an unbalanced stack error from PInvoke when trying to pass strings.  I'm guessing this is because I don't have my data types matching, but I may be missing something else here.

 

VB_error_unbalancedStack.JPG

 

Following the example here:http://zone.ni.com/devzone/cda/tut/p/id/3188
I used Standard Calling Conventions and passed by the C String Pointer when building the DLL in Labview:

LV_functionPrototype.JPG

 

and the function prototype is: void DLLtest_Strings(char StringIn[], char StringOut[], int32_t len)

 

Here's the VB PInvoke declaration:   

Private Declare Sub DLLtest_Strings Lib "DLLtest_Strings.dll" (ByVal StringIn As String, ByVal StringOut As String, ByVal StrLen As Long)

 

And here's the code when I push the test button (note the Button4 is the test button, sendString is the input textbox and returnString is the output textbox):
    Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click       

'Get string from input field       

Dim inpString As String       

inpString = sendString.Text       

'Declare character arrays       

Dim outString As String = " "       

Dim outLen As Long = 20
        Call DLLtest_Strings(inpString, outString, outLen)       

returnString.Text = outString   

    End Sub


I believe this is exactly as in the example link above and it gives the error message, also above.  For good measure I changed the declaration to:

Private Declare Sub DLLtest_Strings Lib "DLLtest_Strings.dll" (ByRef StringIn As String, ByRef StringOut As String, ByVal StrLen As Long)

and that didn't change anything:  I still got the same error.


I did notice that the function prototype for the example demo was:

void Basicstring(CStr String, CStr String2, int32 len)

 

where mine (from LV 2010) is character arrays instead:

void DLLtest_Strings(char StringIn[], char StringOut[], int32_t len)


So, I changed the VB code to character arrays:

 

Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click       

'Get string from input field       

Dim inpString As String       

inpString = sendString.Text       

'Declare character arrays       

Dim inpChar() As Char = inpString.ToCharArray()       

Dim outChar(20) As Char       

Dim outLen As Long = 20
        Call DLLtest_Strings(inpChar, outChar, outLen)       

'Convert outChar to Strings and output       

Dim outString As String = CStr(outChar)       

returnString.Text = outString   

End Sub


This gave the same error, for both ByRef and ByVal cases of the VB DLL declaration above.  Please help.  I'm not sure how to fix this.  

 

I'm pretty sure the DLL is being called correctly because I have been able to pass doubles both ByRef and ByVal as in http://zone.ni.com/devzone/cda/tut/p/id/3188


Any ideas what might be going on here?  Does anyone know how to pass string back and forth between a Labview DLL and VB 2010 .NET?

 

0 Kudos
Message 1 of 9
(6,254 Views)

Hey Casey,

I tried what you are doing on my end and I see the same errors when calling strings.  I think the issue is with the calling convenction being used by your Visual Basic .NET 2010 code.  I think you may need to use the DLLImport command instead of the Declare command.  Some information on that can be found here:http://msdn.microsoft.com/en-us/library/aa984739(v=VS.71).aspx and here:http://www.devx.com/DevX/Article/6990/0/page/3.  I have not been able to get it working yet using this method but I am going to keep working on it.  Our documentation on this definitely needs to be updated for .NET so that is something I will work on as well.  Please post back with any additional information or questions.

Kevin Fort
Principal Software Engineer
NI
0 Kudos
Message 2 of 9
(6,218 Views)

Hey Casey,

I was able to get this working in Visual Basic .NET 2010 if you use the C calling convention when compiling the DLL in LabVIEW if you use the following code:

 

dll.png

I am not entirely sure why the standard calling convention is not working but we are going to continue taking a look at it and update our documentation. Please post back with any additional questions.

Kevin Fort
Principal Software Engineer
NI
0 Kudos
Message 3 of 9
(6,203 Views)

Hi Kevin-

 

Thank you for getting back so quickly.  I'm still having trouble and I'm a bit confused about your code.  Going with the C calling convention sounds like a good thing to try, and again the floats pass back and forth just fine.  My test DLL adds one to the input and sends it back.

 

However, I'm still having trouble on the strings.  In your code you don't initialize val (the length of the output string to return).  When I do this I just get the original value of stringoutput rather than the value that's been sent my the DLL.  In your example case, it would just be the value "test 2".  I believe that 'val' is the length of the string to be returned from the DLL.  My DLL appends some text to the original, so I expect to see that appended text and I don't see it.

 

Here's my results for different values for 'val'

Val = 0               stringoutput = 'test 2'

Val = 1               stringoutput = most of the full path to the DLL

Val = 2               stringoutput is blank (or sometimes random symbols)

Val = 3               Error:  ArgumentException was unhandled:  The pointer passed in as a String must not be in the bottom 64k of the process's address space.

Val > 3               Error:  AccessViolationException was unhandled:  Attempted to read or write protected memory.  This is often an indication that other memory is corrupt.

 

So, I can't get my test code to work, and I'm a little confused about how your code is sending the proper length for the return string.

 

A little more direction would really help me out.  Thanks again,

Casey

0 Kudos
Message 4 of 9
(6,195 Views)

Hey Casey,

To do this you are going to need to build a .NET Interop assembly in LabVIEW instead of a DLL.  You can then just add the assembly as a reference in VB and call it similar to the following code:

interopassembly.png

You will also need to make sure that you set the target framework in Visual Studio to be .NET 3.5 (not 4.0) or you will get an error that looks like this:

interopassemblyerror.png

For more information regarding building .NET interop assemblies in LabVIEW look here:http://zone.ni.com/reference/en-XX/help/371361H-01/lvhowto/building_a_net_assembly/

Kevin Fort
Principal Software Engineer
NI
0 Kudos
Message 5 of 9
(6,173 Views)

@Casey wrote:

Hi Kevin-

 

Thank you for getting back so quickly.  I'm still having trouble and I'm a bit confused about your code.  Going with the C calling convention sounds like a good thing to try, and again the floats pass back and forth just fine.  My test DLL adds one to the input and sends it back.

 

However, I'm still having trouble on the strings.  In your code you don't initialize val (the length of the output string to return).  When I do this I just get the original value of stringoutput rather than the value that's been sent my the DLL.  In your example case, it would just be the value "test 2".  I believe that 'val' is the length of the string to be returned from the DLL.  My DLL appends some text to the original, so I expect to see that appended text and I don't see it.

 

Here's my results for different values for 'val'

Val = 0               stringoutput = 'test 2'

Val = 1               stringoutput = most of the full path to the DLL

Val = 2               stringoutput is blank (or sometimes random symbols)

Val = 3               Error:  ArgumentException was unhandled:  The pointer passed in as a String must not be in the bottom 64k of the process's address space.

Val > 3               Error:  AccessViolationException was unhandled:  Attempted to read or write protected memory.  This is often an indication that other memory is corrupt.

 

So, I can't get my test code to work, and I'm a little confused about how your code is sending the proper length for the return string.

 

A little more direction would really help me out.  Thanks again,

Casey



Actually the caller needs to allocate a buffer long enough for the callee to write in whatever he wants to do. The ByVal len parameter tells the callee how big the buffer is so that he doesn't write beyond the end of the buffer. So you need to allocate a buffer in Visual Basic and pass it to your output string parameter AND tell the function in the len parameter how long it is. This is standard unmanaged memory procedure, and the DLL interface is unmanaged eventhough your VB environment and the LabVIEW environment are managed in itself, although using highly different paradigmas.

 

I'm sure it can be done, but it requires specific knowledge about how to handle unmanaged bufferes in VB. My VB knowledge is not big enough, to be of much help there other than to indicated what you do wrong.

 

Going the .Net assembly path as Kevin suggests does have some implications, but it has the advantage that LabVIEW will take care about providing a .Net compatible managed interface and translate between this and its own paradigma.

Rolf Kalbermatter
My Blog
0 Kudos
Message 6 of 9
(6,162 Views)

Hi Kevin,

 

I also tried the example on the following page:

"I was able to get this working in Visual Basic .NET 2010 if you use the C calling convention when compiling the DLL in LabVIEW if you use the following code:"

 The response at:

 http://forums.ni.com/t5/LabVIEW/Can-t-pass-strings-between-a-Labview-written-DLL-and-Visual/td-p/174...

 

The Console.WriteLine(stringoutput)  result is "test2" , which is the same as the declared stringoutput before the Console call and val = 0

 

 

I was able to do simple adder, which is in type double.  The input and output behave correctly as expected.  

double Adder(double x, double y)

 

But when I tried the string all kinds or error occuring.  Actually, during compiling the dll, the decalration said output but the Function Prototype was not as I expected--C or Standard Calling Convention

void StringConcat(char string1[], char string2[], char concatenatedString[], int32_t len)

 

I expect it behaves the same as simple adder:

string StringConcat( char string1[], char string2[])

 

Please advise an example calling labview dll from .Net for string.

 

Thanks,

Mie Mie

 

 

0 Kudos
Message 7 of 9
(4,686 Views)

I believe string and char in .Net are always Unicode 2 byte characters. LabVIEW strings are always ANSI. So you really will need to pass those strings as byte array and add a VB String to Byte Array conversion to the input string and the opposite to the output. And the previous statement from me remains:

 

You have to allocate a buffer for the outputstring byte array in VB before calling the LabVIEW DLL function that is big enough to contain the longest possible string that your function can return and you have to pass the length of that allocated buffer in the len variable. The function from LabVIEW will not return more data than what you pass in as len parameter and if you pass a longer value in there than what the buffer was allocated with YOU WILL cause MEMORY CORRUPTION.

 

 

<DllImport("SharedLib.dll", CallingConvention:=CallingConvention.Cdecl)> _
Public Shared Sub DLLtest_Strings(ByVal in As Byte(), ByRef out As Byte(), ByVal len As Long)
End Sub

Sub Main()
Dim stringinput As String = "Some string to test"
Dim stringoutput As String
Dim bytearrayout As Byte(255)

DLLtest_Strings(System.Text.Encoding.Default.GetBytes(stringinput), bytearrayout, 255)

stringoutput = System.Text.Encoding.Default.GetString(bytearrayout))
End Sub

Something like this should probably work. I don't have VB installed nor do I intend to install it on any of my machines!

 

Quite likely the input to the first parameter of the function needs to be explicitedly NULL terminated. I don't think the GetBytes() method does this itself! So writing a Function which does the conversion and adds a NULL byte to the end is probably a good idea, rather than just pass the GetBytes() result to the function.

 

Also unless you restrict the string to 7 bit ASCII characters you will get some interesting surprises. Encoding translation is usually not lossless. The Default encoding will use ANSI which is what LabVIEW uses for its strings, so it is the best encoding to use, but it will definitely cause trouble for most non 7 bit ASCII characters. UTF8 encoding isn't a solution either, since its encoding translation could cause illegal characters when interpreting a LabVIEW ANSII encoded string as UTF on the conversion from the outputarray back to a string. 

Rolf Kalbermatter
My Blog
0 Kudos
Message 8 of 9
(4,663 Views)

Thanks for your prompt response and help.

Also, this may help those who is coding in VB.net, I was able to get help with the following link:

https://forums.ni.com/t5/LabVIEW/Calling-a-LV8-2-created-DLL-in-VB-net/td-p/556943

 

This example work well, and I was aslo able to re-create my LV dll on string concatenate work in VB.Net

 

Thanks again,

Mie Mie

0 Kudos
Message 9 of 9
(4,633 Views)