Search code examples
c#pinvoke

AccessViolationException using unmanaged C++ DLL


I'm trying, for the first time, to use an unmanaged C++ DLL ("res_lib") in a C# application. I've used cppsharp to generate the PInvoke code: for example, one of the functions/methods I'm trying to call is get_system_snapshot. From the .h file, this is defined as

SYS_INT SYS_ERR get_system_snapshot(SNAPSHOT_PARMS* snapshotp);

SYS_INT and SYS_ERR equate to a int32_t. SNAPSHOT_PARMS is

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

cppsharp has turned this into the following code snippets:

DllImport

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

Object

 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;
        }

Main code

  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;
        }

When calling the function, I get the infamous:

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

I've tried changing the CallingConvention from StdCall to Cdecl, introducing [In] and [Out] to the DllImport etc, but all to no avail. Can anyone see anything obviously wrong with the code - as may be apparent, this is all new to me, and perhaps I'm asking a bit much for cppsharp to generate code that won't need tweaked.

EDIT The original C++ documentation has an example, where the struct is initialised by

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

and is used

 SNAPSHOT_PARMS snapshot_parms;
 SYS_ERR result;

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

Solution

  • From the C++ declarations, this should suffice:

    [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);
    

    Use as

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