如何在C#中使用Delphi Dll(带PChar类型)

这是Delphi DLL代码:

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])  0; end; procedure BlockFree(Buf: pchar); stdcall; begin if assigned(Buf) then FreeMem(Buf); end; exports MsgEncode, BlockFree; begin end. 

Dll函数MsgEncode将allocmem分配给pOut param,而BlockFree用于释放由MsgEncode分配的内存。

我的问题是: 我怎样才能在C#中使用这个dll? 我是C#的新手。

我将以一些附带条件提出你的问题:

  • 无论您是否使用Unicode Delphi,都必须知道使用PChar互操作代码,因为PChar根据Delphi的版本在AnsiCharWideChar之间浮动。 我假设您使用Unicode Delphi。 如果没有,那么你需要在P / Invoke端更改字符串编组。
  • 我修改了你的DLL代码。 我删除了长度参数,并假设您只是让受信任的代码调用此DLL。 不受信任的代码可能会产生缓冲区溢出,但您不会让不受信任的代码在您的计算机上运行,​​是吗?
  • 我还更改了BlockFree以便它可以接收无类型指针。 没有必要将它作为PChar类型,它只是调用Free

这是修改后的Delphi代码:

 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. 

这是另一方的C#代码:

 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); } } } 

这应该让你开始。 由于您是C#的新手,因此您需要在P / Invoke上进行相当多的阅读。 请享用!

请注意,C#字符串数据是Unicode,因此如果您使用PChar继续使用此Delphi代码,则会在PInvoke调用中执行从PChar到PWideChar的隐藏转换。 (转换意味着分配另一个内存缓冲区并将所有数据复制到新缓冲区)如果您打算将此Delphi代码与C#一起使用并且关心性能,则应更改Delphi代码以对PWideChar数据进行操作。

使用PWideChar而不是PChar还有另一个原因:Delphi根据COM要求使用Win32 SysAllocString分配器分配OleString类型。 这意味着字符串的接收者能够使用Win32 API解除分配它。

如果您实际上没有在函数中处理文本,而是使用PChar作为任意字节值数组的代理,那么您可以在调用的非托管端而不是托管端侥幸逃脱。 如果是字节数据,则应将其声明为byte数组,以避免charset或char size转换。

在房子的C#侧,您将需要使用PInvoke来调用非托管Delphi DLL函数。 有关如何在C#中注释调用以使PInvoke自动处理缓冲区分配的详细信息,请参阅pinvoke.net 。 找到一个Win32 API函数,它传递类似于您的函数的PChar(或PWideChar)参数,然后在PInvoke.net中搜索要在托管代码中使用的PInvoke声明。

编辑

对不起,我没有看到你也导出了BlockFree函数。

经验法则是:始终在同一模块中分配和释放内存; 如果你在Dll中分配内存,它也应该在同一个Dll中释放。

因此,如果您使用BlockFree释放内存,则可以在同一模块中分配和释放内存,这没关系。

请注意,Delphi字符串和PChar类型取决于版本 – 它们是Delphi 2009之前的ANSI和Delphi 2009及之后的UNICODE。