Chad
Chad

Reputation: 141

C# Dllimport Delphi Pointer

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

Answers (1)

David Heffernan
David Heffernan

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

Related Questions