Вызов неуправляемого кода из 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.