Reputation: 141
I'm new in programming, Can someone help me call a pointer function from a delphi dll into Visual C# function.
Here is the Delphi Function stored into the DLL. // Delphi Code Stored in the DLL
function DeviceName(Size : Integer; Msg : Pointer) : Integer stdcall;
var
i: Integer;
TempByte : PByte;
TempName : string;
begin
if DLLClass.DevList.HidDevices.Count > 0 then
begin
TempName := (DLLClass.DevList.HidDevices.Name[DLLClass.HIDTool.CurrentDeviceIndex]);
if Length(TempName) <= Size then
begin
try
TempByte := Msg;
for i := 0 to Length(TempName) - 1 do
begin
TempByte^ := Ord(TempName[i+1]);
Inc(TempByte)
end;
Result := Length(TempName);
except
Result := -2;
end;
end
else
begin
Result := -3;
end;
end
else
begin
Result := -1;
end;
end;
//C# Code ` //Import a Function Pointer
[DllImport("HID_DLL.dll", CallingConvention= CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public unsafe static extern int DeviceName(IntPtr Size, [MarshalAs(UnmanagedType.LPStr)] string Msg);
unsafe static void Main()
{
byte[] buffer = new byte[32768];
DeviceName(255, &**buffer);
Console.WriteLine("%s\n" + buffer);
}
`
Upvotes: 1
Views: 1102
Reputation: 612963
This Delphi function is kind of messy. Its job is to copy a string into a buffer supplied by the caller. Normally that is done using null-terminated strings but the Delphi code does not do so.
Assuming that the Delphi code cannot be modified, here's how to call it from C#. Note that you don't need unsafe code here and I recommend that you stop using unsafe code.
First of all the p/invoke declaration:
[DllImport("Azoteq_HID_DLL.dll", CallingConvention= CallingConvention.StdCall]
public static extern int DeviceName(int Size, byte[] Msg);
The Size
parameter has type Integer
in Delphi. That's a 32 bit integer in C#. So use int
, just as you did for the return value. Don't use IntPtr
which is an integer whose size equals that of a pointer. That is incorrect in a 64 bit process. The Msg
parameter should be allocated as a byte array since you are not adding a null-terminator. And that in turn means there is no need to specify CharSet
.
Now the call:
byte[] Msg = new byte[256];
int retval = DeviceName(Msg.Length, Msg);
if (retval < 0)
{
// handle error
}
else
{
string name = Encoding.Default.GetString(Msg, 0, retval);
}
Your Delphi function is poorly designed though. As mentioned already, it is customary to use null-terminated strings in this scenario. Doing so allows you to let the p/invoke marshaller deal with the boiler plate code. Your error codes are poor also. If the user passes a size that is insufficient to store the string, you should let them know how much space is required.
The implementation of the Delphi function is also sub-optimal. Rather than copying byte-by-byte, you should copy the entire buffer in one shot.
One final piece of advice. Your function returns values indicating whether or not it succeeded. Your C# code simply ignores those return values. As a broad rule, if a function returns a value, you ought to be heeding it. Especially when you are performing interop, and the return value indicates success or failure.
Upvotes: 2