2011-02-23 47 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