没有的P / Invoke环境变化在.NET 4.0中? [英] Did P/Invoke environment change in .NET 4.0?

查看:171
本文介绍了没有的P / Invoke环境变化在.NET 4.0中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经开始升级。NET 2.0 WinForms应用程序到.NET 4.0。好了,好了,在升级过程中交换平台的目标,但是使其实际工作只是一个问题。我以为,一切都将有它。

但似乎事情彻底改变了.NET 4.0关于互操作。使用的DllImport(),应用程序嵌入了一对夫妇德尔福的DLL。当应用程序面向.NET 2.0,一切都正常。但是,当我把它改为面向.NET 4.0,东东开始会失控,喜欢的东西被破坏内存。

例如,它取代了单一的在陌生的地方与数字0。在一个IStream通过数据得到8个字符(十六进制)换成00 00 00 00 00 00 00 80,但只有约70%的时间。两个连续呼叫以检索相同的值返回不同的结果(从检索在存储器中的高速缓存的值,之后的第一时间,未通过2次)。字符串被发送到一个日志都出现了截断。

我试过很多东西,试图让调用约定更加明确,没有有任何影响。所有字符串在.NET端和PWChar德尔福侧处理为[的MarshalAs(UnmanagedType.LPWStr)。

什么改变了在.NET 4.0中,将打破的P / Invoke这样吗?

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

下面是最简单的例子。它生成一个PDF有时正常工作,但更多的最终损坏的(和.NET 2.0中正常工作):

  [的DllImport(DLLNAME)
公共静态外部无效SetDBParameters(
    [的MarshalAs(UnmanagedType.LPWStr)字符串服务器,
    [的MarshalAs(UnmanagedType.LPWStr)字符串数据库,
    [的MarshalAs(UnmanagedType.LPWStr)字符串用户,
    [的MarshalAs(UnmanagedType.LPWStr)字符串密码,
    短IntegratedSecurity);

程序SetDBParameters(服务器,数据库,用户,密码:PWChar;
    IntegratedSecurity:WordBool); STDCALL;


[的DllImport(DLLNAME)
公共静态外部短路GeneratePDF(
    [的MarshalAs(UnmanagedType.LPWStr)字符串参数1,
    [的MarshalAs(UnmanagedType.LPWStr)字符串参数2,
    [的MarshalAs(UnmanagedType.LPWStr)字符串参数3,
    [的MarshalAs(UnmanagedType.LPWStr)字符串Param4,
    出的IStream PDFData);

功能GeneratePDF(参数1,参数2,参数3,Param4:PWChar;
    出PDFData:IStream的):WordBool; STDCALL;

私人字节[] ReadIStream(的IStream流)
{
    如果(流== NULL)
        返回null;
    System.Runtime.InteropServices.ComTypes.STATSTG streamstats;
    Stream.Stat(出streamstats,0);
    Stream.Seek(0,0,IntPtr.Zero);
    如果(streamstats.cbSize&所述; = 0)
        返回null;
    byte []的结果=新的字节[streamstats.cbSize]
    Stream.Read(结果,(INT)streamstats.cbSize,IntPtr.Zero);
    返回结果;
}
 

WordBool和短最初布尔(德尔福)和布尔(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。

 使用系统;
使用了System.Runtime.InteropServices;

命名空间TestNet4interop
{
    静态类节目
    {
        [的DllImport(TestSimpleLibrary.dll,preserveSig = TRUE,CallingConvention = CallingConvention.StdCall)
        公共静态外部无效AddToList(long值);

        [的DllImport(TestSimpleLibrary.dll,preserveSig = TRUE,CallingConvention = CallingConvention.StdCall)
        公共静态外部INT GetFromList(long值);

        静态无效的主要()
        {
            为(长I = 0;我小于10;我++)
            {
                AddToList(ⅰ);
                Console.WriteLine(GetFromList(ⅰ));
            }
        }
    }
}
 

和Delphi的侧面(与2007年德尔福编译):

 库TestSimpleLibrary;

用途
  SysUtils单元,
  类;

{$ R * .RES}

变种
   清单:TStringList中;

程序AddToList(价值:Int64的); STDCALL;
开始
   List.Add(IntToStr(值));
结束;

功能GetFromList(价值:Int64的):整数; STDCALL;
开始
   结果:= List.IndexOf(IntToStr(值));
结束;

出口
   添加到列表,
   GetFromList;

开始
   清单:= TStringList.Create;
结束。
 

解决方案

这似乎是在Visual Studio 2010中的调试器的错误。这似乎是弄错不属于它的内存。所有我所观察到的问题(这些都可以可靠地转载)消失,而不是通过Visual Studio 2010的完全,如果我直接运行应用程序。

该错误实际上是在托管调试助手。如果你将其完全关闭(设置HKLM \ SOFTWARE \ Microsoft.NETFramework \ MDA =0),问题消失。当然,你这样做失去了一些调试功能。

I've started upgrading a .NET 2.0 WinForms application to .NET 4.0. Well, OK, the upgrade process was just a matter of switching platform target, but making it actually work. I assumed that's all there would be to it.

But it seems that something drastically changed in .NET 4.0 regarding interop. Using DllImport(), the application embeds a couple Delphi dlls. When the application targets .NET 2.0, everything works normally. But when I changed it to target .NET 4.0, stuff starts going haywire, like something is corrupting memory.

For example, it replaces single digits with "0" in strange places. Data passed in an IStream gets 8 characters replaced with (Hex) 00 00 00 00 00 00 00 80, but only about 70% of the time. Two consecutive calls to retrieve the same value return different results (retrieving a value from a cache in memory, succeeds the first time, fails the second time). Strings being sent to a log are showing up truncated.

I've tried lots of stuff trying to make calling conventions more explicit, none of it has any effect. All strings are handled as [MarshalAs(UnmanagedType.LPWStr)] on the .NET side and PWChar on the Delphi side.

What changed in .NET 4.0 that would break P/Invoke like this?

----------------------------Edit-------------------------------------

Here's the simplest example. It generates a PDF which sometimes works correctly, but more frequently ends up corrupt (and works correctly in .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 and short were originally boolean (Delphi) and bool (C#), I changed them to be more explicit, just in case.

----------------------------Edit-------------------------------------

The stuff I wrote earlier about WinForms appears to have turned out to be not completely relevant, I've recreated one of the issues without any UI. The following program generates 0,1,2,3,4,5,6,7,8,9 under 2.0/3.5, but 0,-1,-1,-1,-1,-1,-1,-1,-1 under 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));
            }
        }
    }
}

And the Delphi side (compiled with 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.

解决方案

It appears to be a bug in the Visual Studio 2010 debugger. It seems to be clobbering memory that doesn't belong to it. All of the problems I've observed (all of which can be reproduced reliably) disappear completely if I run the application directly, instead of through Visual Studio 2010.

The bug is actually in the Managed Debug Assistant. If you turn it off completely (set HKLM\Software\Microsoft.NETFramework\MDA = "0"), the problem goes away. But of course you lose some debugging capability by doing so.

这篇关于没有的P / Invoke环境变化在.NET 4.0中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆