在.NET 4.0中,P / Invoke环境是否发生了变化?

时间:2010-12-03 18:00:48

标签: delphi .net-4.0 pinvoke

我已经开始将.NET 2.0 WinForms应用程序升级到.NET 4.0。嗯,好的,升级过程只是切换平台目标,但让它真正起作用。我认为这就是它的全部内容。

但似乎.NET 4.0中关于互操作的内容发生了巨大变化。使用DllImport(),该应用程序嵌入了几个Delphi dll。当应用程序面向.NET 2.0时,一切正常。但是当我把它改成目标.NET 4.0时,东西开始变得乱七八糟,就像破坏内存的东西一样。

例如,它在奇怪的地方用“0”替换单个数字。在IStream中传递的数据用(Hex)00 00 00 00 00 00 00 80替换8个字符,但只有大约70%的时间。两次连续调用以检索相同的值会返回不同的结果(从内存中的缓存中检索值,第一次成功,第二次失败)。发送到日志的字符串显示为截断。

我尝试过很多东西试图使调用约定更加明确,但没有任何效果。所有字符串都在.NET端处理为[MarshalAs(UnmanagedType.LPWStr)],在Delphi端处理为PWChar。

.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最初是boolean(Delphi)和bool(C#),为了以防万一,我将它们更改为更明确。

----------------------------编辑------------------ -------------------

我之前写的关于WinForms的东西似乎已经证明并不是完全相关的,我在没有任何UI的情况下重新创建了其中一个问题。以下程序在2.0 / 3.5下生成0,1,2,3,4,5,6,7,8,9,但是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.

4 个答案:

答案 0 :(得分:6)

它似乎是Visual Studio 2010调试器中的错误。它似乎是不属于它的破坏性记忆。如果我直接运行应用程序而不是通过Visual Studio 2010,我观察到的所有问题(所有问题都可以可靠地再现)都会完全消失。

该错误实际上在托管调试助手中。如果你完全关闭它(设置HKLM \ Software \ Microsoft.NETFramework \ MDA =“0”),问题就会消失。但是,你当然会失去一些调试功能。

答案 1 :(得分:3)

出现这是DllImport属性中的Calling Convention属性的问题。应该是Cdecl而不是默认的StdCall。从2.0迁移到4.0并在VS2010中运行时遇到此问题。请看这里的文章。 http://codenition.blogspot.com/2010/05/pinvokestackimbalance-in-net-40i-beg.html

答案 2 :(得分:0)

Boolean是Delphi上的一个字节类型。因此,更改它们必须使用单字节类型

答案 3 :(得分:0)

我发现Delphi dll存在类似的问题: social_msdn我注意到我用FreePascal编译的库(而不是Delphi)甚至在VS2010中也没有任何问题。因此我不知道Delphi,.NET4调试器或组合是否是导致问题的原因。

有证据表明在dll启动期间分配的内存(例如在初始化部分中)会受到内存损坏的影响。