Я начал обновлять приложение .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 (С#), я изменил их, чтобы быть более явным, на всякий случай.
---------------------------- Изменить ------------------ -------------------
Я уже писал ранее о 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.