开发者

How to use Delphi Dll(with PChar type) 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 Dll function MsgEncode will allocmem to pOut param, and the BlockFree is used to free th开发者_运维技巧e memory which alloced by MsgEncode.

My question is: How can I use this dll in C#? I'm a newbie in C#.


I'm going to take your question at face value, with a few provisos:

  • Whether you are using a Unicode Delphi or not is essential to know for interop code using PChar because PChar floats between AnsiChar and WideChar depending on the version of Delphi. I've assumed that you use Unicode Delphi. If not then you'd need to change the string marshalling at the P/Invoke side.
  • I've modified your DLL code. I've removed the length parameters and am working on the assumption that that you are only going to let trusted code call this DLL. Untrusted code could produce buffer overruns but you aren't going to let untrusted code run on your machine, are you?
  • I've also changed BlockFree so that it can receive an untyped pointer. There's no need for it to be types as PChar, it's just calling 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's the C# code on the other side:

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 get you started. Since you are new to C# you are going to need to do quite a bit of reading up on P/Invoke. Enjoy!


Note that C# string data is Unicode, so if you proceed with this Delphi code using PChar there will be a hidden conversion from PChar to PWideChar performed in the PInvoke call. (Conversion meaning allocation of another memory buffer and copying of all the data to the new buffer) If you intend for this Delphi code to be used with C# and you care about performance, you should change your Delphi code to operate on PWideChar data instead.

There is another reason to use PWideChar instead of PChar: Delphi allocates the OleString type using the Win32 SysAllocString allocator, as per COM requirements. This means the recipient of the string is capable of deallocating it using the Win32 API.

If you aren't actually processing text in your function but are using PChar as a surrogate for an array of arbitrary byte values, you can get away with that on the unmanaged side of the call but not on the managed side. If it is byte data, it should be declared as array of byte to avoid charset or char size conversions.

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 about how to annotate the call in C# to get PInvoke to take care of the buffer allocation automatically. Find a Win32 API function that passes PChar (or PWideChar) params similar to your function and then search PInvoke.net for the PInvoke declaration to use in managed code.


Edited

Sorry, I did not seen that you export BlockFree function also.

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

So if you free memory with BlockFree you allocate and free memory in the same module, that is OK.

Note that Delphi string and PChar types are version-dependent - they are ANSI prior Delphi 2009 and UNICODE in Delphi 2009 and after.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜