65.9K
CodeProject 正在变化。 阅读更多。
Home

电脑温度、风扇转速等。

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (28投票s)

2018年7月10日

CPOL

7分钟阅读

viewsIcon

33575

downloadIcon

2281

充分利用 OpenHardwareMonitorLib.dll 的功能

引言

我有一台自己组装的台式机,配备华硕 P6T 主板。它大约有 7-8 年的历史了,但仍然非常强大,并且运行良好。华硕最初提供了 PC Probe II 来监控温度和风扇转速,但在 Windows 10 发布后,它就不再支持 P6T 了。从那时起,我就一直没有一个令人满意的传感器应用程序。不幸的是,市面上提供的第三方传感器应用程序有点古怪,或者不直观,或者无法按照我想要的方式进行配置。当然,也许是我太挑剔了。

几年前,我曾考虑过 OpenHardwardMonitor,这是一个开源 DLL,似乎涵盖了所有必需的功能。不幸的是,他们提供的 文档充其量只能说是最少的,而且在撰写本文时,显然已经过时了。然后,Howard 在 CodeProject 上写了 一篇很棒的文章,提供了足够的信息让我入门。不幸的是,他未能充分利用 OpenHardwareMonitor 的功能。但在经过一些实验后,我认为我已经做到了。

Using the Code

该代码实现为一个 Visual Studio 2015 自包含项目。它包含 OpenHardwareMonitorLib.dll 版本 0.8.0 Beta。您应该能够下载代码文件,解压缩,在 VS 中加载它,然后编译并执行它。

实现

实现的关键是我创建的一个名为 ohmDataTreepublic 包装类。它实现了

  • IDisposable 接口,以确保 OHM 的资源能够正确关闭,无论
  • 一个设置为每 1.0 秒更新所有传感器数据的计时器
  • 属性 MainboardEnabledFanControllerEnabledCPUEnabledGPUEnabledRAMEnabledHDDEnabled,以公开 OHM 可比的原生属性,允许用户禁用 OHM 中不需要的方面;我假设这会加速传感器扫描,但我没有进行任何测试来确认这一点
  • 变量 hWareList,它以 ohmHwNode 类的链接列表二叉树的形式提供对硬件和传感器信息的访问——稍后将详细介绍
  • 方法 UpdateSensorData,它在 hWareList 中包含的每个 ohmSensor 类的实例中更新传感器值 ValueMinMax,以及它们关联的格式化字符串
  • 变量 configChanged,它指示自上次调用 UpdateSensorData 以来硬件配置已更改;仅供外部信息参考;用户需要根据此信息采取行动,并在需要时重置 configChanged
  • 方法 SensorFromID,它搜索链接列表以查找具有特定唯一传感器 IdentifierohmSensor 实例
  • 方法 SensorFromIndex,它搜索链接列表以按传感器在树中出现的线性顺序查找传感器
  • 公共 string[] 数组 sensorFormatssensorUnits,它们为每个 SensorType 提供格式字符串和单位字符串
  • 方法 getValueString,它返回一个完整的、带单位的格式化值字符串

请注意,configChanged 是通过监视 OHM 的 HardwareAddedHardwareRemoved 事件来设置的。通过测试,我确定在设置或重置 xxxEnabled 属性之一时会触发这些事件。我还通过插入和移除旧的(6-8 年)和新的(<6 个月)USB 硬盘驱动器和 USB 闪存驱动器进行了实验。在所有情况下,两个事件都没有触发,并且新插入的驱动器没有立即显示在扫描中。只有当我关闭应用程序并重新启动它时,它们才会显示出来。如果我在每次更新时调用 OpenHardwareMonitor.Hardware.Computer 类中的 Open()Close() 方法,它们会立即显示出来。但是这两个方法似乎都很消耗 CPU,频繁这样调用会拖慢我的系统,使其几乎无法使用。

我在 GitHub 上发布了一个问题,通知 OHM 的开发人员。Dan Neel 立即回复了,他的快速回复值得称赞。他建议我捕获 USB 连接事件。我设置了一个 WMI ManagementEventWatcher class,并且奏效了,在 USB 设备插入时通知了我。作为回应,我执行了 Close()Open() 事件来更新 OHM。在事件响应时这样做意味着我不会在每次计时器滴答时执行这些方法,从而不会拖慢系统。不幸的是,OHM 似乎会打开每个设备的句柄并保持打开状态,或者诸如此类。Windows 不允许我弹出设备,直到我停止用 OHM 监控它。因此,我将该部分代码通过 #define TrapUSB 置于其中,禁用了该部分代码。如果您想对其进行实验,只需将 TrapUSBNo 更改为 TrapUSB

每个硬件节点的信息都存储在一个名为 ohmHwNode 的类中。每个硬件节点都可以包含一个子硬件节点列表,并且子节点的深度似乎没有限制——每个子节点都可以包含更深层次的子节点列表。因此,我通过递归调用 ScanHardware 方法来实现传感器扫描。它接受一个 OHM IHardware 硬件节点数组,以及一个指向 ohmHwNode 父节点的指针,在最顶层的 IHardware 数组的节点情况下,该指针为 nullohmHwNode 类的结构如下:

public class ohmHwNode
{
    public IHardware hWare = null; //The OpenHardwareMonitor interface for a hardware node
    public ohmHwNode ohmParent = null; //pointer to the parent node
    public ohmHwNode[] ohmChildren = null; //array of pointers to the child nodes

    public string name = ""; //Hardware name
    public HardwareType type; //Hardware type
    public Identifier id;

    public ohmSensor[] ohmSensors = null; //array of ohmSensors in this hardware node
}

ohmParentohmChildren 指针是连接链接列表二叉树的链接。它们允许我在 ProbeForm.cs 文件中的 AddItems 方法中遍历树以检索传感器数据。

每个硬件节点都包含一个 OHM ISensor 数据数组,该数组对于任何给定的硬件节点都可能是空的。我将每个传感器的数据存储在一个名为 ohmSensor 的容器类中,该类的结构如下:

public class ohmSensor
{
    public ISensor sensor = null; //The OpenHardwareMonitor interface for a sensor
    public string Name = ""; //Sensor Name
    public string Identifier = ""; //Sensor Identifier
    public SensorType sType; //Sensor Type
    public float? Value; //nullable native Sensor Value
    public float? Min;  //nullable native Sensor Min
    public float? Max;  //nullable native Sensor Max
    public string stValue = ""; //formatted Value String
    public string stMin = ""; //formatted Min String
    public string stMax = ""; //formatted Max String
    public int Index = 0;
}

stValuestMinstMax string 是由 ohmDataTree 方法 getValueString 从返回的可空值格式化而来的,该方法实现如下:

private string SensorUnits(SensorType type)
{
    switch (type)
    {
        case SensorType.Voltage:
            return "v";
        case SensorType.Clock:
            return "MHz";
        case SensorType.Temperature:
            return "°C";
        case SensorType.Level:
        case SensorType.Control:
        case SensorType.Load:
            return "%";
        case SensorType.Fan:
            return "rpm";
        case SensorType.Flow:
            return "L/h";

        //No documentation so had to guess on these:
        case SensorType.SmallData:
            return "MB";
        case SensorType.Data:
            return "GB";
        case SensorType.Power:
            return "W";

            //No documentation and no guess on these:
            //case SensorType.Factor: 
    }
    return "";
}

private string SensorFormat(SensorType type)
{
    switch (type)
    {
        case SensorType.Voltage:
            return "N3";
        case SensorType.Clock:
            return "N0";
        case SensorType.Temperature:
            return "N1";
        case SensorType.Level:
        case SensorType.Control:
        case SensorType.Load:
            return "N1";
        case SensorType.Fan:
            return "N0";
        case SensorType.Flow:
            return "N1";

        //No documentation so had to guess on these:
        case SensorType.SmallData:
            return "N1";
        case SensorType.Data:
            return "N1";
        case SensorType.Power:
            return "N1";

            //No documentation and no guess on these:
            //case SensorType.Factor: 
    }
    return "";
}

public string getValueString(SensorType type, float? value)
{
    if (value == null)
        return "--- " + sensorUnits[(int)type];
    else
        return value.Value.ToString(sensorFormats[(int)type]) + " " + sensorUnits[(int)type];
}

方法 SensorUnitsSensorFormat 仅用于在运行时构建 sensorsUnitssensorsFormats 字符串数组。

请注意,在撰写本文时,OHM 的文档列出了传感器类型枚举为:

public enum SensorType
{
    Voltage,
    Clock,
    Temperature,
    Load,
    Fan,
    Flow,
    Control,
    Level
}

但在 OHM 的当前版本(0.8.0 Beta)中,已添加了 SmallDataDataFactorPower。OHM 过时且不足的文档是其最大的问题,也可能是广泛实施的最大障碍。

以管理员身份运行

以管理员特权运行应用程序至关重要。当我在台式机上不以管理员特权运行应用程序时,我会得到以下结果:

请注意值为“---”的传感器。那些传感器返回了一个可空的(floart?null 值。

另一方面,当我以管理员特权运行时,我会得到这个:

请注意,主板下的“无传感器”条目仅表示主板硬件节点没有传感器。但是,它有一个子硬件节点,其中包含所有主板传感器。

如果您以管理员特权运行 Visual Studio,它将自动以管理员特权运行调试和发布版本。如果您以普通特权运行 Visual Studio,Probe 程序将提示您提升特权。如果您想了解您的系统在没有管理员权限的情况下如何响应,请在提示时回答“否”。

我还在我的 Sony Vaio SVS15125CXB 笔记本电脑上运行了此程序。它内置了双显示适配器:Intel (R) HD Graphics 4000 和 NVIDIA GeForce GT 640M LE。NVIDIA 用于图形密集型应用程序,并且经常在不需要时关闭。当发生这种情况时,它会为其传感器返回 null 值,因此即使在以管理员特权运行时,这些结果也是有效的。

待办事项

  1. 创建一款精美、可配置、极简、浮动桌面应用程序,用于监控风扇转速和温度,并带有警报——完成,请参阅 CPU 温度、风扇转速等应用程序,第二部分。第二部分现在包括历史跟踪和警报日志。
  2. 尝试理解 OHM 的许多未记录的功能。

历史

  • 2018.07.10:首次实现和发布
  • 2018.07.22:
    • 实现了 sensorFormatssensorUnits 数组
    • 将计时器封装在 ohmDataTree 中,并添加了 UpdateSensors 事件处理程序,以便外部类可以挂钩到更新事件链
    • 添加了 UpdateSensorData 方法来更新 ohmDataTree 中的传感器值,而无需重新创建树,从而无需在每次更新时都处理并重建每个 ohmHwNode 和每个 ohmSensor
  • 2018.10.16:创建了一个桌面应用程序,用于监控风扇转速、温度等,并带有警报、历史跟踪和警报日志。请参阅 CPU 温度、风扇转速等应用程序,第二部分
© . All rights reserved.