我开始将一个 .NET 2.0 WinForms 应用程序升级到.NET 4.0。好吧,升级过程只是切换平台目标,但使它实际工作是另一回事。我假设这就是所有要做的事情。
但似乎在 .NET 4.0 中有一些关于交互的重大变化。使用 DllImport(),应用程序嵌入了几个 Delphi dll。当应用程序目标为 .NET 2.0 时,一切都正常工作。但是当我将其更改为目标为 .NET 4.0 时,一切都开始变得混乱,就像某些东西正在破坏内存一样。
例如,在奇怪的地方用“0”替换单个数字。传递给 IStream 的数据将有 8 个字符替换为(十六进制)00 00 00 00 00 00 00 80,但仅约 70% 的时间。连续两次调用以检索相同的值会返回不同的结果(从内存高速缓存中检索值,第一次成功,第二次失败)。发送到日志的字符串显示被截断。
我尝试了很多东西尝试使调用约定更明确,但没有任何效果。在 .NET 端,所有字符串都处理为 [MarshalAs(UnmanagedType.LPWStr)],Delphi 端则为 PWChar。
.NET 4.0 中改变了什么,会导致这样破坏 P/Invoke 呢?
但似乎在 .NET 4.0 中有一些关于交互的重大变化。使用 DllImport(),应用程序嵌入了几个 Delphi dll。当应用程序目标为 .NET 2.0 时,一切都正常工作。但是当我将其更改为目标为 .NET 4.0 时,一切都开始变得混乱,就像某些东西正在破坏内存一样。
例如,在奇怪的地方用“0”替换单个数字。传递给 IStream 的数据将有 8 个字符替换为(十六进制)00 00 00 00 00 00 00 80,但仅约 70% 的时间。连续两次调用以检索相同的值会返回不同的结果(从内存高速缓存中检索值,第一次成功,第二次失败)。发送到日志的字符串显示被截断。
我尝试了很多东西尝试使调用约定更明确,但没有任何效果。在 .NET 端,所有字符串都处理为 [MarshalAs(UnmanagedType.LPWStr)],Delphi 端则为 PWChar。
.NET 4.0 中改变了什么,会导致这样破坏 P/Invoke 呢?
[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中的boolean和C#中的bool,我将它们更改为更加明确,以防万一。
----------------------------编辑-------------------------------------
我之前写的关于WinForms的内容似乎并不完全相关,我已经重新创建了一个没有任何UI的问题。在2.0/3.5下,以下程序生成0,1,2,3,4,5,6,7,8,9,但在4.0下生成0,-1,-1,-1,-1,-1,-1,-1,-1。
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.