2013-04-23 31 views
6

Tôi có một ứng dụng C# trong đó kêu gọi có nguồn gốc Delphi dll sử dụng đoạn mã sau:Trả về một chuỗi từ delphi dll C# gọi trong 64 bit

C#

[DllImport("NativeDLL.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] 
public static extern int GetString(string out str); 

Delphi

function GetString(out a: PChar): Integer; stdcall; 
begin 
    a := PChar('abc'); 
    Result := 1; 
end; 

hoạt động tốt trong ứng dụng 32 bit. Nhưng khi tôi biên dịch cả hai C# exe và dll Delphi cho 64 bit, tôi nhận được một vấn đề lạ. Sau khi một cuộc gọi đến GetString trong trình gỡ lỗi Delphi tôi có thể thấy rằng một ngoại lệ được nêu ra ở đâu đó trong mã .NET và chuỗi sau xuất hiện trong cửa sổ Debugger Output: "Đã phát hiện lỗi nghiêm trọng c0000374". Google nói rằng lỗi này có liên quan đến tham nhũng đống. Tôi đã thử sử dụng công cụ sửa đổi thông số ref/var thay vì out/out. Vẫn không có may mắn. Tại sao tôi nhận được lỗi này? Tôi có nên sử dụng quy ước gọi điện khác cho 64 bit không?

BTW. Sự kết hợp sau đây hoạt động tốt:

C#

[DllImport("NativeDLL.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] 
public static extern string GetString(string a); 

Delphi

function GetString(a: PChar): PChar; stdcall; 
var 
    inp: string; 
begin 
    inp := a; 
    Result := PChar('test ' + inp); 
end; 

hoạt động tốt. Nhưng tôi cần trả về một chuỗi như một tham số ngoài.

Trả lời

17

Bạn không thể chuyển một chuỗi từ gốc được quản lý theo cách đó. Mã của bạn là sai trong 32 bit cũng có, bạn chỉ xảy ra để có được đi với nó. Phiên bản thứ hai của mã cũng sai. Nó chỉ xuất hiện để làm việc.

Bạn cần một trong hai:

  1. Phân bổ từ một đống chia sẻ do đó rằng mã quản lý có thể deallocate tắt rằng heap. Heap chia sẻ cho p/invoke là đống COM.
  2. Phân bổ bộ nhớ ở bên được quản lý và sao chép nội dung vào bộ đệm đó ở phía gốc.

Tùy chọn 2 luôn thích hợp hơn.Nó trông giống như thế này:

[DllImport("NativeDLL.dll", CharSet = CharSet.Unicode)] 
public static extern int GetString(StringBuilder str, int len); 

Về phía mẹ đẻ bạn sẽ phải

function GetString(str: PChar; len: Integer): Integer; stdcall; 
begin 
    StrLCopy(str, 'abc', len); 
    Result := 1; // real code would have real error handling 
end; 

Sau đó gọi đây là như vậy:

StringBuilder str = new StringBuilder(256); 
int retval = GetString(str, str.Capacity); 

Nếu bạn muốn thử phương án 1, có vẻ như mặt hàng được quản lý này:

[DllImport("NativeDLL.dll", CharSet = CharSet.Unicode)] 
public static extern int GetString(out string str); 

và giống như mẹ đẻ này:

function GetString(out str: PChar): Integer; stdcall; 
begin 
    str = CoTaskMemAlloc(SizeOf(Char)*(Length('abc')+1)); 
    StrCopy(str, 'abc'); 
    Result := 1; // real code would have real error handling 
end; 

Khi các bản sao mã số quản lý nội dung của str với giá trị chuỗi, nó sau đó gọi CoTaskMemFree trên con trỏ mà bạn trả lại.

Và đây là trivially dễ dàng để gọi:

string str; 
int retval = GetString(out str); 
+0

Cảm ơn bạn rất nhiều! Bạn đã cứu tôi nhiều giờ chiến đấu với vấn đề này. – Max

+0

'PChar'? trò chơi may rủi là 'PAnsiChar' hay' PWideChar'? (có, chúng tôi có thể suy ra nó là XE2 64 bit, nhưng chúng tôi không thể đảm bảo mã này sẽ không được biên dịch với FPC 64 bit hoặc sao chép sang dự án 32 bit, v.v.) –

+0

@ Arioch'The Sau đó sử dụng 'PWideChar' nếu bạn thích. –

Các vấn đề liên quan