Я начал обновление приложения .NET 2.0 WinForms до .NET 4.0. Ну, ладно, процесс обновления был просто вопросом переключения целевой платформы, но заставлял ее работать на самом деле. Я предполагал, что это все, что будет.
Но, похоже, что-то кардинально изменилось в .NET 4.0 в отношении взаимодействия. Используя DllImport (), приложение встраивает пару DLL Delphi. Когда приложение предназначено для .NET 2.0, все работает нормально. Но когда я изменил его на целевую .NET 4.0, все начинает ухудшаться, как будто что-то портит память.
Например, он заменяет однозначные числа на «0» в незнакомых местах. Данные, передаваемые в IStream, заменяются 8 символами на (Hex) 00 00 00 00 00 00 00 80, но только в 70% случаев. Два последовательных вызова для получения одного и того же значения возвращают разные результаты (получение значения из кеша в памяти, успешно в первый раз, во втором случае неудача). Строки, отправляемые в журнал, отображаются усеченными.
Я пробовал много вещей, пытаясь сделать соглашения о вызовах более явными, ни одно из них не имеет никакого эффекта. Все строки обрабатываются как [MarshalAs (UnmanagedType.LPWStr)] на стороне .NET и PWChar на стороне Delphi.
Что изменилось в .NET 4.0, что сломало бы P / Invoke следующим образом?
---------------------------- Редактировать ------------------ -------------------
Вот самый простой пример. Он генерирует PDF, который иногда работает правильно, но чаще оказывается поврежденным (и работает правильно в .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 и short изначально были булевыми (Delphi) и bool (C #), я изменил их на более явные, на всякий случай.
---------------------------- Редактировать ------------------ -------------------
То, что я писал ранее о WinForms, похоже, оказалось не совсем актуальным, я воссоздал одну из проблем без какого-либо пользовательского интерфейса. Следующая программа генерирует 0,1,2,3,4,5,6,7,8,9 в 2.0 / 3.5, но 0, -1, -1, -1, -1, -1, -1, - 1, -1 до 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));
}
}
}
}
И сторона Delphi (скомпилировано с 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.