VikFreeze VikFreeze - 11 months ago 150
C# Question

Calling unmanaged Code from C#

With some inspiration from the OpenHardwareMonitor Project i have made myself a nice gadget to monitor CPU and GPU metrics Temperature, Load, etc.

It works fine but I am running in to a

warning when calling the NVidia Driver Methods and don't think is wise to ignore them.

However after weeks of experimentation (with NVidia Documentaion in hand) I still can't figure out how to define and use the Drivers Structs and Methods in such a way that VS 2015 is happy with - Which is strange because there are no warnings in the OpenHardwareMonitor Project despite using the exact same code.

Hopefully someone here can point me in the right direction.

[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

Answer Source

First, the functions are C-style, not C++. Which is fortunate, since interoping with C++ directly from C# is a huge pain (you'd really want to use C++/CLI in that case).

Native interop is not easy. You need to understand who owns what memory, how to allocate and deallocate it, and you need to pay a lot of attention to whether you're running 32-bit or 64-bit.

At first glance, you're missing a calling convention on the delegate, so it will default to StdCall. However, as defined in NVAPI (and as very reasonable for interop libraries), you are supposed to use Cdecl:

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

The tricky thing with Cdecl and StdCall is that both are very similar (the arguments are passed on stack right-to-left, the return value goes in EAX if integer or poitner etc.), except that in Cdecl, the caller is responsible for clearing the stack, while in StdCall, it's the callee's job. This means that P/Invoking with StdCall instead of Cdecl will almost always work (the .NET runtime notices the stack imbalance and fixes it), but will produce a warning.

If this doesn't solve your problem, pay attention to the bitness. Try using the 32-bit library from a 32-bit .NET application.