【问题标题】:Marshal.PtrToStructure access violationMarshal.PtrToStructure 访问冲突
【发布时间】:2016-08-01 16:03:39
【问题描述】:

任何人都可以看到它失败的原因吗?如果我用“ref BITMAP lpvObject”替换“out IntPtr lpvObject”,我可以让它以这种方式工作。但我看不出代码有什么问题。

using System;
using System.Runtime.InteropServices;
using System.Drawing;

namespace Program
{
  class Core
  {
    [StructLayout(LayoutKind.Sequential)]    
    public struct BITMAP
    {
        public Int32  bmType;
        public Int32  bmWidth;
        public Int32  bmHeight;
        public Int32  bmWidthBytes;
        public UInt16 bmPlanes;
        public UInt16 bmBitsPixel;
        public IntPtr bmBits;
    }


    [DllImport("gdi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
      public static extern int GetObject ( IntPtr hgdiobj, int cbBuffer, out IntPtr lpvObject );


    static void Main(string[] args)
    {
      Bitmap TestBmp = new Bitmap ( 10, 10 );
      BITMAP BitmapStruct = new BITMAP();
      IntPtr pBitmapStruct, pBitmapStructSave;
      int    Status;

      pBitmapStructSave = pBitmapStruct = Marshal.AllocHGlobal ( Marshal.SizeOf(BitmapStruct) );

      Status = GetObject ( TestBmp.GetHbitmap(), Marshal.SizeOf (BitmapStruct), out pBitmapStruct );

      Console.WriteLine ( "\nBytes returned is " + Status + ", buffer address = " + pBitmapStruct );

      try
      {
        BitmapStruct = (BITMAP) Marshal.PtrToStructure ( pBitmapStruct, typeof(BITMAP) );
      }
      catch ( Exception Ex )
      {
        Console.WriteLine ( Ex.Message );
      }

      Marshal.FreeHGlobal(pBitmapStructSave);
    }
  }
}

输出是:

返回的字节数为 32,缓冲区地址 = 42949672960

未处理的异常:System.AccessViolationException:试图读取或写入受保护的内存。这通常表明其他内存已损坏。

在 System.Runtime.InteropServices.Marshal.PtrToStructure(IntPtr ptr, Type structureType)

在 D:\data\Projects\Test\Test\Program.cs:line 41 中的 Program.Core.Main(String[] args) 处

【问题讨论】:

  • out IntPtr lpvObject -> IntPtr lpvObject
  • 使用 int GetObject (IntPtr hgdiobj, int cbBuffer, out BITMAP lpobj)。轻松愉快。
  • 是的,正如我所说,它也适用于 ref BITMAP。这样做的问题是,当函数返回一个空的第三个参数时,它需要额外的处理。最简单的解决方案仍然是将指针作为输出。无论如何,我需要知道 IntPtr 类型是隐式输出。一千年后我都猜不到。我也不会通过搜索找到它。

标签: c#


【解决方案1】:

可能是因为您使用了错误的签名?

[DllImport("gdi32.dll")] static extern int GetObject(IntPtr hgdiobj, int cbBuffer, IntPtr lpvObject);

http://www.pinvoke.net/default.aspx/gdi32/GetObject.html

另外,out 意味着需要在方法中初始化值,这可能不会发生,因为您已经定义了 lpvObject

【讨论】:

  • API 规范规定了输出的第三个参数:Out LPVOID lpvObject。所以签名没问题。程序输出表明 out 参数按预期返回了一个指针值。
  • 好的,谢谢。我删除了“out”,它现在可以工作了。我必须重新阅读 out 关键字的使用方式。
  • @user118708 看the example useage,需要传入一个有效缓冲区的地址。我不知道为什么它在文档中被标记为 OUT 但它肯定需要传入一个有效的对象
  • 这是一个OUT,因为有两个成功的结果。首先,当缓冲区被填满时。其次,当缓冲区太小并且指针返回空值时。我很困惑,因为我没有意识到 IntPtr 在 p/invoke 的上下文中默认是一个 out 参数。我试过但没有找到任何相关信息。我想我必须是 MS 核心圈内的人才能访问此类信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-06
  • 2013-02-26
  • 2015-04-23
  • 2018-04-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多