从C#调用非托管代码

时间:2016-08-29 09:11:19

标签: c# c

借助OpenHardwareMonitor项目的一些灵感,我已经成为一个很好的小工具来监控CPU和GPU指标温度,负载等。

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

然而,经过数周的实验(手持NVidia Documentaion),我仍然无法弄清楚如何以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 个答案:

答案 0 :(得分:0)

首先,函数是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中,这是被调用者的工作。这意味着使用StdCall而不是Cdecl进行P / Invoking几乎总能工作(.NET运行时会注意到堆栈不平衡并修复它),但会产生警告。

如果这不能解决您的问题,请注意位数。尝试使用32位.NET应用程序中的32位库。