AccessViolationException с использованием неуправляемой DLL C++

Я впервые пытаюсь использовать неуправляемую DLL C++ ("res_lib") в приложении C#. Я использовал cppsharp для генерации кода PInvoke: например, одну из функций/методов, которые я м, пытающийся позвонить, это get_system_snapshot. Из файла .h это определяется как

SYS_INT SYS_ERR get_system_snapshot(SNAPSHOT_PARMS* snapshotp);

SYS_INT и SYS_ERR равны int32_t. SNAPSHOT_PARMS это

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

cppsharp превратил это в следующие фрагменты кода:

Импорт библиотеки

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

При вызове функции получаю печально известное:

Попытка чтения или записи защищенной памяти. Это часто указывает на то, что другая память повреждена.

Я пытался изменить CallingConvention с StdCall на 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); 

person KenD    schedule 17.08.2018    source источник
comment
Что говорится в документации или существующих примерах кода о том, как должна вызываться эта функция? В частности, часто предполагается, что структура с членом size инициализируется установкой этого члена в размер структуры, видимый вызывающей стороне — это распространенный метод поддержки версионных структур. Просто увидев аргумент как SNAPSHOT_PARMS*, вы не можете сказать, ожидает ли вызывающая сторона инициализацию каких-либо данных перед вызовом.   -  person Jeroen Mostert    schedule 17.08.2018
comment
Я добавил некоторые детали выше (для упрощения форматирования), но все это выглядит довольно безобидно — делает ли это что-то отличное от кода C #?   -  person KenD    schedule 17.08.2018
comment
Итак, код C++ действительно 1) устанавливает член size и 2) хочет, чтобы функция поместила свои данные в эту структуру. Код C# не выполняет 1 и не может работать с 2, поскольку структура всегда передается только по значению в качестве входного параметра. Параметр GetSystemSnapshot должен быть не IntPtr, а ref SNAPSHOT_PARMS, и он должен быть инициализирован new SNAPSHOT_PARMS { size = Marshal.SizeOf<SNAPSHOT_PARMS>() }.   -  person Jeroen Mostert    schedule 17.08.2018
comment
Я думаю, что cppsharp в данном случае больше мешает, чем помогает — я вижу, что он пытается сделать, но его обертки на самом деле не упрощают задачу, и он импортирует некоторые вещи, которые не должен был импортировать (например, конструктор копирования в SNAPSHOT_PARMS ). Структура __Internal, которую он поместил в класс, действительно все, что вам нужно, в сочетании с импортом GetSystemSnapshot.   -  person Jeroen Mostert    schedule 17.08.2018
comment
Спасибо - это очень помогает, и теперь я получаю значения из библиотеки. Пожалуйста, добавьте это как ответ, и я отмечу его как ответ :)   -  person KenD    schedule 17.08.2018


Ответы (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
person Jeroen Mostert    schedule 18.08.2018