【问题标题】:AccessViolationException using unmanaged C++ DLL使用非托管 C++ DLL 的 AccessViolationException
【发布时间】:2018-08-18 18:00:38
【问题描述】:

我第一次尝试在 C# 应用程序中使用非托管 C++ DLL(“res_lib”)。我使用cppsharp 来生成PInvoke 代码:例如,我试图调用的函数/方法之一是get_system_snapshot。在.h 文件中,这被定义为

SYS_INT SYS_ERR get_system_snapshot(SNAPSHOT_PARMS* snapshotp);

SYS_INTSYS_ERR 等同于 int32_tSNAPSHOT_PARMS

typedef struct SNAPSHOT_PARMS
{
    SYS_ULONG   size;
    SYS_UINT    count;
    SYS_CHAR    serial_no[600];
} SYS_PACK_DIRECTIVE SYS_SNAPSHOT_PARMS;

cppsharp 已经把它变成了下面的代码 sn-ps:

DllImport

[SuppressUnmanagedCodeSecurity]
[DllImport("res_lib", CallingConvention = CallingConvention.StdCall,
                EntryPoint="get_system_snapshot")]       
   internal static extern int GetSystemSnapshot(IntPtr snapshotp);  

对象

 public unsafe partial class SNAPSHOT_PARMS : IDisposable
    {
 [StructLayout(LayoutKind.Explicit, Size = 608)]
  public partial struct __Internal
    {
        [FieldOffset(0)]
        internal uint size;

        [FieldOffset(4)]
        internal uint count;

        [FieldOffset(8)]
        internal fixed sbyte serial_no[600];

        [SuppressUnmanagedCodeSecurity]
        [DllImport("res_lib", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.ThisCall,
            EntryPoint="??0SNAPSHOT_PARMS@@QAE@ABU0@@Z")]
        internal static extern global::System.IntPtr cctor(global::System.IntPtr instance, global::System.IntPtr _0);
    }
}

 public SNAPSHOT_PARMS()
        {
             __Instance = Marshal.AllocHGlobal(sizeof(global::res_lib.SNAPSHOT_PARMS.__Internal));         
            __ownsNativeInstance = true;
            NativeToManagedMap[__Instance] = this;
        }

主要代码

  static void Main(string[] args)
        {
            SNAPSHOT_PARMS p = new SNAPSHOT_PARMS();
            var result = res_lib.res_lib.GetSystemSnapshot(p);
        }

 public static unsafe int GetSystemSnapshot(global::res_lib.SNAPSHOT_PARMS snapshotp)
        {         
            var __arg0 = ReferenceEquals(snapshotp, null) ? global::System.IntPtr.Zero : snapshotp.__Instance;
            var __ret = __Internal.GetSystemSnapshot(out __arg0);
            return __ret;
        }

调用函数时,我得到了臭名昭著的:

试图读取或写入受保护的内存。这通常表明其他内存已损坏。

我尝试将CallingConventionStdCall 更改为Cdecl,将[In][Out] 引入DllImport 等,但都无济于事。任何人都可以看出代码有什么明显的错误吗?很明显,这对我来说是全新的,也许我要求 cppsharp 生成不需要调整的代码。

EDIT 原始的 C++ 文档有一个示例,其中结构体由

#define INIT_STRUCT(struct_p) { memset(struct_p, 0, sizeof(*(struct_p))); (struct_p)->size = sizeof(*(struct_p)); }

并且被使用

 SNAPSHOT_PARMS snapshot_parms;
 SYS_ERR result;

 INIT_STRUCT(&snapshot_parms);
result = get_system_snapshot(&snapshot_parms); 

【问题讨论】:

  • 文档或现有代码示例说明了如何调用此函数?具体来说,具有size 成员的结构通常应该通过将该成员设置为调用者看到的结构的大小来初始化——这是支持版本化结构的常用技术。从简单地将参数视为SNAPSHOT_PARMS*,您无法判断调用者是否希望在调用之前初始化任何数据。
  • 我在上面添加了一些细节(为了便于格式化),但在我看来这一切看起来都相当温和 - 它与 C# 代码做的事情是否不同?
  • 你去 - C++ 代码确实 1) 设置 size 成员和 2) 希望函数将其数据放入该结构中。 C# 代码不执行 1 并且不能与 2 一起使用,因为该结构仅通过值作为输入参数传递。 GetSystemSnapshot的参数不应该是IntPtr,而是ref SNAPSHOT_PARMS,并且应该用new SNAPSHOT_PARMS { size = Marshal.SizeOf<SNAPSHOT_PARMS>() }初始化。
  • 我认为cppsharp 在这种情况下比帮助更多的障碍——我明白它想要做什么,但它的包装器并没有真正让事情变得更简单,它导入了一些它应该的东西' t 已导入(如 SNAPSHOT_PARMS 上的复制构造函数)。它放在类中的 __Internal 结构真的是你所需要的,结合 GetSystemSnapshot 导入。
  • 谢谢 - 这对我有很大帮助,我现在正在从图书馆取回价值。请将其添加为答案,我会将其标记为已回答:)

标签: c# pinvoke


【解决方案1】:

从 C++ 声明来看,这就足够了:

[StructLayout(LayoutKind.Sequential)]
unsafe struct SNAPSHOT_PARMS {
    public int size;
    public int count;
    public fixed byte serial_no[600];
}

[DllImport("res_lib", EntryPoint = "get_system_snapshot")]
static extern int GetSystemSnapshot(ref SNAPSHOT_PARMS snapshot);

用作

var s = new SNAPSHOT_PARMS { size = Marshal.SizeOf<SNAPSHOT_PARMS>() };
int result = GetSystemSnapshot(ref s);
// check result, use s

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-07
    • 2010-10-14
    相关资源
    最近更新 更多