下面是我写的一个函数,它旨在使用COM和JavaScript返回用户PC存储的名称:
Q_INVOKABLE QString systeminfo::getDiskInfo() {
// Disk info
HRESULT hres;
IWbemLocator* pLoc = NULL;
IWbemServices* pSvc = NULL;
initializeCOM(hres, pLoc, pSvc);
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
ConvertStringToBSTR("WQL"),
ConvertStringToBSTR("SELECT * FROM Win32_DiskDrive"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hres))
{
std::cout << "Error code = 0x" << std::hex << hres << std::endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return QString(); // Returns an empty string in case of an error
}
// Getting data
QString storageModel;
while (pEnumerator)
{
IWbemClassObject* pclsObj = NULL;
ULONG uReturn = 0;
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if (0 == uReturn)
{
break;
}
VARIANT vtProp;
hr = pclsObj->Get(L"Model", 0, &vtProp, 0, 0);
storageModel = QString::fromWCharArray(vtProp.bstrVal);
qDebug() << "Storage: " << storageModel;
VariantClear(&vtProp);
pclsObj->Release();
}
// Cleanup
pSvc->Release();
pLoc->Release();
pEnumerator->Release();
CoUninitialize();
return storageModel;
}
字符串
在App.qml中,我将值赋给了一个文本字段:
Component.onCompleted: {
mainScreen.gpuInfoField.text = sysInfo.getGpuInfo();
mainScreen.diskInfoField.text = sysInfo.getDiskInfo();
}
型
在构建项目时,qDebug()
触发的控制台输出显示了预期的值,即磁盘的名称。但随后它抛出了此错误,文本字段的值没有更改:qrc:/qt/qml/content/App.qml:20: TypeError: Value is undefined and could not be converted to an object
个
类似编写的getGpuInfo();函数工作正常。
要重现此问题,请在Qt Creator中创建一个Qt Quick项目,并添加一个带有getData Info()函数的类“systeminfo.cpp”,不要忘记初始化COM并包含Wbemidl. h和comdef. h。在Qt Design Studio中添加一个文本字段,并尝试在App. qml中分配值。这很明显,但请注意,除了Windows之外的任何操作系统都无法工作,因为Windows Management Instrumentation API是Windows特定的。
我花了整整一天的时间试图弄清楚到底是什么问题,添加了很多错误处理程序和qDebugs,然后完全删除了该函数,并编写了一个类似的函数getRamInfo(),具有完全相同的问题和完全相同的错误。qDebugs()将预期值输出到控制台,但是仍然有一个错误,指出该值是未定义的。getGpuInfo()功能正常,它几乎是一样的。我最初认为这个错误是特定于getGpuInfo(),但同样的问题,在新编写的函数暗示相反.不知道是什么问题,因为函数本身似乎工作正常,并打印磁盘的名称.如果你需要更多的信息,试图找到解决这个问题,随时写在评论.我真的需要帮助.
任何帮助,任何想法,在这一点上的任何东西都将受到高度赞赏。
Minimal Reproducible ExampleSide Note:* 有趣的是,当我创建这个示例并构建它时,没有一个函数可以工作。甚至getGpuInfo();函数也不再按预期工作,而当控制台中显示预期值时,有一个TypeError。我肯定错过了一些东西。*
在新创建的Qt Quick项目中创建一个systeminfo
类。.cpp代码必须包含COM初始化函数,getGpuInfo()和getGpuInfo(),如下所示:
#define _WIN32_DCOM
#include <iostream>
#include <comdef.h>
#include <Wbemidl.h>
#include "systeminfo.h"
#include <QDebug>
#pragma comment(lib, "wbemuuid.lib")
systeminfo::systeminfo(QObject *parent) : QObject(parent) {
HRESULT hres;
IWbemLocator* pLoc = nullptr;
IWbemServices* pSvc = nullptr;
initializeCOM(hres, pLoc, pSvc);
}
// Function for converting char* to BSTR.
BSTR ConvertStringToBSTR(const char *pSrc)
{
const size_t cSize = strlen(pSrc)+1;
wchar_t* wc = new wchar_t[cSize];
mbstowcs (wc, pSrc, cSize);
BSTR bstr = SysAllocString(wc);
delete [] wc;
return bstr;
}
void systeminfo::initializeCOM(HRESULT& hres, IWbemLocator*& pLoc, IWbemServices*& pSvc) {
// Initializing COM.
hres = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (FAILED(hres) && hres != RPC_E_CHANGED_MODE)
{
std::cout << "There was an error initializing COM. Error code = 0x" << std::hex << hres << std::endl;
return;
}
// Configuring COM security level.
hres = CoInitializeSecurity(
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE,
NULL);
if(hres == RPC_E_TOO_LATE)
hres = S_OK;
else if (FAILED(hres))
{
std::cout << "There was an error configuring COM security level. Error code = 0x" << std::hex << hres << std::endl;
CoUninitialize();
return;
}
// Getting a pointer to the WMI service.
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID*)&pLoc);
if (FAILED(hres))
{
std::cout << "Error creating IWbemLocator instance. Error code = 0x" << std::hex << hres << std::endl;
CoUninitialize();
return;
}
// Connecting to WMI namespace.
hres = pLoc->ConnectServer(
ConvertStringToBSTR("ROOT\\CIMV2"),
nullptr,
nullptr,
0,
0,
0,
0,
&pSvc);
if (FAILED(hres))
{
std::cout << "There was an error connecting to WMI namespace. Error code = 0x" << std::hex << hres << std::endl;
pLoc->Release();
CoUninitialize();
return;
}
// Setting the security level for WMI proxy.
hres = CoSetProxyBlanket(
pSvc,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE,
NULL,
RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE);
if (FAILED(hres))
{
std::cout << "Error setting security level. Error code = 0x" << std::hex << hres << std::endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return;
}
}
Q_INVOKABLE QString systeminfo::getGpuInfo() {
HRESULT hres;
IWbemLocator* pLoc = NULL;
IWbemServices* pSvc = NULL;
initializeCOM(hres, pLoc, pSvc);
// Using WMI to retrieve information about the video controller.
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
ConvertStringToBSTR("WQL"),
ConvertStringToBSTR("SELECT * FROM Win32_VideoController"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hres))
{
std::cout << "Query execution failed. Error code = 0x" << std::hex << hres << std::endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return QString(); // Returns an empty string in case of an error.
}
IWbemClassObject* pclsObj = NULL;
ULONG uReturn = 0;
QString gpuInfo;
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if (0 == uReturn)
{
break;
}
VARIANT vtProp;
// Getting the name of the GPU.
hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
gpuInfo = QString::fromWCharArray(vtProp.bstrVal);
qDebug() << "GPU: " << gpuInfo;
VariantClear(&vtProp);
pclsObj->Release();
}
// Cleanup.
pSvc->Release();
pLoc->Release();
pEnumerator->Release();
CoUninitialize();
return gpuInfo;
}
Q_INVOKABLE QString systeminfo::getDiskInfo() {
// Using WMI to get retrieve information about the storage.
HRESULT hres;
IWbemLocator* pLoc = NULL;
IWbemServices* pSvc = NULL;
initializeCOM(hres, pLoc, pSvc);
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
ConvertStringToBSTR("WQL"),
ConvertStringToBSTR("SELECT * FROM Win32_DiskDrive"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hres))
{
std::cout << "Query execution failed. Error code = 0x" << std::hex << hres << std::endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return QString(); // Returns an empty string in case of an error.
}
QString storageModel;
while (pEnumerator)
{
IWbemClassObject* pclsObj = NULL;
ULONG uReturn = 0;
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if (0 == uReturn)
{
break;
}
VARIANT vtProp;
// Getting the name of the disk.
hr = pclsObj->Get(L"Model", 0, &vtProp, 0, 0);
storageModel = QString::fromWCharArray(vtProp.bstrVal);
qDebug() << "Storage: " << storageModel;
VariantClear(&vtProp);
pclsObj->Release();
}
// Cleanup.
pSvc->Release();
pLoc->Release();
pEnumerator->Release();
CoUninitialize();
return storageModel;
}
型
在头文件(systeminfo. h)中定义systeminfo类
#ifndef SYSTEMINFO_H
#define SYSTEMINFO_H
#include <QObject>
#include <comdef.h>
#include <Wbemidl.h>
interface IWbemLocator;
interface IWbemServices;
class systeminfo : public QObject
{
Q_OBJECT
public:
explicit systeminfo(QObject *parent = nullptr);
Q_INVOKABLE QString getGpuInfo();
Q_INVOKABLE QString getDiskInfo();
private:
void initializeCOM(HRESULT& hres, IWbemLocator*& pLoc, IWbemServices*& pSvc);
};
#endif // SYSTEMINFO_H
型
在你的main.cpp文件中定义一个sysInfo对象,并使它对QML可用,包括QQmlContext和systeminfo.h:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext> // Add this line
#include "app_environment.h"
#include "import_qml_components_plugins.h"
#include "import_qml_plugins.h"
#include "../systeminfo.h" // And this
int main(int argc, char *argv[])
{
set_qt_environment();
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
// Add these two lines below:
systeminfo sysInfo;
engine.rootContext()->setContextProperty("sysInfo", &sysInfo);
const QUrl url(u"qrc:/qt/qml/Main/main.qml"_qs);
QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreated,
&app,
[url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
},
Qt::QueuedConnection);
engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml");
engine.addImportPath(":/");
engine.load(url);
if (engine.rootObjects().isEmpty()) {
return -1;
}
return app.exec();
}
型
请注意,我的代码中的#include ".../systeminfo.h"
假设该文件位于项目的根目录中。如果您在其他地方创建了一个systeminfo类,则需要指定该路径-显然。
在Screen01.ui.qml中创建两个ID为gpuInfoField
和diskInfoField
的文本区域,并禁用它们:
import QtQuick 6.2
import QtQuick.Controls 6.2
import nameofyourproject
Rectangle {
id: rectangle
width: Constants.width
height: Constants.height
color: Constants.backgroundColor
TextArea {
id: gpuInfoField
x: 511
y: 267
width: 714
height: 136
enabled: false
placeholderText: qsTr("Text Area")
}
TextArea {
id: diskInfoField
x: 511
y: 453
width: 714
height: 136
enabled: false
placeholderText: qsTr("Text Area")
}
}
型
将值分配给App.qml中的文本区域:
Component.onCompleted: {
mainScreen.gpuInfoField.text = sysInfo.getGpuInfo();
mainScreen.diskInfoField.text = sysInfo.getDiskInfo();
}
型
最后,确保在CMakeLists.txt中链接wbemuuid
库:
target_link_libraries(MREApp PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Qml
Qt6::Quick
wbemuuid #Add this library
)
型
在我的例子中,文本区域的值没有改变,控制台输出如下所示:
GPU: "NVIDIA GeForce RTX 3080 Ti"
qrc:/qt/qml/content/App.qml:19: TypeError: Value is undefined and could not be converted to an object
型
1条答案
按热度按时间sxissh061#
你的问题与你正在阅读的C++对象无关。这是你试图向其分配文本的TextArea对象的问题。你不能访问对象的子对象的ID。相反,你需要通过属性公开对象。在我上面的评论中,我应该建议使用属性别名而不仅仅是属性,但它们在这种情况下都应该工作。
MainScreen.qml
字符串
App.qml
型
也就是说,你通常不需要访问整个对象。通常最好是简单地将需要的子对象的属性暴露在类之外:
MainScreen.qml
型
App.qml
型