NvCplGetThermalSettings call to nvcpl.dll returns false (C++)

Hi all.

I’m trying to retrieve GPU temperature information (mainly to be able to rest a long-haul computation if the GPU exceeds a threshold temperature) using the code below, but get a ‘false’ return when I attempt to call the .dll function, nvCplGetThermalSettings:

HINSTANCE lib = LoadLibraryA("nvcpl.dll");
if(lib)
{
    NvCplGetThermalSettings nvCplGetThermalSettings = reinterpret_cast<NvCplGetThermalSettings> (GetProcAddress(lib,"NvCplGetThermalSettings"));
    DWORD coreTemp,ambientTemp,upperLimit;
    int success = nvCplGetThermalSettings(0,&coreTemp,&ambientTemp,&upperLimit);
    if(!success) {
        printf("Call failed: %d\n", success);
    }
    else {
        printf(
            "Core Temp:    %d°\n"
            "Ambient Temp:    %d°\n"
            "Upper Limit:    %d°",coreTemp,ambientTemp,upperLimit);
    }
    FreeLibrary(lib);
}
else {
     printf("Couldn't load library.\n");
}

The .dll is being loaded, and applications such as GPU-Z are able to display this information, so I know it’s not a hardware/driver issue. Also, other functions from this .dll can be successfully called, so I’m at a loss as to why this one can’t be. Any information is appreciated in advance.

In case anyone else was interested in the solution, using nvapi64.dll did the trick, but required replicating a few structs using non-API-specific objects and some digging through headers. Functional code is as follows:

#include <windows.h>
#include <iostream>
#define NVAPI_MAX_THERMAL_SENSORS_PER_GPU 3
typedef enum 
{
    NVAPI_THERMAL_TARGET_NONE          = 0,
    NVAPI_THERMAL_TARGET_GPU           = 1,     //!< GPU core temperature requires NvPhysicalGpuHandle
    NVAPI_THERMAL_TARGET_MEMORY        = 2,     //!< GPU memory temperature requires NvPhysicalGpuHandle
    NVAPI_THERMAL_TARGET_POWER_SUPPLY  = 4,     //!< GPU power supply temperature requires NvPhysicalGpuHandle
    NVAPI_THERMAL_TARGET_BOARD         = 8,     //!< GPU board ambient temperature requires NvPhysicalGpuHandle
    NVAPI_THERMAL_TARGET_VCD_BOARD     = 9,     //!< Visual Computing Device Board temperature requires NvVisualComputingDeviceHandle
    NVAPI_THERMAL_TARGET_VCD_INLET     = 10,    //!< Visual Computing Device Inlet temperature requires NvVisualComputingDeviceHandle
    NVAPI_THERMAL_TARGET_VCD_OUTLET    = 11,    //!< Visual Computing Device Outlet temperature requires NvVisualComputingDeviceHandle

    NVAPI_THERMAL_TARGET_ALL           = 15,
    NVAPI_THERMAL_TARGET_UNKNOWN       = -1,
} NV_THERMAL_TARGET;

typedef struct
{
    int   version;                //!< structure version 
    int   count;                  //!< number of associated thermal sensors
    struct 
    {
        int       controller;        //!< internal, ADM1032, MAX6649...
        int                       defaultMinTemp;    //!< The min default temperature value of the thermal sensor in degree Celsius 
        int                       defaultMaxTemp;    //!< The max default temperature value of the thermal sensor in degree Celsius 
        int                       currentTemp;       //!< The current temperature value of the thermal sensor in degree Celsius 
        NV_THERMAL_TARGET           target;            //!< Thermal sensor targeted @ GPU, memory, chipset, powersupply, Visual Computing Device, etc.
    } sensor[NVAPI_MAX_THERMAL_SENSORS_PER_GPU];

} NV_GPU_THERMAL_SETTINGS;

// magic numbers, do not change them
#define NVAPI_MAX_PHYSICAL_GPUS   64
#define NVAPI_MAX_USAGES_PER_GPU  34

// function pointer types
typedef int *(*NvAPI_QueryInterface_t)(unsigned int offset);
typedef int (*NvAPI_Initialize_t)();
typedef int (*NvAPI_EnumPhysicalGPUs_t)(int **handles, int *count);
typedef int (*NvAPI_GPU_GetUsages_t)(int *handle, unsigned int *usages);
typedef int (*NvAPI_GPU_GetThermalSettings_t)(int *handle, int sensorIndex, NV_GPU_THERMAL_SETTINGS *temp);

int main()
{   
    HMODULE hmod = LoadLibraryA("nvapi64.dll");
    if (hmod == NULL)
    {
        std::cerr << "Couldn't find nvapi.dll" << std::endl;
        return 1;
    }

    // nvapi.dll internal function pointers
    NvAPI_QueryInterface_t      NvAPI_QueryInterface     = NULL;
    NvAPI_Initialize_t          NvAPI_Initialize         = NULL;
    NvAPI_EnumPhysicalGPUs_t    NvAPI_EnumPhysicalGPUs   = NULL;
    NvAPI_GPU_GetUsages_t       NvAPI_GPU_GetUsages      = NULL;
    NvAPI_GPU_GetThermalSettings_t	NvAPI_GPU_GetThermalSettings = NULL;

    // nvapi_QueryInterface is a function used to retrieve other internal functions in nvapi.dll
    NvAPI_QueryInterface = (NvAPI_QueryInterface_t) GetProcAddress(hmod, "nvapi_QueryInterface");

    // some useful internal functions that aren't exported by nvapi.dll
    NvAPI_Initialize = (NvAPI_Initialize_t) (*NvAPI_QueryInterface)(0x0150E828);
    NvAPI_EnumPhysicalGPUs = (NvAPI_EnumPhysicalGPUs_t) (*NvAPI_QueryInterface)(0xE5AC921F);
    NvAPI_GPU_GetUsages = (NvAPI_GPU_GetUsages_t) (*NvAPI_QueryInterface)(0x189A1FDF);
    NvAPI_GPU_GetThermalSettings = (NvAPI_GPU_GetThermalSettings_t) (*NvAPI_QueryInterface)(0xE3640A56);

    if (NvAPI_Initialize == NULL || NvAPI_EnumPhysicalGPUs == NULL ||
        NvAPI_EnumPhysicalGPUs == NULL || NvAPI_GPU_GetUsages == NULL)
    {
        std::cerr << "Couldn't get functions in nvapi.dll" << std::endl;
        return 2;
    }

    // initialize NvAPI library, call it once before calling any other NvAPI functions
    (*NvAPI_Initialize)();
	NV_GPU_THERMAL_SETTINGS 		 nvgts;
    int          gpuCount = 0;
    int 		 gpuTemp;
    int         *gpuHandles[NVAPI_MAX_PHYSICAL_GPUS] = { NULL };
    unsigned int gpuUsages[NVAPI_MAX_USAGES_PER_GPU] = { 0 };

    // gpuUsages[0] must be this value, otherwise NvAPI_GPU_GetUsages won't work
    gpuUsages[0] = (NVAPI_MAX_USAGES_PER_GPU * 4) | 0x10000;

    (*NvAPI_EnumPhysicalGPUs)(gpuHandles, &gpuCount);
    nvgts.version = sizeof(NV_GPU_THERMAL_SETTINGS) | (1<<16);
    nvgts.count = 0;
    nvgts.sensor[0].controller = -1;
    nvgts.sensor[0].target = NVAPI_THERMAL_TARGET_GPU;

    // print GPU usage every second
    for (int i = 0; i < 100; i++)
    {
    	(*NvAPI_GPU_GetThermalSettings)(gpuHandles[0], 0 ,&nvgts);
    	printf("%d\n", nvgts.sensor[0].currentTemp);
        (*NvAPI_GPU_GetUsages)(gpuHandles[0], gpuUsages);
        int usage = gpuUsages[3];
        std::cout << "GPU Usage: " << usage << std::endl;
        Sleep(1000);
    }

    return 0;
}