2010-12-03 40 views
13

Tôi đã bắt đầu nâng cấp ứng dụng .NET 2.0 WinForms lên .NET 4.0. Vâng, OK, quá trình nâng cấp chỉ là vấn đề chuyển đổi mục tiêu nền tảng, nhưng làm cho nó thực sự hoạt động. Tôi cho rằng đó là tất cả sẽ có nó.Đã P/Invoke thay đổi môi trường trong .NET 4.0?

Nhưng có vẻ như điều gì đó đã thay đổi đáng kể trong .NET 4.0 liên quan đến interop. Sử dụng DllImport(), ứng dụng nhúng một vài tệp dll Delphi. Khi ứng dụng nhắm mục tiêu .NET 2.0, mọi thứ hoạt động bình thường. Nhưng khi tôi thay đổi nó để nhắm mục tiêu .NET 4.0, công cụ bắt đầu đi haywire, giống như một cái gì đó đang làm hỏng bộ nhớ.

Ví dụ: nó thay thế các chữ số duy nhất bằng "0" ở những vị trí lạ. Dữ liệu được truyền trong IStream được thay thế bằng 8 ký tự bằng (Hex) 00 00 00 00 00 00 00 80, nhưng chỉ khoảng 70% thời gian. Hai cuộc gọi liên tiếp để truy lục cùng một giá trị trả lại các kết quả khác nhau (lấy một giá trị từ bộ đệm trong bộ nhớ, thành công lần đầu tiên, không thực hiện lần thứ hai). Các chuỗi được gửi tới nhật ký được hiển thị bị cắt bớt.

Tôi đã thử rất nhiều nội dung cố gắng thực hiện các quy ước gọi điện rõ ràng hơn, không có tác vụ nào có hiệu lực. Tất cả các chuỗi được xử lý như [MarshalAs (UnmanagedType.LPWStr)] ở phía .NET và PWChar ở phía Delphi.

Điều gì đã thay đổi trong .NET 4.0 sẽ phá vỡ P/Gọi như thế này?

---------------------------- Chỉnh sửa ----------------- --------------------

Dưới đây là ví dụ đơn giản nhất. Nó tạo một file PDF mà đôi khi hoạt động chính xác, nhưng thường xuyên hơn kết thúc lên bị hỏng (và hoạt động chính xác trong .NET 2.0):

[DllImport(DLLName)] 
public static extern void SetDBParameters(
    [MarshalAs(UnmanagedType.LPWStr)] string Server, 
    [MarshalAs(UnmanagedType.LPWStr)] string Database, 
    [MarshalAs(UnmanagedType.LPWStr)] string User, 
    [MarshalAs(UnmanagedType.LPWStr)] string Password, 
    short IntegratedSecurity); 

procedure SetDBParameters(Server, Database, User, Password: PWChar; 
    IntegratedSecurity: WordBool); stdcall; 


[DllImport(DLLName)] 
public static extern short GeneratePDF(
    [MarshalAs(UnmanagedType.LPWStr)] string Param1, 
    [MarshalAs(UnmanagedType.LPWStr)] string Param2, 
    [MarshalAs(UnmanagedType.LPWStr)] string Param3, 
    [MarshalAs(UnmanagedType.LPWStr)] string Param4, 
    out IStream PDFData); 

function GeneratePDF(Param1, Param2, Param3, Param4: PWChar; 
    out PDFData: IStream): WordBool; stdcall; 

private byte[] ReadIStream(IStream Stream) 
{ 
    if (Stream == null) 
     return null; 
    System.Runtime.InteropServices.ComTypes.STATSTG streamstats; 
    Stream.Stat(out streamstats, 0); 
    Stream.Seek(0, 0, IntPtr.Zero); 
    if (streamstats.cbSize <= 0) 
     return null; 
    byte[] result = new byte[streamstats.cbSize]; 
    Stream.Read(result, (int)streamstats.cbSize, IntPtr.Zero); 
    return result; 
} 

WordBool và ngắn ban đầu boolean (Delphi) và bool (C#), tôi đã thay đổi họ để rõ ràng hơn, chỉ trong trường hợp.

---------------------------- Chỉnh sửa ----------------- --------------------

Nội dung tôi đã viết trước đó về WinForms dường như đã không trở nên hoàn toàn phù hợp, tôi đã tạo lại một trong những vấn đề không có giao diện người dùng nào. Chương trình sau tạo ra 0,1,2,3,4,5,6,7,8,9 dưới 2,0/3,5, nhưng 0, -1, -1, -1, -1, -1, -1, - 1, -1 dưới 4.0.

using System; 
using System.Runtime.InteropServices; 

namespace TestNet4interop 
{ 
    static class Program 
    { 
     [DllImport("TestSimpleLibrary.dll", PreserveSig=true, CallingConvention = CallingConvention.StdCall)] 
     public static extern void AddToList(long value); 

     [DllImport("TestSimpleLibrary.dll", PreserveSig=true, CallingConvention = CallingConvention.StdCall)] 
     public static extern int GetFromList(long value); 

     static void Main() 
     { 
      for (long i = 0; i < 10; i++) 
      { 
       AddToList(i); 
       Console.WriteLine(GetFromList(i)); 
      } 
     } 
    } 
} 

Và phía bên Delphi (biên soạn với Delphi 2007):

library TestSimpleLibrary; 

uses 
    SysUtils, 
    Classes; 

{$R *.res} 

var 
    List: TStringList; 

procedure AddToList(value: int64); stdcall; 
begin 
    List.Add(IntToStr(value)); 
end; 

function GetFromList(value: int64): integer; stdcall; 
begin 
    result := List.IndexOf(IntToStr(value)); 
end; 

exports 
    AddToList, 
    GetFromList; 

begin 
    List := TStringList.Create; 
end. 
+0

Bạn có thể hiển thị chữ ký PInvoke cho chúng tôi không? – JaredPar

+0

Bài đăng này http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/922fa431-5426-48c6-8949-538dfbb2f266 cho thấy rằng bản sửa lỗi đã thay đổi trong 4.0, nhưng không cung cấp danh sách đầy đủ về những gì đã thay đổi. –

+0

Có thể thú vị khi biết: [Visual Studio 2010 SP1] (http://support.microsoft.com/kb/983509) không ** không ** khắc phục sự cố, chúng tôi vừa thử ở đây. –

Trả lời

6

Dường như có lỗi trong trình gỡ rối Visual Studio 2010. Nó có vẻ là bộ nhớ clobbering mà không thuộc về nó. Tất cả các vấn đề tôi đã quan sát (tất cả đều có thể được sao chép đáng tin cậy) biến mất hoàn toàn nếu tôi chạy ứng dụng trực tiếp, thay vì thông qua Visual Studio 2010.

Lỗi này thực sự nằm trong Trợ lý gỡ lỗi được quản lý. Nếu bạn tắt hoàn toàn (thiết lập HKLM \ Software \ Microsoft.NETFramework \ MDA = "0"), sự cố sẽ biến mất. Nhưng tất nhiên bạn mất một số khả năng gỡ lỗi bằng cách làm như vậy.

0

Boolean là một loại một byte trên Delphi. Vì vậy, việc thay đổi chúng phải bằng một loại byte

+0

Tôi đã không chắc chắn những gì kích thước một trong hai bên giả định, vì vậy tôi đã thay đổi nó để WordBool và ngắn (cả 16 bit) ở khắp mọi nơi nó xuất hiện. Nhưng điều đó không có hiệu lực. –

0

Tôi thấy một vấn đề tương tự với một dll Delphi: social_msdn
Tôi đã nhận thấy rằng thư viện của tôi được biên dịch với FreePascal (thay vì Delphi) hoạt động ngay cả trong VS2010 mà không gặp bất kỳ sự cố nào.Vì vậy, tôi không biết nếu Delphi, trình gỡ lỗi .NET4 hoặc kết hợp là lý do cho sự cố.

Có một số bằng chứng cho thấy bộ nhớ được cấp phát trong khi khởi động dll (ví dụ: trong phần khởi tạo) bị ảnh hưởng bởi tham nhũng bộ nhớ.

+0

Tôi đã nhìn thấy vấn đề rõ ràng bị ngắt kết nối từ khởi động ... trừ khi nó bằng cách nào đó làm hỏng trình quản lý bộ nhớ Delphi đến mức nó phân bổ lại cùng một bộ nhớ cho những thứ khác nhau. Nó có vẻ cư xử theo một cách xác định nhưng không thể đoán trước. –

3

Xuất hiện rằng đây là sự cố với thuộc tính Công ước cuộc gọi trong thuộc tính DllImport. Nên là Cdecl không phải là StdCall mặc định. Tôi đã có vấn đề này khi di chuyển từ 2,0 đến 4,0 và chạy trong VS2010. Xem bài viết tại đây. http://codenition.blogspot.com/2010/05/pinvokestackimbalance-in-net-40i-beg.html

+0

Sự cố bạn mô tả là khác nhau. Khi tôi kiểm tra mã mẫu ở trên với Cdecl thay vì StdCall, tôi có ngoại lệ mất cân bằng chồng. Nhưng với cả hai bên tuyên bố StdCall, nó không mất cân bằng ngăn xếp, nó chỉ làm hỏng ruột Delphi. Trong trường hợp của bạn, đó là MDA đã giúp theo dõi một vấn đề chính đáng, trong trường hợp Delphi, MDA thực sự gây ra vấn đề. –

+0

Nhưng nếu ai đó chỉ nhìn vào tiêu đề của câu hỏi ban đầu của tôi, câu trả lời của bạn ở đây là khá có liên quan, P/Invoke đã thay đổi trong 4.0. Nó không liên quan gì đến vấn đề của tôi. –

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