2011-02-23 49 views
3

这里是Delphi的DLL的代码:如何在C#使用Delphi DLL(与PChar类型类型)

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. 

的DLL功能MsgEncode将allocmem到POUT PARAM,并且BlockFree用于释放其中alloced存储器由MsgEncode。

我的问题是:如何在C#中使用此dll?我是C#的新手。

+0

Delphi的什么版本? (非常重要) – 2011-02-23 12:03:20

回答

9

我打算把你的问题,以票面价值,有一些附加条件:

  • 无论您使用的是Unicode的德尔福与否关键是​​要了解使用PChar因为PCharAnsiChar之间浮动互操作码和WideChar取决于德尔福的版本。我假设你使用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.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的工作。请享用!

+0

你可以安全地分配在德尔福一方,并免费在另一边?他们不是不一样的堆? – 2011-02-23 12:02:18

+2

@Warren你不能这样做,这段代码不会这么做!它在Delphi中使用GetMem和FreeMem,具有相同的堆。 – 2011-02-23 12:04:41

+0

如果我使用Delphi 6来执行相同的功能,我该怎么办?尝试同样的步骤,但“味精”的值将是“桔子”整个“。 (正在使用Delphi 6和Visual Studio 2010,.NET框架4.0) – bejarun 2015-08-11 14:12:41

1

编辑

对不起,我没看到你导出BlockFree功能也。经验法则是:始终在同一模块中分配和释放内存;如果你在Dll中分配内存,它应该在同一个Dll中释放。

所以,如果有BlockFree你自由你的内存分配和释放内存在同一模块中,这是确定的。

需要注意的是德尔福字符串,PChar类型类型是依赖于版本 - 他们是ANSI前德尔福2009年和UNICODE德尔福2009年以后。

+0

因为我需要在MsgEncode中实现复杂的计算,所以上面的代码只是一个简单的例子。 如果在主机应用程序中分配内存,分配多少?主机应用程序不知道。 是的,主机应用程序可以调用Msgencode两次,第一次计算内存大小,第二次实际分配内存。 但是,你知道MsgEncode非常复杂,花费很多时间,从性能上说,我不希望称它为两次。 我希望在dll中分配和释放内存。 – Leo 2011-02-23 06:59:54

4

需要注意的是C#字符串数据是Unicode,所以如果你使用PChar类型与此Delphi代码进行将有来自PChar类型隐藏转化为PWideChar中的PInvoke调用执行。 (转换意味着所有的数据到新的缓冲区的另一个内存缓冲区和复制的分配),如果您打算为这个Delphi代码与C#中使用,你关心性能,你应该改变你的Delphi代码在PWideChar数据,而不是工作。

还有另外一个原因使用PWideChar代替PChar类型:Delphi的分配使用Win32 SysAllocString分配器的OleString类型,如每COM要求。这意味着字符串的接收者可以使用Win32 API来释放它。

如果你实际上没有处理函数中的文本,但是使用PChar作为任意字节数组的替代品,那么你可以在通话的非托管端避开,而不在托管端。如果是字节数据,则应该将其声明为字节数组,以避免字符集或字符大小转换。

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

+1

当然海报可能会使用宽字符德尔福。 – 2011-02-23 07:40:51

+0

@David:然后仍然使用通过SysAllocString分配的PWideChar很重要。 – 2011-02-23 10:54:02

+0

@Jeroen您是否阅读过避免使用SysAllocString的P/Invoke答案? – 2011-02-23 11:02:27