From 3023089017457bbd348d04a8c51ecf43fa47441f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=89=E5=A4=9C?= Date: Fri, 26 Sep 2025 16:34:20 +0800 Subject: [PATCH] Changed NvApi lifecycle (#1885) * Changed NvApi lifecycle * Changed from public to internal * Revert unnecessary changes. Reinitialize() -> Initialize(). * Refactor try-catch blocks for NVML device methods --------- Co-authored-by: PhyxionNL <7643972+PhyxionNL@users.noreply.github.com> --- .../Hardware/Gpu/NvidiaGpu.cs | 4 +- .../Hardware/Gpu/NvidiaGroup.cs | 4 +- LibreHardwareMonitorLib/Interop/NvApi.cs | 104 +- LibreHardwareMonitorLib/Interop/NvidiaML.cs | 901 +++++++++--------- 4 files changed, 510 insertions(+), 503 deletions(-) diff --git a/LibreHardwareMonitorLib/Hardware/Gpu/NvidiaGpu.cs b/LibreHardwareMonitorLib/Hardware/Gpu/NvidiaGpu.cs index 8d41a86..853f758 100644 --- a/LibreHardwareMonitorLib/Hardware/Gpu/NvidiaGpu.cs +++ b/LibreHardwareMonitorLib/Hardware/Gpu/NvidiaGpu.cs @@ -1022,7 +1022,7 @@ internal sealed class NvidiaGpu : GenericGpu private NvApi.NvThermalSensors GetThermalSensors(uint mask, out NvApi.NvStatus status) { - if (NvApi.NvAPI_GPU_ThermalGetSensors == null) + if (NvApi.NvAPI_GPU_GetThermalSensors == null) { status = NvApi.NvStatus.Error; return default; @@ -1034,7 +1034,7 @@ internal sealed class NvidiaGpu : GenericGpu Mask = mask }; - status = NvApi.NvAPI_GPU_ThermalGetSensors(_handle, ref thermalSensors); + status = NvApi.NvAPI_GPU_GetThermalSensors(_handle, ref thermalSensors); return status == NvApi.NvStatus.OK ? thermalSensors : default; } diff --git a/LibreHardwareMonitorLib/Hardware/Gpu/NvidiaGroup.cs b/LibreHardwareMonitorLib/Hardware/Gpu/NvidiaGroup.cs index 1ca13ba..039ef97 100644 --- a/LibreHardwareMonitorLib/Hardware/Gpu/NvidiaGroup.cs +++ b/LibreHardwareMonitorLib/Hardware/Gpu/NvidiaGroup.cs @@ -18,6 +18,8 @@ internal class NvidiaGroup : IGroup public NvidiaGroup(ISettings settings) { + NvApi.Initialize(); + if (!NvApi.IsAvailable) return; @@ -98,4 +100,4 @@ internal class NvidiaGroup : IGroup NvidiaML.Close(); } -} \ No newline at end of file +} diff --git a/LibreHardwareMonitorLib/Interop/NvApi.cs b/LibreHardwareMonitorLib/Interop/NvApi.cs index 1a51268..07b6c84 100644 --- a/LibreHardwareMonitorLib/Interop/NvApi.cs +++ b/LibreHardwareMonitorLib/Interop/NvApi.cs @@ -25,31 +25,31 @@ internal static class NvApi private const string DllName = "nvapi.dll"; private const string DllName64 = "nvapi64.dll"; - public static readonly NvAPI_EnumNvidiaDisplayHandleDelegate NvAPI_EnumNvidiaDisplayHandle; - public static readonly NvAPI_EnumPhysicalGPUsDelegate NvAPI_EnumPhysicalGPUs; - public static readonly NvAPI_GetDisplayDriverVersionDelegate NvAPI_GetDisplayDriverVersion; - public static readonly NvAPI_GetPhysicalGPUsFromDisplayDelegate NvAPI_GetPhysicalGPUsFromDisplay; - public static readonly NvAPI_GPU_ClientFanCoolersGetControlDelegate NvAPI_GPU_ClientFanCoolersGetControl; - public static readonly NvAPI_GPU_ClientFanCoolersGetStatusDelegate NvAPI_GPU_ClientFanCoolersGetStatus; - public static readonly NvAPI_GPU_ClientFanCoolersSetControlDelegate NvAPI_GPU_ClientFanCoolersSetControl; - public static readonly NvAPI_GPU_ClientPowerTopologyGetStatusDelegate NvAPI_GPU_ClientPowerTopologyGetStatus; - public static readonly NvAPI_GPU_GetAllClockFrequenciesDelegate NvAPI_GPU_GetAllClockFrequencies; - public static readonly NvAPI_GPU_GetAllClocksDelegate NvAPI_GPU_GetAllClocks; - public static readonly NvAPI_GPU_GetBusIdDelegate NvAPI_GPU_GetBusId; - public static readonly NvAPI_GPU_GetCoolerSettingsDelegate NvAPI_GPU_GetCoolerSettings; - public static readonly NvAPI_GPU_GetDynamicPstatesInfoExDelegate NvAPI_GPU_GetDynamicPstatesInfoEx; - public static readonly NvAPI_GPU_GetMemoryInfoDelegate NvAPI_GPU_GetMemoryInfo; - public static readonly NvAPI_GPU_GetPCIIdentifiersDelegate NvAPI_GPU_GetPCIIdentifiers; - public static readonly NvAPI_GPU_GetTachReadingDelegate NvAPI_GPU_GetTachReading; - public static readonly NvAPI_GPU_GetThermalSettingsDelegate NvAPI_GPU_GetThermalSettings; - public static readonly NvAPI_GPU_GetUsagesDelegate NvAPI_GPU_GetUsages; - public static readonly NvAPI_GPU_SetCoolerLevelsDelegate NvAPI_GPU_SetCoolerLevels; - public static readonly NvAPI_GPU_GetThermalSensorsDelegate NvAPI_GPU_ThermalGetSensors; + public static NvAPI_EnumNvidiaDisplayHandleDelegate NvAPI_EnumNvidiaDisplayHandle { get; internal set; } + public static NvAPI_EnumPhysicalGPUsDelegate NvAPI_EnumPhysicalGPUs { get; internal set; } + public static NvAPI_GetDisplayDriverVersionDelegate NvAPI_GetDisplayDriverVersion { get; internal set; } + public static NvAPI_GetPhysicalGPUsFromDisplayDelegate NvAPI_GetPhysicalGPUsFromDisplay { get; internal set; } + public static NvAPI_GPU_ClientFanCoolersGetControlDelegate NvAPI_GPU_ClientFanCoolersGetControl { get; internal set; } + public static NvAPI_GPU_ClientFanCoolersGetStatusDelegate NvAPI_GPU_ClientFanCoolersGetStatus { get; internal set; } + public static NvAPI_GPU_ClientFanCoolersSetControlDelegate NvAPI_GPU_ClientFanCoolersSetControl { get; internal set; } + public static NvAPI_GPU_ClientPowerTopologyGetStatusDelegate NvAPI_GPU_ClientPowerTopologyGetStatus { get; internal set; } + public static NvAPI_GPU_GetAllClockFrequenciesDelegate NvAPI_GPU_GetAllClockFrequencies { get; internal set; } + public static NvAPI_GPU_GetAllClocksDelegate NvAPI_GPU_GetAllClocks { get; internal set; } + public static NvAPI_GPU_GetBusIdDelegate NvAPI_GPU_GetBusId { get; internal set; } + public static NvAPI_GPU_GetCoolerSettingsDelegate NvAPI_GPU_GetCoolerSettings { get; internal set; } + public static NvAPI_GPU_GetDynamicPstatesInfoExDelegate NvAPI_GPU_GetDynamicPstatesInfoEx { get; internal set; } + public static NvAPI_GPU_GetMemoryInfoDelegate NvAPI_GPU_GetMemoryInfo { get; internal set; } + public static NvAPI_GPU_GetPCIIdentifiersDelegate NvAPI_GPU_GetPCIIdentifiers { get; internal set; } + public static NvAPI_GPU_GetTachReadingDelegate NvAPI_GPU_GetTachReading { get; internal set; } + public static NvAPI_GPU_GetThermalSettingsDelegate NvAPI_GPU_GetThermalSettings { get; internal set; } + public static NvAPI_GPU_GetUsagesDelegate NvAPI_GPU_GetUsages { get; internal set; } + public static NvAPI_GPU_SetCoolerLevelsDelegate NvAPI_GPU_SetCoolerLevels { get; internal set; } + public static NvAPI_GPU_GetThermalSensorsDelegate NvAPI_GPU_GetThermalSensors { get; internal set; } - private static readonly NvAPI_GetInterfaceVersionStringDelegate _nvAPI_GetInterfaceVersionString; - private static readonly NvAPI_GPU_GetFullNameDelegate _nvAPI_GPU_GetFullName; + private static NvAPI_GetInterfaceVersionStringDelegate _nvAPI_GetInterfaceVersionString; + private static NvAPI_GPU_GetFullNameDelegate _nvAPI_GPU_GetFullName; - static NvApi() + public static void Initialize() { NvAPI_InitializeDelegate nvApiInitialize; @@ -58,7 +58,7 @@ internal static class NvApi if (!DllExists()) return; - GetDelegate(0x0150E828, out nvApiInitialize); + nvApiInitialize = GetDelegate(0x0150E828); } catch (Exception e) when (e is DllNotFoundException or ArgumentNullException or EntryPointNotFoundException or BadImageFormatException) { @@ -67,28 +67,28 @@ internal static class NvApi if (nvApiInitialize() == NvStatus.OK) { - GetDelegate(0xE3640A56, out NvAPI_GPU_GetThermalSettings); - GetDelegate(0xCEEE8E9F, out _nvAPI_GPU_GetFullName); - GetDelegate(0x9ABDD40D, out NvAPI_EnumNvidiaDisplayHandle); - GetDelegate(0x34EF9506, out NvAPI_GetPhysicalGPUsFromDisplay); - GetDelegate(0xE5AC921F, out NvAPI_EnumPhysicalGPUs); - GetDelegate(0x5F608315, out NvAPI_GPU_GetTachReading); - GetDelegate(0x1BD69F49, out NvAPI_GPU_GetAllClocks); - GetDelegate(0x60DED2ED, out NvAPI_GPU_GetDynamicPstatesInfoEx); - GetDelegate(0x189A1FDF, out NvAPI_GPU_GetUsages); - GetDelegate(0xDA141340, out NvAPI_GPU_GetCoolerSettings); - GetDelegate(0x891FA0AE, out NvAPI_GPU_SetCoolerLevels); - GetDelegate(0x774AA982, out NvAPI_GPU_GetMemoryInfo); - GetDelegate(0xF951A4D1, out NvAPI_GetDisplayDriverVersion); - GetDelegate(0x01053FA5, out _nvAPI_GetInterfaceVersionString); - GetDelegate(0x2DDFB66E, out NvAPI_GPU_GetPCIIdentifiers); - GetDelegate(0x1BE0B8E5, out NvAPI_GPU_GetBusId); - GetDelegate(0x35AED5E8, out NvAPI_GPU_ClientFanCoolersGetStatus); - GetDelegate(0xDCB616C3, out NvAPI_GPU_GetAllClockFrequencies); - GetDelegate(0x814B209F, out NvAPI_GPU_ClientFanCoolersGetControl); - GetDelegate(0xA58971A5, out NvAPI_GPU_ClientFanCoolersSetControl); - GetDelegate(0x0EDCF624E, out NvAPI_GPU_ClientPowerTopologyGetStatus); - GetDelegate(0x65FE3AAD, out NvAPI_GPU_ThermalGetSensors); + NvAPI_GPU_GetThermalSettings = GetDelegate(0xE3640A56); + _nvAPI_GPU_GetFullName = GetDelegate(0xCEEE8E9F); + NvAPI_EnumNvidiaDisplayHandle = GetDelegate(0x9ABDD40D); + NvAPI_GetPhysicalGPUsFromDisplay = GetDelegate(0x34EF9506); + NvAPI_EnumPhysicalGPUs = GetDelegate(0xE5AC921F); + NvAPI_GPU_GetTachReading = GetDelegate(0x5F608315); + NvAPI_GPU_GetAllClocks = GetDelegate(0x1BD69F49); + NvAPI_GPU_GetDynamicPstatesInfoEx = GetDelegate(0x60DED2ED); + NvAPI_GPU_GetUsages = GetDelegate(0x189A1FDF); + NvAPI_GPU_GetCoolerSettings = GetDelegate(0xDA141340); + NvAPI_GPU_SetCoolerLevels = GetDelegate(0x891FA0AE); + NvAPI_GPU_GetMemoryInfo = GetDelegate(0x774AA982); + NvAPI_GetDisplayDriverVersion = GetDelegate(0xF951A4D1); + _nvAPI_GetInterfaceVersionString = GetDelegate(0x01053FA5); + NvAPI_GPU_GetPCIIdentifiers = GetDelegate(0x2DDFB66E); + NvAPI_GPU_GetBusId = GetDelegate(0x1BE0B8E5); + NvAPI_GPU_ClientFanCoolersGetStatus = GetDelegate(0x35AED5E8); + NvAPI_GPU_GetAllClockFrequencies = GetDelegate(0xDCB616C3); + NvAPI_GPU_ClientFanCoolersGetControl = GetDelegate(0x814B209F); + NvAPI_GPU_ClientFanCoolersSetControl = GetDelegate(0xA58971A5); + NvAPI_GPU_ClientPowerTopologyGetStatus = GetDelegate(0x0EDCF624E); + NvAPI_GPU_GetThermalSensors = GetDelegate(0x65FE3AAD); IsAvailable = true; } @@ -188,7 +188,7 @@ internal static class NvApi BusInterface // Bus } - public static bool IsAvailable { get; } + public static bool IsAvailable { get; private set; } [DllImport(DllName, EntryPoint = "nvapi_QueryInterface", CallingConvention = CallingConvention.Cdecl, PreserveSig = true)] [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] @@ -216,14 +216,18 @@ internal static class NvApi return status; } - private static void GetDelegate(uint id, out T newDelegate) where T : class + private static T GetDelegate(uint id) where T : class { IntPtr ptr = Environment.Is64BitProcess ? NvAPI64_QueryInterface(id) : NvAPI32_QueryInterface(id); if (ptr != IntPtr.Zero) - newDelegate = Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)) as T; + { + return Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)) as T; + } else - newDelegate = null; + { + return null; + } } public static bool DllExists() diff --git a/LibreHardwareMonitorLib/Interop/NvidiaML.cs b/LibreHardwareMonitorLib/Interop/NvidiaML.cs index 0636133..9ba6267 100644 --- a/LibreHardwareMonitorLib/Interop/NvidiaML.cs +++ b/LibreHardwareMonitorLib/Interop/NvidiaML.cs @@ -1,325 +1,325 @@ -// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -// Copyright (C) LibreHardwareMonitor and Contributors. -// All Rights Reserved. - -using System; -using System.IO; -using System.Runtime.InteropServices; - -namespace LibreHardwareMonitor.Interop; - -internal static class NvidiaML -{ - private const string LinuxDllName = "nvidia-ml"; - private const string WindowsDllName = "nvml.dll"; - - private static readonly object _syncRoot = new(); - - private static IntPtr _windowsDll; - - private static WindowsNvmlGetHandleDelegate _windowsNvmlDeviceGetHandleByIndex; - private static WindowsNvmlGetHandleByPciBusIdDelegate _windowsNvmlDeviceGetHandleByPciBusId; - private static WindowsNvmlDeviceGetPcieThroughputDelegate _windowsNvmlDeviceGetPcieThroughputDelegate; - private static WindowsNvmlDeviceGetPciInfo _windowsNvmlDeviceGetPciInfo; - private static WindowsNvmlGetPowerUsageDelegate _windowsNvmlDeviceGetPowerUsage; - private static WindowsNvmlDelegate _windowsNvmlInit; - private static WindowsNvmlDelegate _windowsNvmlShutdown; - - public enum NvmlPcieUtilCounter - { - TxBytes = 0, - RxBytes = 1 - } - - public enum NvmlReturn - { - /// - /// The operation was successful - /// - Success = 0, - - /// - /// NvidiaML was not first initialized with nvmlInit() - /// - Uninitialized = 1, - - /// - /// A supplied argument is invalid - /// - InvalidArgument = 2, - - /// - /// The requested operation is not available on target device - /// - NotSupported = 3, - - /// - /// The current user does not have permission for operation - /// - NoPermission = 4, - - /// - /// A query to find an object was unsuccessful - /// - NotFound = 6, - - /// - /// An input argument is not large enough - /// - InsufficientSize = 7, - - /// - /// A device's external power cables are not properly attached - /// - InsufficientPower = 8, - - /// - /// NVIDIA driver is not loaded - /// - DriverNotLoaded = 9, - - /// - /// User provided timeout passed - /// - TimeOut = 10, - - /// - /// NVIDIA Kernel detected an interrupt issue with a GPU - /// - IRQIssue = 11, - - /// - /// NvidiaML Shared Library couldn't be found or loaded - /// - LibraryNotFound = 12, - - /// - /// Local version of NvidiaML doesn't implement this function - /// - FunctionNotFound = 13, - - /// - /// infoROM is corrupted - /// - CorruptedInfoRom = 14, - - /// - /// The GPU has fallen off the bus or has otherwise become inaccessible - /// - GpuIsLost = 15, - - /// - /// The GPU requires a reset before it can be used again - /// - ResetRequired = 16, - - /// - /// The GPU control device has been blocked by the operating system/cgroups - /// - OperatingSystem = 17, - - /// - /// RM detects a driver/library version mismatch - /// - LibRmVersionMismatch = 18, - - /// - /// An operation cannot be performed because the GPU is currently in use - /// - InUse = 19, - - /// - /// An public driver error occurred - /// - Unknown = 999 - } - - public static bool IsAvailable { get; private set; } - - public static bool Initialize() - { - lock (_syncRoot) - { - if (IsAvailable) - { - return true; - } - - if (Software.OperatingSystem.IsUnix) - { - try - { - IsAvailable = nvmlInit() == NvmlReturn.Success; - } - catch (DllNotFoundException) - { } - catch (EntryPointNotFoundException) - { - try - { - IsAvailable = nvmlInitLegacy() == NvmlReturn.Success; - } - catch (EntryPointNotFoundException) - { } - } - } - else if (IsNvmlCompatibleWindowsVersion()) - { - // Attempt to load the Nvidia Management Library from the - // windows standard search order for applications. This will - // help installations that either have the library in - // %windir%/system32 or provide their own library - _windowsDll = Kernel32.LoadLibrary(WindowsDllName); - - // If there is no dll in the path, then attempt to load it - // from program files - if (_windowsDll == IntPtr.Zero) - { - string programFilesDirectory = Environment.ExpandEnvironmentVariables("%ProgramW6432%"); - string dllPath = Path.Combine(programFilesDirectory, @"NVIDIA Corporation\NVSMI", WindowsDllName); - - _windowsDll = Kernel32.LoadLibrary(dllPath); - } - - IsAvailable = (_windowsDll != IntPtr.Zero) && InitialiseDelegates() && (_windowsNvmlInit() == NvmlReturn.Success); - } - - return IsAvailable; - } - } - - private static bool IsNvmlCompatibleWindowsVersion() - { - return Software.OperatingSystem.Is64Bit && ((Environment.OSVersion.Version.Major > 6) || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 1)); - } - - private static bool InitialiseDelegates() - { - IntPtr nvmlInit = Kernel32.GetProcAddress(_windowsDll, "nvmlInit_v2"); - - if (nvmlInit != IntPtr.Zero) - { - _windowsNvmlInit = (WindowsNvmlDelegate)Marshal.GetDelegateForFunctionPointer(nvmlInit, typeof(WindowsNvmlDelegate)); - } - else - { - nvmlInit = Kernel32.GetProcAddress(_windowsDll, "nvmlInit"); - if (nvmlInit != IntPtr.Zero) - _windowsNvmlInit = (WindowsNvmlDelegate)Marshal.GetDelegateForFunctionPointer(nvmlInit, typeof(WindowsNvmlDelegate)); - else - return false; - } - - IntPtr nvmlShutdown = Kernel32.GetProcAddress(_windowsDll, "nvmlShutdown"); - if (nvmlShutdown != IntPtr.Zero) - _windowsNvmlShutdown = (WindowsNvmlDelegate)Marshal.GetDelegateForFunctionPointer(nvmlShutdown, typeof(WindowsNvmlDelegate)); - else - return false; - - IntPtr nvmlGetHandle = Kernel32.GetProcAddress(_windowsDll, "nvmlDeviceGetHandleByIndex_v2"); - if (nvmlGetHandle != IntPtr.Zero) - _windowsNvmlDeviceGetHandleByIndex = (WindowsNvmlGetHandleDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetHandle, typeof(WindowsNvmlGetHandleDelegate)); - else - { - nvmlGetHandle = Kernel32.GetProcAddress(_windowsDll, "nvmlDeviceGetHandleByIndex"); - if (nvmlGetHandle != IntPtr.Zero) - _windowsNvmlDeviceGetHandleByIndex = (WindowsNvmlGetHandleDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetHandle, typeof(WindowsNvmlGetHandleDelegate)); - else - return false; - } - - IntPtr nvmlGetPowerUsage = Kernel32.GetProcAddress(_windowsDll, "nvmlDeviceGetPowerUsage"); - if (nvmlGetPowerUsage != IntPtr.Zero) - _windowsNvmlDeviceGetPowerUsage = (WindowsNvmlGetPowerUsageDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetPowerUsage, typeof(WindowsNvmlGetPowerUsageDelegate)); - else - return false; - - IntPtr nvmlGetPcieThroughput = Kernel32.GetProcAddress(_windowsDll, "nvmlDeviceGetPcieThroughput"); - if (nvmlGetPcieThroughput != IntPtr.Zero) - _windowsNvmlDeviceGetPcieThroughputDelegate = (WindowsNvmlDeviceGetPcieThroughputDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetPcieThroughput, typeof(WindowsNvmlDeviceGetPcieThroughputDelegate)); - else - return false; - - IntPtr nvmlGetHandlePciBus = Kernel32.GetProcAddress(_windowsDll, "nvmlDeviceGetHandleByPciBusId_v2"); - if (nvmlGetHandlePciBus != IntPtr.Zero) - _windowsNvmlDeviceGetHandleByPciBusId = (WindowsNvmlGetHandleByPciBusIdDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetHandlePciBus, typeof(WindowsNvmlGetHandleByPciBusIdDelegate)); - else - return false; - - IntPtr nvmlDeviceGetPciInfo = Kernel32.GetProcAddress(_windowsDll, "nvmlDeviceGetPciInfo_v2"); - if (nvmlDeviceGetPciInfo != IntPtr.Zero) - _windowsNvmlDeviceGetPciInfo = (WindowsNvmlDeviceGetPciInfo)Marshal.GetDelegateForFunctionPointer(nvmlDeviceGetPciInfo, typeof(WindowsNvmlDeviceGetPciInfo)); - else - return false; - - return true; - } - - public static void Close() - { - lock (_syncRoot) - { - if (IsAvailable) - { - if (Software.OperatingSystem.IsUnix) - { - nvmlShutdown(); - } - else if (_windowsDll != IntPtr.Zero) - { - _windowsNvmlShutdown(); - Kernel32.FreeLibrary(_windowsDll); - } - - IsAvailable = false; - } - } - } - - public static NvmlDevice? NvmlDeviceGetHandleByIndex(int index) - { - if (IsAvailable) - { - NvmlDevice nvmlDevice; - if (Software.OperatingSystem.IsUnix) - { - try - { - if (nvmlDeviceGetHandleByIndex(index, out nvmlDevice) == NvmlReturn.Success) - return nvmlDevice; - } - catch (EntryPointNotFoundException) - { - if (nvmlDeviceGetHandleByIndexLegacy(index, out nvmlDevice) == NvmlReturn.Success) - return nvmlDevice; - } - } - else - { +// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// Copyright (C) LibreHardwareMonitor and Contributors. +// All Rights Reserved. + +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace LibreHardwareMonitor.Interop; + +internal static class NvidiaML +{ + private const string LinuxDllName = "nvidia-ml"; + private const string WindowsDllName = "nvml.dll"; + + private static readonly object _syncRoot = new(); + + private static IntPtr _windowsDll; + + private static WindowsNvmlGetHandleDelegate _windowsNvmlDeviceGetHandleByIndex; + private static WindowsNvmlGetHandleByPciBusIdDelegate _windowsNvmlDeviceGetHandleByPciBusId; + private static WindowsNvmlDeviceGetPcieThroughputDelegate _windowsNvmlDeviceGetPcieThroughputDelegate; + private static WindowsNvmlDeviceGetPciInfo _windowsNvmlDeviceGetPciInfo; + private static WindowsNvmlGetPowerUsageDelegate _windowsNvmlDeviceGetPowerUsage; + private static WindowsNvmlDelegate _windowsNvmlInit; + private static WindowsNvmlDelegate _windowsNvmlShutdown; + + public enum NvmlPcieUtilCounter + { + TxBytes = 0, + RxBytes = 1 + } + + public enum NvmlReturn + { + /// + /// The operation was successful + /// + Success = 0, + + /// + /// NvidiaML was not first initialized with nvmlInit() + /// + Uninitialized = 1, + + /// + /// A supplied argument is invalid + /// + InvalidArgument = 2, + + /// + /// The requested operation is not available on target device + /// + NotSupported = 3, + + /// + /// The current user does not have permission for operation + /// + NoPermission = 4, + + /// + /// A query to find an object was unsuccessful + /// + NotFound = 6, + + /// + /// An input argument is not large enough + /// + InsufficientSize = 7, + + /// + /// A device's external power cables are not properly attached + /// + InsufficientPower = 8, + + /// + /// NVIDIA driver is not loaded + /// + DriverNotLoaded = 9, + + /// + /// User provided timeout passed + /// + TimeOut = 10, + + /// + /// NVIDIA Kernel detected an interrupt issue with a GPU + /// + IRQIssue = 11, + + /// + /// NvidiaML Shared Library couldn't be found or loaded + /// + LibraryNotFound = 12, + + /// + /// Local version of NvidiaML doesn't implement this function + /// + FunctionNotFound = 13, + + /// + /// infoROM is corrupted + /// + CorruptedInfoRom = 14, + + /// + /// The GPU has fallen off the bus or has otherwise become inaccessible + /// + GpuIsLost = 15, + + /// + /// The GPU requires a reset before it can be used again + /// + ResetRequired = 16, + + /// + /// The GPU control device has been blocked by the operating system/cgroups + /// + OperatingSystem = 17, + + /// + /// RM detects a driver/library version mismatch + /// + LibRmVersionMismatch = 18, + + /// + /// An operation cannot be performed because the GPU is currently in use + /// + InUse = 19, + + /// + /// An public driver error occurred + /// + Unknown = 999 + } + + public static bool IsAvailable { get; private set; } + + public static bool Initialize() + { + lock (_syncRoot) + { + if (IsAvailable) + { + return true; + } + + if (Software.OperatingSystem.IsUnix) + { + try + { + IsAvailable = nvmlInit() == NvmlReturn.Success; + } + catch (DllNotFoundException) + { } + catch (EntryPointNotFoundException) + { + try + { + IsAvailable = nvmlInitLegacy() == NvmlReturn.Success; + } + catch (EntryPointNotFoundException) + { } + } + } + else if (IsNvmlCompatibleWindowsVersion()) + { + // Attempt to load the Nvidia Management Library from the + // windows standard search order for applications. This will + // help installations that either have the library in + // %windir%/system32 or provide their own library + _windowsDll = Kernel32.LoadLibrary(WindowsDllName); + + // If there is no dll in the path, then attempt to load it + // from program files + if (_windowsDll == IntPtr.Zero) + { + string programFilesDirectory = Environment.ExpandEnvironmentVariables("%ProgramW6432%"); + string dllPath = Path.Combine(programFilesDirectory, @"NVIDIA Corporation\NVSMI", WindowsDllName); + + _windowsDll = Kernel32.LoadLibrary(dllPath); + } + + IsAvailable = (_windowsDll != IntPtr.Zero) && InitialiseDelegates() && (_windowsNvmlInit() == NvmlReturn.Success); + } + + return IsAvailable; + } + } + + private static bool IsNvmlCompatibleWindowsVersion() + { + return Software.OperatingSystem.Is64Bit && ((Environment.OSVersion.Version.Major > 6) || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 1)); + } + + private static bool InitialiseDelegates() + { + IntPtr nvmlInit = Kernel32.GetProcAddress(_windowsDll, "nvmlInit_v2"); + + if (nvmlInit != IntPtr.Zero) + { + _windowsNvmlInit = (WindowsNvmlDelegate)Marshal.GetDelegateForFunctionPointer(nvmlInit, typeof(WindowsNvmlDelegate)); + } + else + { + nvmlInit = Kernel32.GetProcAddress(_windowsDll, "nvmlInit"); + if (nvmlInit != IntPtr.Zero) + _windowsNvmlInit = (WindowsNvmlDelegate)Marshal.GetDelegateForFunctionPointer(nvmlInit, typeof(WindowsNvmlDelegate)); + else + return false; + } + + IntPtr nvmlShutdown = Kernel32.GetProcAddress(_windowsDll, "nvmlShutdown"); + if (nvmlShutdown != IntPtr.Zero) + _windowsNvmlShutdown = (WindowsNvmlDelegate)Marshal.GetDelegateForFunctionPointer(nvmlShutdown, typeof(WindowsNvmlDelegate)); + else + return false; + + IntPtr nvmlGetHandle = Kernel32.GetProcAddress(_windowsDll, "nvmlDeviceGetHandleByIndex_v2"); + if (nvmlGetHandle != IntPtr.Zero) + _windowsNvmlDeviceGetHandleByIndex = (WindowsNvmlGetHandleDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetHandle, typeof(WindowsNvmlGetHandleDelegate)); + else + { + nvmlGetHandle = Kernel32.GetProcAddress(_windowsDll, "nvmlDeviceGetHandleByIndex"); + if (nvmlGetHandle != IntPtr.Zero) + _windowsNvmlDeviceGetHandleByIndex = (WindowsNvmlGetHandleDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetHandle, typeof(WindowsNvmlGetHandleDelegate)); + else + return false; + } + + IntPtr nvmlGetPowerUsage = Kernel32.GetProcAddress(_windowsDll, "nvmlDeviceGetPowerUsage"); + if (nvmlGetPowerUsage != IntPtr.Zero) + _windowsNvmlDeviceGetPowerUsage = (WindowsNvmlGetPowerUsageDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetPowerUsage, typeof(WindowsNvmlGetPowerUsageDelegate)); + else + return false; + + IntPtr nvmlGetPcieThroughput = Kernel32.GetProcAddress(_windowsDll, "nvmlDeviceGetPcieThroughput"); + if (nvmlGetPcieThroughput != IntPtr.Zero) + _windowsNvmlDeviceGetPcieThroughputDelegate = (WindowsNvmlDeviceGetPcieThroughputDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetPcieThroughput, typeof(WindowsNvmlDeviceGetPcieThroughputDelegate)); + else + return false; + + IntPtr nvmlGetHandlePciBus = Kernel32.GetProcAddress(_windowsDll, "nvmlDeviceGetHandleByPciBusId_v2"); + if (nvmlGetHandlePciBus != IntPtr.Zero) + _windowsNvmlDeviceGetHandleByPciBusId = (WindowsNvmlGetHandleByPciBusIdDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetHandlePciBus, typeof(WindowsNvmlGetHandleByPciBusIdDelegate)); + else + return false; + + IntPtr nvmlDeviceGetPciInfo = Kernel32.GetProcAddress(_windowsDll, "nvmlDeviceGetPciInfo_v2"); + if (nvmlDeviceGetPciInfo != IntPtr.Zero) + _windowsNvmlDeviceGetPciInfo = (WindowsNvmlDeviceGetPciInfo)Marshal.GetDelegateForFunctionPointer(nvmlDeviceGetPciInfo, typeof(WindowsNvmlDeviceGetPciInfo)); + else + return false; + + return true; + } + + public static void Close() + { + lock (_syncRoot) + { + if (IsAvailable) + { + if (Software.OperatingSystem.IsUnix) + { + nvmlShutdown(); + } + else if (_windowsDll != IntPtr.Zero) + { + _windowsNvmlShutdown(); + Kernel32.FreeLibrary(_windowsDll); + } + + IsAvailable = false; + } + } + } + + public static NvmlDevice? NvmlDeviceGetHandleByIndex(int index) + { + if (IsAvailable) + { + NvmlDevice nvmlDevice; + if (Software.OperatingSystem.IsUnix) + { + try + { + if (nvmlDeviceGetHandleByIndex(index, out nvmlDevice) == NvmlReturn.Success) + return nvmlDevice; + } + catch (EntryPointNotFoundException) + { + if (nvmlDeviceGetHandleByIndexLegacy(index, out nvmlDevice) == NvmlReturn.Success) + return nvmlDevice; + } + } + else + { try { if (_windowsNvmlDeviceGetHandleByIndex(index, out nvmlDevice) == NvmlReturn.Success) return nvmlDevice; } catch { } - } - } - - return null; - } - - public static NvmlDevice? NvmlDeviceGetHandleByPciBusId(string pciBusId) - { - if (IsAvailable) - { - NvmlDevice nvmlDevice; - if (Software.OperatingSystem.IsUnix) - { - if (nvmlDeviceGetHandleByPciBusId(pciBusId, out nvmlDevice) == NvmlReturn.Success) - return nvmlDevice; - } - else + } + } + + return null; + } + + public static NvmlDevice? NvmlDeviceGetHandleByPciBusId(string pciBusId) + { + if (IsAvailable) + { + NvmlDevice nvmlDevice; + if (Software.OperatingSystem.IsUnix) + { + if (nvmlDeviceGetHandleByPciBusId(pciBusId, out nvmlDevice) == NvmlReturn.Success) + return nvmlDevice; + } + else { try { @@ -327,151 +327,152 @@ internal static class NvidiaML return nvmlDevice; } catch { } - } - } - - return null; - } - - public static int? NvmlDeviceGetPowerUsage(NvmlDevice nvmlDevice) - { - if (IsAvailable) - { - int powerUsage; - if (Software.OperatingSystem.IsUnix) - { - if (nvmlDeviceGetPowerUsage(nvmlDevice, out powerUsage) == NvmlReturn.Success) - return powerUsage; - } - else - { + } + } + + return null; + } + + public static int? NvmlDeviceGetPowerUsage(NvmlDevice nvmlDevice) + { + if (IsAvailable) + { + int powerUsage; + if (Software.OperatingSystem.IsUnix) + { + if (nvmlDeviceGetPowerUsage(nvmlDevice, out powerUsage) == NvmlReturn.Success) + return powerUsage; + } + else + { try - { + { if (_windowsNvmlDeviceGetPowerUsage(nvmlDevice, out powerUsage) == NvmlReturn.Success) return powerUsage; - } + } catch { } - } - } - - return null; - } - - public static uint? NvmlDeviceGetPcieThroughput(NvmlDevice nvmlDevice, NvmlPcieUtilCounter counter) - { - if (IsAvailable) - { - uint pcieThroughput; - if (Software.OperatingSystem.IsUnix) - { - if (nvmlDeviceGetPcieThroughput(nvmlDevice, counter, out pcieThroughput) == NvmlReturn.Success) - return pcieThroughput; - } - else - { + } + } + + return null; + } + + public static uint? NvmlDeviceGetPcieThroughput(NvmlDevice nvmlDevice, NvmlPcieUtilCounter counter) + { + if (IsAvailable) + { + uint pcieThroughput; + if (Software.OperatingSystem.IsUnix) + { + if (nvmlDeviceGetPcieThroughput(nvmlDevice, counter, out pcieThroughput) == NvmlReturn.Success) + return pcieThroughput; + } + else + { try - { + { if (_windowsNvmlDeviceGetPcieThroughputDelegate(nvmlDevice, counter, out pcieThroughput) == NvmlReturn.Success) return pcieThroughput; - } + } catch { } - } - } - - return null; - } - - public static NvmlPciInfo? NvmlDeviceGetPciInfo(NvmlDevice nvmlDevice) - { - if (IsAvailable) - { - var pci = new NvmlPciInfo(); - - if (Software.OperatingSystem.IsUnix) - { - if (nvmlDeviceGetPciInfo(nvmlDevice, ref pci) == NvmlReturn.Success) - return pci; - } - else - { + } + } + + return null; + } + + public static NvmlPciInfo? NvmlDeviceGetPciInfo(NvmlDevice nvmlDevice) + { + if (IsAvailable) + { + var pci = new NvmlPciInfo(); + + if (Software.OperatingSystem.IsUnix) + { + if (nvmlDeviceGetPciInfo(nvmlDevice, ref pci) == NvmlReturn.Success) + return pci; + } + else + { try { if (_windowsNvmlDeviceGetPciInfo(nvmlDevice, ref pci) == NvmlReturn.Success) return pci; } catch { } - } - } - - return null; - } - - [DllImport(LinuxDllName, EntryPoint = "nvmlInit_v2", ExactSpelling = true)] - private static extern NvmlReturn nvmlInit(); - - [DllImport(LinuxDllName, EntryPoint = "nvmlInit", ExactSpelling = true)] - private static extern NvmlReturn nvmlInitLegacy(); - - [DllImport(LinuxDllName, EntryPoint = "nvmlShutdown", ExactSpelling = true)] - private static extern NvmlReturn nvmlShutdown(); - - [DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetHandleByIndex_v2", ExactSpelling = true)] - private static extern NvmlReturn nvmlDeviceGetHandleByIndex(int index, out NvmlDevice device); - - [DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetHandleByPciBusId_v2", ExactSpelling = true)] - private static extern NvmlReturn nvmlDeviceGetHandleByPciBusId([MarshalAs(UnmanagedType.LPStr)] string pciBusId, out NvmlDevice device); - - [DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetHandleByIndex", ExactSpelling = true)] - private static extern NvmlReturn nvmlDeviceGetHandleByIndexLegacy(int index, out NvmlDevice device); - - [DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetPowerUsage", ExactSpelling = true)] - private static extern NvmlReturn nvmlDeviceGetPowerUsage(NvmlDevice device, out int power); - - [DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetPcieThroughput", ExactSpelling = true)] - private static extern NvmlReturn nvmlDeviceGetPcieThroughput(NvmlDevice device, NvmlPcieUtilCounter counter, out uint value); - - [DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetPciInfo_v2")] - private static extern NvmlReturn nvmlDeviceGetPciInfo(NvmlDevice device, ref NvmlPciInfo pci); - - private delegate NvmlReturn WindowsNvmlDelegate(); - - private delegate NvmlReturn WindowsNvmlGetHandleDelegate(int index, out NvmlDevice device); - - private delegate NvmlReturn WindowsNvmlGetHandleByPciBusIdDelegate([MarshalAs(UnmanagedType.LPStr)] string pciBusId, out NvmlDevice device); - - private delegate NvmlReturn WindowsNvmlGetPowerUsageDelegate(NvmlDevice device, out int power); - - private delegate NvmlReturn WindowsNvmlDeviceGetPcieThroughputDelegate(NvmlDevice device, NvmlPcieUtilCounter counter, out uint value); - - private delegate NvmlReturn WindowsNvmlDeviceGetPciInfo(NvmlDevice device, ref NvmlPciInfo pci); - - [StructLayout(LayoutKind.Sequential)] - public struct NvmlDevice - { - public IntPtr Handle; - } - - [StructLayout(LayoutKind.Sequential)] - public struct NvmlPciInfo - { - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] - public string busId; - - public uint domain; - - public uint bus; - - public uint device; - - public ushort pciVendorId; - - public ushort pciDeviceId; - - public uint pciSubSystemId; - - public uint reserved0; - public uint reserved1; - public uint reserved2; - public uint reserved3; - } -} + + } + } + + return null; + } + + [DllImport(LinuxDllName, EntryPoint = "nvmlInit_v2", ExactSpelling = true)] + private static extern NvmlReturn nvmlInit(); + + [DllImport(LinuxDllName, EntryPoint = "nvmlInit", ExactSpelling = true)] + private static extern NvmlReturn nvmlInitLegacy(); + + [DllImport(LinuxDllName, EntryPoint = "nvmlShutdown", ExactSpelling = true)] + private static extern NvmlReturn nvmlShutdown(); + + [DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetHandleByIndex_v2", ExactSpelling = true)] + private static extern NvmlReturn nvmlDeviceGetHandleByIndex(int index, out NvmlDevice device); + + [DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetHandleByPciBusId_v2", ExactSpelling = true)] + private static extern NvmlReturn nvmlDeviceGetHandleByPciBusId([MarshalAs(UnmanagedType.LPStr)] string pciBusId, out NvmlDevice device); + + [DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetHandleByIndex", ExactSpelling = true)] + private static extern NvmlReturn nvmlDeviceGetHandleByIndexLegacy(int index, out NvmlDevice device); + + [DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetPowerUsage", ExactSpelling = true)] + private static extern NvmlReturn nvmlDeviceGetPowerUsage(NvmlDevice device, out int power); + + [DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetPcieThroughput", ExactSpelling = true)] + private static extern NvmlReturn nvmlDeviceGetPcieThroughput(NvmlDevice device, NvmlPcieUtilCounter counter, out uint value); + + [DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetPciInfo_v2")] + private static extern NvmlReturn nvmlDeviceGetPciInfo(NvmlDevice device, ref NvmlPciInfo pci); + + private delegate NvmlReturn WindowsNvmlDelegate(); + + private delegate NvmlReturn WindowsNvmlGetHandleDelegate(int index, out NvmlDevice device); + + private delegate NvmlReturn WindowsNvmlGetHandleByPciBusIdDelegate([MarshalAs(UnmanagedType.LPStr)] string pciBusId, out NvmlDevice device); + + private delegate NvmlReturn WindowsNvmlGetPowerUsageDelegate(NvmlDevice device, out int power); + + private delegate NvmlReturn WindowsNvmlDeviceGetPcieThroughputDelegate(NvmlDevice device, NvmlPcieUtilCounter counter, out uint value); + + private delegate NvmlReturn WindowsNvmlDeviceGetPciInfo(NvmlDevice device, ref NvmlPciInfo pci); + + [StructLayout(LayoutKind.Sequential)] + public struct NvmlDevice + { + public IntPtr Handle; + } + + [StructLayout(LayoutKind.Sequential)] + public struct NvmlPciInfo + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] + public string busId; + + public uint domain; + + public uint bus; + + public uint device; + + public ushort pciVendorId; + + public ushort pciDeviceId; + + public uint pciSubSystemId; + + public uint reserved0; + public uint reserved1; + public uint reserved2; + public uint reserved3; + } +}