J'ai commencé à mettre à niveau une application .NET 2.0 WinForms vers .NET 4.0. Eh bien, OK, le processus de mise à niveau était juste une question de changement de cible de la plate-forme, mais le faire fonctionner réellement. J'ai supposé que tout serait là.L'environnement P/Invoke at-il été modifié dans .NET 4.0?
Mais il semble que quelque chose a radicalement changé dans .NET 4.0 en ce qui concerne Interop. En utilisant DllImport(), l'application embarque quelques DLL Delphi. Lorsque l'application cible .NET 2.0, tout fonctionne normalement. Mais quand je l'ai changé pour cibler .NET 4.0, les choses commencent à se détraquer, comme si quelque chose corrompait la mémoire.
Par exemple, il remplace un chiffre « 0 » dans des endroits étranges. Les données transmises dans un flux IStream sont remplacées par 8 caractères (Hex) 00 00 00 00 00 00 00 80, mais seulement 70% du temps. Deux appels consécutifs pour récupérer la même valeur renvoient des résultats différents (récupérer une valeur d'un cache en mémoire, réussir la première fois, échouer la deuxième fois). Les chaînes envoyées à un journal apparaissent tronquées.
J'ai essayé beaucoup de choses en essayant de rendre plus explicites les conventions d'appel, rien de tout cela n'a aucun effet. Toutes les chaînes sont traitées comme [MarshalAs (UnmanagedType.LPWStr)] sur le côté .NET et PWChar sur le côté Delphi.
Ce qui a changé dans .NET 4.0 qui briserait P/Invoke comme ça?
---------------------------- Modifier ----------------- --------------------
Voici l'exemple le plus simple. Il génère un fichier PDF qui fonctionne parfois correctement, mais plus souvent finit par corrompre (et fonctionne correctement dans .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 court et étaient à l'origine booléenne (Delphi) et bool (C#), je les ai changé être plus explicite, juste au cas où.
---------------------------- Modifier ----------------- --------------------
les choses que je l'ai déjà écrit au sujet WinForms semble avoir avéré être pas tout à fait pertinente, je l'ai recréé l'une des questions sans interface utilisateur. Le programme suivant génère 0,1,2,3,4,5,6,7,8,9 sous 2,0/3,5, mais 0, -1, -1, -1, -1, -1, -1, - 1, -1 sous 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));
}
}
}
}
et le côté Delphi (compilé avec 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.
Pouvez-vous nous montrer la signature PInvoke? – JaredPar
Cet article http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/922fa431-5426-48c6-8949-538dfbb2f266 suggère que marshalling a bien changé en 4.0, mais ne fournit pas une liste complète de ce qui a changé. –
Peut-être intéressant de savoir: [Visual Studio 2010 SP1] (http://support.microsoft.com/kb/983509) ** ne ** corrige pas le problème, nous venons de l'essayer ici. –