How to use Delphi Dll (with type PChar) in C #

Here is the Delphi dll code:

library Project2; uses SysUtils, Classes; {$R *.res} function SimpleConv(const s: string): string; var i: Integer; begin Result := ''; for i := 1 to Length(s) do if Ord(S[i]) < 91 then Result := Result + S[i]; end; function MsgEncode(pIn: pchar; InLen: Integer; var pOut: pchar; var OutLen: Integer): Boolean; stdcall; var sIn: string; sOut: string; begin SetLength(sIn, InLen); Move(pIn^, sIn[1], InLen); sOut := SimpleConv(sIn); // Do something OutLen := Length(sOut); GetMem(pOut, OutLen); Move(sOut[1], pOut^, OutLen); Result := OutLen > 0; end; procedure BlockFree(Buf: pchar); stdcall; begin if assigned(Buf) then FreeMem(Buf); end; exports MsgEncode, BlockFree; begin end. 

The MsgEncode Dll function will pass mem to the pOut parameter, and BlockFree is used to free memory that was allocated using MsgEncode.

My question is: How to use this DLL in C #? I am new to C #.

+3
source share
3 answers

I am going to take your question at par, with a few caveats:

  • If you use Unicode Delphi or not, it is important to know the interaction code using PChar , because PChar floats between AnsiChar and WideChar depending on the version of Delphi. I assumed that you are using Unicode Delphi. If not, you will need to change the string sorting on the P / Invoke side.
  • I changed your dll code. I have deleted the length parameters and work under the assumption that you are only going to allow trusted code to call this DLL. Incorrect code can cause buffer overflows, but you are not going to run untrusted code on your computer, are you?
  • I also modified BlockFree so that it can get an untyped pointer. There is no need for him to be a type of PChar , he just called Free .

Here's the modified Delphi code:

 library Project2; uses SysUtils; {$R *.res} function SimpleConv(const s: string): string; begin Result := LowerCase(s); end; function MsgEncode(pIn: PWideChar; out pOut: PWideChar): LongBool; stdcall; var sOut: string; BuffSize: Integer; begin sOut := SimpleConv(pIn); BuffSize := SizeOf(Char)*(Length(sOut)+1);//+1 for null-terminator GetMem(pOut, BuffSize); FillChar(pOut^, BuffSize, 0); Result := Length(sOut)>0; if Result then Move(PChar(sOut)^, pOut^, BuffSize); end; procedure BlockFree(p: Pointer); stdcall; begin FreeMem(p);//safe to call when p=nil end; exports MsgEncode, BlockFree; begin end. 

And here is the C # code on the other hand:

using System; using System.Runtime.InteropServices;

 namespace ConsoleApplication1 { class Program { [DllImport("project2.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool MsgEncode(string pIn, out IntPtr pOut); [DllImport("project2.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] public static extern void BlockFree(IntPtr p); static void Main(string[] args) { IntPtr pOut; string msg; if (MsgEncode("Hello from C#", out pOut)) msg = Marshal.PtrToStringAuto(pOut); BlockFree(pOut); } } } 

This should help you get started. Since you're new to C #, you will need to read a little P / Invoke. Enjoy it!

+9
source

Note that C # string data is Unicode, so if you continue with this Delphi code using PChar, there will be a hidden conversion from PChar to PWideChar performed in the PInvoke call. (Conversion means allocating another memory buffer and copying all data to a new buffer). If you intend to use this Delphi code for use with C # and you care about performance, you must modify your Delphi code to work with PWideChar data.

There is another reason to use PWideChar instead of PChar: Delphi allocates an OleString type using a Win32 SysAllocString dispenser, just like COM requirements. This means that the receiver of the string is able to free it using the Win32 API.

If you do not actually process the text in your function, but use PChar as a surrogate for an array of arbitrary byte values, you can avoid this on the unmanaged side of the call, but not on the managed side. If it is byte data, it must be declared as an array of bytes in order to avoid encoding conversion or char.

On the C # side of the house, you will need to use PInvoke to call the unmanaged Delphi DLL function. See pinvoke.net for details on how to annotate a call in C # so that PInvoke automatically performs buffer allocation. Find the Win32 API function that passes PChar (or PWideChar) parameters similar to your function, and then search for PInvoke.net to declare PInvoke for use in managed code.

+4
source

Edited

Sorry, I have not seen that you are also exporting the BlockFree function.

The rule of thumb: always allocate and free memory in one module; if you allocate memory in a Dll, it must also be freed in the same Dll.

So, if you free memory with BlockFree, you allocate and free memory in one module, that’s fine.

Note that Delphi strings and PChar types are version dependent β€” these are ANSI before Delphi 2009 and UNICODE in Delphi 2009 and after.

+1
source

All Articles