【问题标题】:Did P/Invoke environment change in .NET 4.0?.NET 4.0 中的 P/Invoke 环境是否发生了变化?
【发布时间】:2012-01-26 03:29:01
【问题描述】:

我已经开始将 .NET 2.0 WinForms 应用程序升级到 .NET 4.0。好吧,升级过程只是切换平台目标的问题,但要让它真正工作。我想这就是它的全部了。

但 .NET 4.0 中的互操作性似乎发生了翻天覆地的变化。使用 DllImport(),应用程序嵌入了几个 Delphi dll。当应用程序以 .NET 2.0 为目标时,一切正常。但是当我将其更改为面向 .NET 4.0 时,事情开始变得混乱,就像某些东西正在破坏内存一样。

例如,它在奇怪的地方用“0”替换单个数字。在 IStream 中传递的数据将 8 个字符替换为 (Hex) 00 00 00 00 00 00 00 80,但只有大约 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.

【问题讨论】:

标签: delphi .net-4.0 pinvoke


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    看来这是 DllImport 属性中的调用约定属性的问题。应该是 Cdecl 而不是默认的 StdCall。从 2.0 迁移到 4.0 并在 VS2010 中运行时,我遇到了这个问题。请参阅此处的文章。 http://codenition.blogspot.com/2010/05/pinvokestackimbalance-in-net-40i-beg.html

    【讨论】:

    • 你描述的问题不一样。当我使用 Cdecl 而不是 StdCall 测试上面的示例代码时,我得到了堆栈不平衡异常。但是双方都声明了 StdCall,它不会使堆栈不平衡,它只会破坏 Delphi 内部。在您的情况下,是 MDA 帮助追踪了一个合法问题,在 Delphi 的情况下,MDA 实际上导致了问题。
    • 但是如果有人只是在看我原来的问题的标题,那么你在这里的回答是非常相关的,P/Invoke 在 4.0 中确实发生了变化。它与我的问题没有任何关系。
    【解决方案3】:

    布尔值是 Delphi 上的单字节类型。所以改变它们必须是单字节类型

    【讨论】:

    • 我不确定两边的大小,所以我把它改成了 WordBool 和短(都是 16 位)出现的所有地方。但这没有任何效果。
    【解决方案4】:

    我看到一个 Delphi dll 的类似问题: social_msdn
    我注意到我用 FreePascal(而不是 Delphi)编译的库即使在 VS2010 中也可以正常工作,没有任何问题。因此我不知道是不是 Delphi、.NET4 调试器或组合是问题的原因。

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

    【讨论】:

    • 我已经看到明显与启动断开连接的问题......除非它以某种方式破坏了 Delphi 内存管理器,以至于它为不同的东西重新分配相同的内存。它似乎以一种确定性但不可预测的方式运行。
    猜你喜欢
    • 1970-01-01
    • 2011-01-26
    • 2020-12-03
    • 2012-02-12
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多