Qt案例 使用C++/CLI编程通过引用C#的LibreHardwareMonitorLib库实现监控电脑硬件的温度,温度传感器、风扇速度、电压、负载和时钟速度
在Windows系统中想要通过Qt/C++编程监控电脑硬件的温度,温度传感器、风扇速度、电压、负载和时钟速度的功能:实际上是使用C++/CLI编程通过引用C#的LibreHardwareMonitorLib库实现的。其中需要 .net 4.7.2 库运行环境。
前言
之前有几位私信问发C++ 获取CPU实时温度,获取监控计算机的温度传感器那两篇文章的,也不说有啥问题的,让我一顿猜。时间也久了,我也找不到源码了,最近也不知道总结啥了,于是又重新复习了下,干脆重新整理成这篇文章重新发了。
总得来说:在Windows系统中想要通过Qt/C++编程监控电脑硬件的温度,温度传感器、风扇速度、电压、负载和时钟速度的功能:
实际上是使用C++/CLI编程通过引用C#的LibreHardwareMonitorLib库实现的。
其中需要 .net 4.7.2 库运行环境。
这是我能找到最简单的方案了,总不能自己写个驱动来获取数据吧…
目录导读
开发环境-相关库
开发环境:
Visual Studio 2019 Relese X64配合Qt 5.15.2开发,
C# 必须安装 .net4.7.2版本.
LibreHardwareMonitorLib库:
Libre Hardware Monitor是开放式硬件监视器的一个分支,是一个免费的软件,可以监控计算机的温度传感器、风扇速度、电压、负载和时钟速度。
LibreHardwareMonitor/LibreHardwareMonitor 库在GitHub有开源代码,
而在Visual Studio NuGet包中也有创建好的包,截止这篇文章最新的发布日期为2024年11月24日,也算是比较新的,还一直在维护。
当前,这个库是用C#开发的,如下图示:
数据是实时获取的,但是我总感觉与任务管理器中的数据对比,总感觉慢了一点,数值差异也有一点,但是总的数据浮动是没问题的。
整体思路:
C#端封装LibreHardwareMonitorLib库生成一个静态DLL库供QT/C++调用定时获取监控数据。
如图:
C#端
太久没写C#了,虽然忘了不少,但是还好还能看得懂。
想要Qt/C++调用需要C# 端封装LibreHardwareMonitorLib库并且开放接口:
新增一个C#的静态库项目并且引用LibreHardwareMonitorLib包
包具体的调用在LibreHardwareMonitor/LibreHardwareMonitor 库简介中有详细的描述。
这里重新封装一下:
定义一个HARDWAREWrapper单例类 用于获取数据
在初始化类时获取硬件标识与硬件类的对应视图数据,
IHardware类就是硬件类,对应一个硬件设备。
//private Dictionary<string, IHardware> HardwareMap =new Dictionary<string, IHardware>();
//class HARDWAREWrapper
private void init()
{
Computer computer = new Computer
{
IsCpuEnabled = true,
IsGpuEnabled = true,
IsMemoryEnabled = true,
IsMotherboardEnabled = true,
IsControllerEnabled = true,
IsNetworkEnabled = true,
IsStorageEnabled = true,
IsPsuEnabled=true,
IsBatteryEnabled=true
};
computer.Open();
computer.Accept(new UpdateVisitor());
//! 获取硬件信息
foreach (IHardware hardware in computer.Hardware)
{
HardwareMap[hardware.Identifier.ToString()] = hardware;
}
}
为了不频繁刷新数据,这里我采用按硬件模块刷新所有节点数据。
通过定义Tuple<int, string, string> 类型返回
Tuple<硬件类型, 硬件标识符, 硬件名称> 的信息给到QT/C++端。
//! 获取硬件名称与分类
public List<Tuple<int, string, string>> GetHardwareTypes()
{
List<Tuple<int, string, string>> DataVal =new List<Tuple<int, string, string>>();
foreach (var itme in HardwareMap)
{
DataVal.Add(new Tuple<int, string, string>((int)itme.Value.HardwareType, itme.Key, itme.Value.Name));
}
return DataVal;
}
定义个 SensorVal 数据项结构体 用于获取ISensor 类中的数据,用结构体方便数据交互。
ISensor 类就是监控的电脑硬件的温度,温度传感器,风扇速度,电压,负载和时钟速度等信息项
根据硬件标识符获取监控项信息:
public List<SensorVal> GetSensorVals(string HardwareName)
{
List<SensorVal> DataVal = new List<SensorVal>();
//! 没匹配到数据
if (!HardwareMap.ContainsKey(HardwareName))
return DataVal;
//! 一个硬件项只刷新一次,避免高频刷新
HardwareMap[HardwareName].Update();
foreach (ISensor sensor in HardwareMap[HardwareName].Sensors)
{
SensorVal val = new();
val.Name = sensor.Name;
val.Index = sensor.Index;
val.Identifier = sensor.Identifier.ToString();
val.Max = sensor.Max.GetValueOrDefault(0.0f);
val.Min = sensor.Min.GetValueOrDefault(0.0f);
val.Value = sensor.Value.GetValueOrDefault(0.0f);
val.SensorType = (int)sensor.SensorType;
DataVal.Add(val);
}
foreach (IHardware subhardware in HardwareMap[HardwareName].SubHardware)
{
foreach (ISensor sensor in subhardware.Sensors)
{
SensorVal val = new();
val.Name = sensor.Name;
val.Index = sensor.Index;
val.Identifier = sensor.Identifier.ToString();
val.Max = sensor.Max.GetValueOrDefault(0.0f);
val.Min = sensor.Min.GetValueOrDefault(0.0f);
val.Value = sensor.Value.GetValueOrDefault(0.0f);
val.SensorType = (int)sensor.SensorType;
DataVal.Add(val);
}
}
return DataVal;
}
右键生成DLL文件。
QT/C++端
QT端通过C++/CLI编程定时调用C#的DLL库方法获取监控数据。
C++/CLI(C++ Common Language Infrastructure)是微软开发的一种编程语言,它是标准C++的扩展,用于在.NET框架上开发应用程序。C++/CLI作为托管C++的后继者,提供了更好的语法和与.NET框架的更紧密集成。
开发这么久了,C++/CLI编程模式也不是第一次尝试用,
我深刻的记得在以前是尝试过使用C++/CLI编程模式调用C#库的,但是均以失败告终,
就比如:C++/CLI 或 C++/CX 不支持两阶段名称查找;请使用 /Zc:twoPhase-
这个问题我以前愣是没解决了,
现在直接[解决方案属性]-->[C/C++]-->[命令行-]-->[ /Zc:twoPhase- ]搞定。
在通过C++/CLI获取C#库数据时需要使用msclr::interop::marshal_context context;
将C#的string类型转成std::string类型,同时使用System::String^类型中转。
其他如int,double,float是可以直接交换数据,
void IhwmWrapper_DALPrivate::LoadHardwareTypes(QMap<HardwareType, QList<QPair<QString, QString>>>& DataVal)
{
msclr::interop::marshal_context context;
System::Collections::Generic::List<System::Tuple<int, System::String^, System::String^>^>^
HardwareMap = HARDWAREWrapperNameSpace::HARDWAREWrapper::GetInstance()->GetHardwareTypes();
if (HardwareMap == nullptr)
return;
auto pair = HardwareMap->GetEnumerator();
while (pair.MoveNext())
{
auto outerPair = pair.Current;
std::string Identifier = context.marshal_as<std::string>(outerPair->Item2);
std::string name = context.marshal_as<std::string>(outerPair->Item3);
DataVal[(HardwareType)outerPair->Item1].append(QPair<QString, QString>(QString::fromLocal8Bit(Identifier.c_str()), QString::fromLocal8Bit(name.c_str())));
}
}
值得一提的是:
1.在QT混合C++/CLI编程时使用for each遍历数据会编译失败,
只能通过使用GetEnumerator()遍历字典,列表类数据。
2.C#中数据默认是Utf16需要通过QString::fromLocal8Bit()获取正常字符串.
同样通过msclr::interop::marshal_context context
将std::string 转化成System::String^再传递给C#的string参数方法。
void IhwmWrapper_DALPrivate::LoadSensorList(QString HardwareName, QMap<SensorType, QList<SensorItem>>& SenSorList)
{
msclr::interop::marshal_context context;
System::String^ Name = context.marshal_as<System::String^>(std::string(HardwareName.toLocal8Bit()));
System::Collections::Generic::List<HARDWAREWrapperNameSpace::SensorVal>^ DataVal = HARDWAREWrapperNameSpace::HARDWAREWrapper::GetInstance()->GetSensorVals(Name);
if (DataVal == nullptr)
return;
auto sensorEnum = DataVal->GetEnumerator();
while (sensorEnum.MoveNext())
{
auto sensor = sensorEnum.Current;
SensorItem item;
item.Index = sensor.Index;
item.Max = sensor.Max;
item.Min = sensor.Min;
std::string Identifier = context.marshal_as<std::string>(sensor.Identifier);
item.Identifier= QString::fromLocal8Bit(Identifier.c_str());
std::string sensorname = context.marshal_as<std::string>(sensor.Name);
item.Name= QString::fromLocal8Bit(sensorname.c_str());
item.Value = sensor.Value;
item.SensorType = sensor.SensorType;
SenSorList[(SensorType)sensor.SensorType].append(item);
}
}
至此Qt就成功获取到了硬件的监控项数据,
剩下的就只需要使用一个定时器。
定时获取监控项数据,并显示到界面上,就完成了相关功能
TreeNodes::TreeNodes(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
timer = new QTimer();
connect(timer, SIGNAL(timeout()), this, SLOT(dataRefresh()));
}
TreeNodes::~TreeNodes()
{
if (timer != nullptr)
{
timer->stop();
delete timer;
}
}
void TreeNodes::Init(QString _identifier, QString _hardwarename)
{
Identifier = _identifier;
HardwareName = _hardwarename;
QMap<SensorType, QList<SensorItem>> SensorMaps = globalIhwmWrapper()->GetSensorList(Identifier);
ui.treeWidget->clear();
ui.treeWidget->setColumnCount(4);
ui.treeWidget->setHeaderLabels(QStringList() << "Sensor" << "Value" << "Max" << "Min");
QList<SensorType> keys = SensorMaps.keys();
for (int i = 0; i < keys.count(); i++)
{
QTreeWidgetItem* SensorType = new QTreeWidgetItem();
SensorType->setText(0, SENSORTYPE(keys[i]));
SensorType->setIcon(0, SENSORICON(keys[i]));
QList<SensorItem> SensorList = SensorMaps[keys[i]];
for (int j = 0; j < SensorList.count(); j++)
{
QTreeWidgetItem* child = new QTreeWidgetItem();
child->setText(0, SensorList[j].Name);
child->setText(1, NORMUNIT(keys[i], SensorList[j].Value));
child->setText(2, NORMUNIT(keys[i], SensorList[j].Max));
child->setText(3, NORMUNIT(keys[i], SensorList[j].Min));
SensorType->addChild(child);
TreeItemMap.insert(SensorList[j].Identifier, child);
}
ui.treeWidget->addTopLevelItem(SensorType);
}
ui.treeWidget->expandAll();
ui.treeWidget->setColumnWidth(0, 260);
ui.treeWidget->header()->setSectionResizeMode(QHeaderView::Fixed);
timer->start(1000);
}
void TreeNodes::dataRefresh()
{
//! 数据更新时暂停定时器,数据更新完再等待刷新
timer->stop();
QMap<SensorType, QList<SensorItem>> SensorMaps = globalIhwmWrapper()->GetSensorList(Identifier);
//qDebug() << "Name: " << HardwareName << " Map: " << SensorMaps.keys();
QList<SensorType> keys = SensorMaps.keys();
for (int i = 0; i < keys.count(); i++)
{
QList<SensorItem> SensorList = SensorMaps[keys[i]];
for (int j = 0; j < SensorList.count(); j++)
{
QString Identifier = SensorList[j].Identifier;
QTreeWidgetItem* child = TreeItemMap[Identifier];
if (child != nullptr) {}
child->setText(1, NORMUNIT(keys[i], SensorList[j].Value));
child->setText(2, NORMUNIT(keys[i], SensorList[j].Max));
child->setText(3, NORMUNIT(keys[i], SensorList[j].Min));
}
}
timer->start(1000);
}
实现效果
最终效果展示:
可执行程序示例下载链接: https://pan.baidu.com/s/1uwbAbtFdb6fiFrQTueVptw?pwd=yht7
如果上述的示例代码没有看懂,那么可以在GItHub上搜索 https://gitlab.com/OpenRGBDevelopers/lhwm-wrapper 项目示例,这个项目的示例更加简单易懂。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)