packoman
packoman

Reputation: 1282

Translate this DLL include from VB.net to C#

This has been asked a million times, but I still cannot get it to work. So I ask: I have a DLL import (unmanaged C/C++ code) that I want to call from C# and cannot get to work. I am porting from VB.net and there the code (which works) is as follows:

Module Module1
    Public Declare Function autodetect_SearchAxis Lib "autodetect.dll" (ByVal onusb As Boolean, ByVal searchsubadress As Byte, ByRef portname As String, ByRef devicelocation As String) As Integer
    Public AxisCom As String
    Public AxisAdress As Byte = 1
    Public Dummy As String
    Public rc As Integer
    Sub Main()
        Dummy = Space(1024)
        AXISCom = Space(1024)
        rc = autodetect_SearchAxis(False, AxisAdress, Dummy, AxisCom)
        Debug.WriteLine("rc: " + rc.ToString())
        Debug.WriteLine("AxisCom: " + AxisCom.ToString())
    End Sub
End Module

I do not have my C# code-attempts at hand ATM, but I have tried various versions using the SttingBuilder class. I would be greatful, if somebody could help me porting this code to C#. Thanks in advance!

EDIT:

I now have the signature of the function of the DLL:

int _stdcall autodetect_SearchAxis(bool onusb, BYTE searchsubadress, char* &portname, char* &devicelocation)

The solution suggested by David Heffernan works partially. It is working (I don't error messages), but the returned string is garbage. Essentially it is what I already had working (with the same garbage output). I am not sure whether that has to do with character encoding or what not (I am not getting any error messages). Hope the signature helps.

Upvotes: 0

Views: 176

Answers (3)

David Heffernan
David Heffernan

Reputation: 613442

This isn't the greatest of interface specifications. On the face of it, the caller must allocate string buffers of length 1024, and trust that the unmanaged code won't write beyond that. A better interface would be to have the caller pass the buffer, and its length, so that the unmanaged code could make sure it does not overrun the buffer.

However, assuming that you cannot change the interface, you translate it something like this:

[DllImport("autodetect.dll", CallingConvention = CallingConvention.StdCall,
    CharSet = CharSet.Ansi)]
public static extern int autodetect_SearchAxis(
    bool onusb, 
    byte searchsubaddress, 
    StringBuilder portname, 
    StringBuilder devicelocation
);

You need to declare and allocate the StringBuilder instances:

StringBuilder portname = new StringBuilder(1024);
StringBuilder devicelocation = new StringBuilder(1024);

And then the call looks like this:

int retval = autodetect_SearchAxis(onusb, searchsubaddress, portname, devicelocation);
// check retval for errors

And then use the ToString() method on the two StringBuilder instances to read out the returned string values.

I must admit to being a little wobbly on how Declare works under .net since it is a backwards compatibility crutch for old VB6 code. So I do wonder how the bool parameter should be marshalled. Is it a 1 byte boolean, or a 4 byte boolean? As written in this answer, it is marshalled as the default 4 byte Windows boolean, BOOL.

Upvotes: 1

BrainSlugs83
BrainSlugs83

Reputation: 6411

I'm assuming the line that's giving you trouble (per the question's title) is:

Public Declare Function autodetect_SearchAxis Lib "autodetect.dll" _
( _
    ByVal onusb As Boolean, _
    ByVal searchsubadress As Byte, _
    ByRef portname As String, _
    ByRef devicelocation As String _
) As Integer

In C#, you'll need to add a reference (at the top of your static class):

using System.Runtime.InteropServices;

And then you'll need to make the same DLL Import:

[DllImport("autodetect.dll", SetLastError = true)]
public static extern int autodetect_SearchAxis
(
    bool onusb, 
    byte searchsubadress, 
    [MarshalAs(UnmanagedType.AnsiBStr)] ref string portname, 
    [MarshalAs(UnmanagedType.AnsiBStr)] ref string devicelocation
);

The rest of the code should be pretty straight forward.

To learn more, checkout:

Upvotes: 0

Ray
Ray

Reputation: 8871

It should basically be:

using System.Diagnostics;
using System.Runtime.InteropServices;

public static class Module1
{
    [DllImport("autodetect.dll")]
    public static extern int autodetect_SearchAxis(bool onusb, byte searchsubadress, ref string portname, ref string devicelocation);

    public static string AxisCom;

    public static byte AxisAddress = 1;

    public static string Dummy;

    public static int rc;

    public static void Main()
    {
        Dummy = new string(' ', 1024);
        AxisCom = new string(' ', 1024);
        rc = autodetect_SearchAxis(false, AxisAddress, ref Dummy, ref AxisCom);
        Debug.WriteLine("rc: " + rc.ToString());
        Debug.WriteLine("AxisCom: " + AxisCom.ToString());
    }
}

Upvotes: 1

Related Questions