2010-12-03 8 views
13

Zacząłem aktualizować aplikację WinForms .NET 2.0 do .NET 4.0. No cóż, proces aktualizacji był tylko kwestią zamiany celu platformy, ale sprawił, że faktycznie działa. Sądziłem, że to wszystko.Czy środowisko P/Invoke zmieniło się w .NET 4.0?

Wygląda jednak na to, że coś zmieniło się drastycznie w .NET 4.0 w odniesieniu do współpracy. Używając DllImport(), aplikacja osadza kilka bibliotek dll Delphi. Gdy aplikacja jest przeznaczona dla .NET 2.0, wszystko działa normalnie. Ale kiedy zmieniłem go na docelowy .NET 4.0, rzeczy zaczynają szaleć, jak coś psuje pamięć.

Na przykład zamienia pojedyncze cyfry na "0" w dziwnych miejscach. Dane przekazane w IStream otrzymują 8 znaków zastąpionych (Hex) 00 00 00 00 00 00 80, ale tylko około 70% czasu. Dwa kolejne wywołania w celu pobrania tej samej wartości zwracają różne wyniki (pobieranie wartości z pamięci podręcznej w pamięci, udane za pierwszym razem, niepowodzenie po raz drugi). Ciągi wysyłane do dziennika są ucinane.

Próbowałem wiele rzeczy, próbując uczynić konwencje wywoływania bardziej wyraźnymi, żadna z nich nie ma żadnego efektu. Wszystkie ciągi są traktowane jako [MarshalAs (UnmanagedType.LPWStr)] po stronie .NET i PWChar po stronie Delphi.

Co zmieniło się w .NET 4.0, które złamałoby P/Invoke w ten sposób?

---------------------------- Edytuj ----------------- --------------------

Oto najprostszy przykład. To generuje plik PDF, który czasami działa poprawnie, ale częściej kończy się uszkodzony (i działa poprawnie w .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 i krótkie były pierwotnie logiczna (Delphi) i bool (C#), ja je zmienił być bardziej konkretnym, na wszelki wypadek.

---------------------------- Edytuj ----------------- --------------------

Wydaje się, że rzeczy, które napisałem wcześniej o WinFormach, okazały się nie całkiem odpowiednie, odtworzyłem jeden z problemów bez żadnego interfejsu użytkownika. Poniższy program generuje 0,1,2,3,4,5,6,7,8,9 pod 2,0/3,5, ale 0, -1, -1, -1, -1, -1, -1, - 1, -1 poniżej 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)); 
      } 
     } 
    } 
} 

i po stronie Delphi (skompilowane z 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

Może pokażesz nam podpis PInvoke? – JaredPar

+0

Ten post http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/922fa431-5426-48c6-8949-538dfbb2f266 sugeruje, że przetaczanie uległ zmianie 4,0, ale nie zapewnia pełną listę co się zmieniło. –

+0

Może interesujące wiedzieć: [Visual Studio 2010 SP1] (http://support.microsoft.com/kb/983509) nie ma ** ** rozwiązać ten problem, właśnie próbowałem go tutaj. –

Odpowiedz

6

Wygląda na błąd w debugerze Visual Studio 2010. Wydaje się, że jest to niewzruszona pamięć, która do niej nie należy. Wszystkie problemy, które zauważyłem (wszystkie z nich można odtworzyć niezawodnie) znikają całkowicie, jeśli uruchomię aplikację bezpośrednio, zamiast przez Visual Studio 2010.

Błąd znajduje się w Managed Debug Assistant. Jeśli wyłączysz go całkowicie (zestaw HKLM \ Software \ Microsoft.NETFramework \ MDA = "0"), problem zniknie. Ale oczywiście tracisz możliwość debugowania.

0

Boolean jest typem jeden bajt na Delphi. Więc zmiana ich musi być jednobajtowym typem:

+0

Nie byłem pewien, jaki rozmiar ma jedna strona, więc zmieniłem go na WordBool i krótki (oba 16 bitów) wszędzie, gdzie się pojawił. Ale to nie miało wpływu. –

0

widzę podobny problem z DLL Delphi: social_msdn
Zauważyłem, że moja biblioteka skompilowana z FreePascal (zamiast Delphi) działa nawet w VS2010 bez żadnych problemów.Dlatego nie wiem, czy Delphi, debuggerlub kombinacja jest przyczyną problemu.

Istnieją pewne dowody, że pamięć przydzielona dll podczas rozruchu (na przykład w sekcji inicjalizacji) jest dotknięte korupcją pamięci.

+0

Widziałem wyraźnie problemy z uruchomieniem ... odłączony chyba to jakoś psuje menedżera pamięci Delphi do punktu, to ponowne przydzielenie samą pamięć do różnych rzeczy. Wydaje się, że zachowuje się w deterministyczny, ale nieprzewidywalny sposób. –

3

wydaje się, że jest to problem z powołania własności Convention w atrybucie dllimport. Powinien być Cdecl nie domyślnym StdCall. Miałem ten problem podczas migracji z wersji 2.0 na 4.0 i działającej w VS2010. Zobacz artykuł tutaj. http://codenition.blogspot.com/2010/05/pinvokestackimbalance-in-net-40i-beg.html

+0

Problem, który opisujesz, jest inny. Kiedy przetestowałem powyższy przykładowy kod z Cdecl zamiast StdCall, otrzymałem wyjątki niewyważenia stosu. Ale gdy obie strony zadeklarowały StdCall, nie powoduje to braku równowagi stosu, po prostu psuje wewnętrzne elementy Delphi. W twoim przypadku to MDA pomogło wyśledzić uzasadnioną kwestię, w przypadku Delphi MDA faktycznie powoduje problem. –

+0

Ale jeśli ktoś tylko patrzy na tytuł mojego oryginalnego pytania, twoja odpowiedź tutaj jest całkiem istotna, P/Invoke zmieniło się w 4.0. To po prostu nie miało nic wspólnego z moim problemem. –

Powiązane problemy