2016-08-29 60 views
1

非託管代碼與來自所述OpenHardwareMonitor項目,我已經自己做了一個漂亮的小工具來監視CPU和GPU指標溫度,負載一些啓示等調用從C#

它工作正常,但我到一個PInvokeStackImbalance警告運行當調用NVidia驅動程序方法時,不要認爲忽略它們是明智的。然而,經過數週的實驗(使用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 

回答

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 /調用幾乎總是能夠工作的(.NET運行時會注意到堆棧不平衡並修復它),但會產生警告。

如果這不能解決您的問題,請注意位數。嘗試使用來自32位.NET應用程序的32位庫。

+0

賓果!我不知道你可以在代表中指定調用約定,我將它添加到所有代表中,不再有任何警告! – VikFreeze