Search code examples
vb.netdllunmanageddllimport

How do I import and call unmanaged C dll with ANSI C string "char *" pointer string from VB.NET?


I have written my own function, which in C would be declared like this, using standard Win32 calling conventions:

int Thing( char * command, char * buffer, int * BufSize);

I have the following amount of Visual Basic code figured out, which should import the DLL file and call this function, wrapping it up to make it easy to call Thing("CommandHere",GetDataBackHere).

UPDATE: This code is now a working solution, as shown here:

Imports Microsoft.VisualBasic
Imports System.Runtime.InteropServices
Imports System
Imports System.Text

Namespace dllInvocationSpace

    Public Class dllInvoker

        ' I tried attributes, but I could not make it build:
        ' <DllImport("thing1.dll", False, CallingConvention.Cdecl, CharSet.Ansi, "Thing", True, True, False, True)>
        Declare Ansi Function Thing Lib "thing1.dll" (ByVal Command As String, ByRef Buffer As StringBuilder, ByRef BufferLength As Integer) As Integer

        ' This part contributed by helpful user:
        Shared Function dllCall(ByVal Command As String, ByRef Results As String) As Integer
            Dim Buffer As StringBuilder = New StringBuilder(65536)
            Dim Length As Integer = Buffer.Capacity
            Dim retCode As Integer = Thing(Command, Buffer, Length)
            Results = Buffer.ToString()
            'Debug.Assert(Results.Length = Length)  ' This assertion is not true for me
            Return retCode
        End Function

    End Class

End Namespace

I got the code to build by following the help received here, and then I had forgot the As Return Type (which got me a MarshalDirectiveException PInvokeRestriction). Then I had an assertion failure inside my DLL, which lead to an SEHException. Once fixed, this works BEAUTIFULLY. Thank you folks. There are newsgroups where people are saying this can not be done, that Visual Basic only loads managed DLL assemblies (which I guess is the normal thing most Visual Basic users are used to).


Solution

  • It depends on how you use the buffer argument in your C code. If you only pass a string from your VB.NET code to your C code then declaring it ByVal String is good enough. If however you let the C code return a string in the buffer then you have to declare it ByVal StringBuilder and initialize it properly before the call. For example:

    Public Class dllInvoker
        Declare Ansi Function Thing Lib "Thing1.dll" (ByVal Command As String, ByVal Buffer As StringBuilder, ByRef BufferLength As Integer) As Integer
    
        Shared Function dllCall(ByVal Command As String, ByRef Results As String) As Integer
            Dim Buffer As StringBuilder = New StringBuilder(65536)
            Dim Length As Integer = Buffer.Capacity
            Dim retCode As Integer = Thing(Command, Buffer, Length)
            Results = Buffer.ToString()
            Debug.Assert(Results.Length = Length)
            Return retCode
        End Function
    End Class
    

    Note the ambiguity in the returned Length value.