从c#调用非托管代码

本文关键字:非托管代码 调用 | 更新日期: 2023-09-27 18:04:25

从OpenHardwareMonitor项目中获得一些灵感,我为自己做了一个很好的小工具来监控CPU和GPU指标温度,负载等。

它工作得很好,但我在调用NVidia驱动程序方法时遇到了PInvokeStackImbalance警告,并且不认为忽略它们是明智的。

然而,经过几周的实验(与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

从c#调用非托管代码

首先,这些函数是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中如果是整数或指针等),除了在Cdecl中,调用者负责清除堆栈,而在StdCall中,这是被调用者的工作。这意味着P/调用StdCall而不是Cdecl几乎总是工作(. net运行时注意到堆栈不平衡并修复它),但会产生一个警告。

如果这不能解决您的问题,请注意咬合。尝试在32位。net应用程序中使用32位库。