Reputation: 1282
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
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
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
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