Вызов неуправляемого кода из C#

Вдохновившись проектом OpenHardwareMonitor, я сделал себе отличный гаджет для мониторинга температуры процессора и графического процессора, нагрузки, нагрузки и т. Д.

Это работает нормально, но я бегу к PInvokeStackImbalance предупреждение при вызове методов драйвера NVidia и не думаю, что разумно игнорировать их.

Однако после нескольких недель экспериментов (с документами NVidia в руках) я все еще не могу понять, как определить и использовать структуры и методы драйверов таким образом, чтобы VS 2015 был доволен - что странно, потому что в Проект OpenHardwareMonitor, несмотря на использование точно такого же кода.

Надеюсь, кто-то здесь может указать мне правильное направление.

[DllImport("nvapi.dll", CallingConvention = CallingConvention.Cdecl, PreserveSig = true)]
private static extern IntPtr nvapi_QueryInterface(uint id);

private delegate NvStatus NvAPI_EnumPhysicalGPUsDelegate([Out] NvPhysicalGpuHandle[] gpuHandles, out int gpuCount);
private static readonly NvAPI_EnumPhysicalGPUsDelegate NvAPI_EnumPhysicalGPUs;

NvAPI_EnumPhysicalGPUs = Marshal.GetDelegateForFunctionPointer(nvapi_QueryInterface(0xE5AC921F), typeof(NvAPI_EnumPhysicalGPUsDelegate)) as NvAPI_EnumPhysicalGPUsDelegate;

status = NvAPI_EnumPhysicalGPUs != null ? NvAPI_EnumPhysicalGPUs(PhysicalGPUHandles, out PhysicalGPUHandlesCount) : NvStatus.FUNCTION_NOT_FOUND; // warning is thrown here

1 ответ

Решение

Во-первых, функции в стиле C, а не в C++. К счастью, вмешательство в C++ напрямую из C# является огромной болью (в этом случае вы действительно захотите использовать C++/CLI).

Родное взаимодействие не легкое. Вы должны понимать, кому принадлежит какая память, как ее распределять и освобождать, и вам нужно уделять много внимания тому, используете ли вы 32-разрядную или 64-разрядную систему.

На первый взгляд, у вас нет соглашения о вызовах для делегата, поэтому по умолчанию StdCall, Однако, как определено в NVAPI (и очень разумно для библиотек взаимодействия), вы должны использовать Cdecl:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
private delegate NvStatus NvAPI_EnumPhysicalGPUsDelegate([Out] NvPhysicalGpuHandle[] gpuHandles, out int gpuCount);

Сложность в Cdecl и StdCall заключается в том, что оба они очень похожи (аргументы передаются в стек справа налево, возвращаемое значение помещается в EAX, если целое число или poitner и т. Д.), За исключением того, что в Cdecl вызывающая сторона отвечает за очистка стека, в то время как в StdCall, это работа вызываемого. Это означает, что P/Invoking с StdCall вместо Cdecl будет работать почти всегда (среда выполнения.NET замечает дисбаланс стека и исправляет его), но выдает предупреждение.

Если это не решит вашу проблему, обратите внимание на разрядность. Попробуйте использовать 32-разрядную библиотеку из 32-разрядного приложения.NET.

Другие вопросы по тегам