未定义值错误和QML文本字段不更新,尽管在Qt Quick C++项目中正确的qvod输出

xu3bshqb  于 2024-01-09  发布在  其他
关注(0)|答案(1)|浏览(151)

下面是我写的一个函数,它旨在使用COM和JavaScript返回用户PC存储的名称:

  1. Q_INVOKABLE QString systeminfo::getDiskInfo() {
  2. // Disk info
  3. HRESULT hres;
  4. IWbemLocator* pLoc = NULL;
  5. IWbemServices* pSvc = NULL;
  6. initializeCOM(hres, pLoc, pSvc);
  7. IEnumWbemClassObject* pEnumerator = NULL;
  8. hres = pSvc->ExecQuery(
  9. ConvertStringToBSTR("WQL"),
  10. ConvertStringToBSTR("SELECT * FROM Win32_DiskDrive"),
  11. WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
  12. NULL,
  13. &pEnumerator);
  14. if (FAILED(hres))
  15. {
  16. std::cout << "Error code = 0x" << std::hex << hres << std::endl;
  17. pSvc->Release();
  18. pLoc->Release();
  19. CoUninitialize();
  20. return QString(); // Returns an empty string in case of an error
  21. }
  22. // Getting data
  23. QString storageModel;
  24. while (pEnumerator)
  25. {
  26. IWbemClassObject* pclsObj = NULL;
  27. ULONG uReturn = 0;
  28. HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
  29. if (0 == uReturn)
  30. {
  31. break;
  32. }
  33. VARIANT vtProp;
  34. hr = pclsObj->Get(L"Model", 0, &vtProp, 0, 0);
  35. storageModel = QString::fromWCharArray(vtProp.bstrVal);
  36. qDebug() << "Storage: " << storageModel;
  37. VariantClear(&vtProp);
  38. pclsObj->Release();
  39. }
  40. // Cleanup
  41. pSvc->Release();
  42. pLoc->Release();
  43. pEnumerator->Release();
  44. CoUninitialize();
  45. return storageModel;
  46. }

字符串
在App.qml中,我将值赋给了一个文本字段:

  1. Component.onCompleted: {
  2. mainScreen.gpuInfoField.text = sysInfo.getGpuInfo();
  3. mainScreen.diskInfoField.text = sysInfo.getDiskInfo();
  4. }


在构建项目时,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(),如下所示:

  1. #define _WIN32_DCOM
  2. #include <iostream>
  3. #include <comdef.h>
  4. #include <Wbemidl.h>
  5. #include "systeminfo.h"
  6. #include <QDebug>
  7. #pragma comment(lib, "wbemuuid.lib")
  8. systeminfo::systeminfo(QObject *parent) : QObject(parent) {
  9. HRESULT hres;
  10. IWbemLocator* pLoc = nullptr;
  11. IWbemServices* pSvc = nullptr;
  12. initializeCOM(hres, pLoc, pSvc);
  13. }
  14. // Function for converting char* to BSTR.
  15. BSTR ConvertStringToBSTR(const char *pSrc)
  16. {
  17. const size_t cSize = strlen(pSrc)+1;
  18. wchar_t* wc = new wchar_t[cSize];
  19. mbstowcs (wc, pSrc, cSize);
  20. BSTR bstr = SysAllocString(wc);
  21. delete [] wc;
  22. return bstr;
  23. }
  24. void systeminfo::initializeCOM(HRESULT& hres, IWbemLocator*& pLoc, IWbemServices*& pSvc) {
  25. // Initializing COM.
  26. hres = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
  27. if (FAILED(hres) && hres != RPC_E_CHANGED_MODE)
  28. {
  29. std::cout << "There was an error initializing COM. Error code = 0x" << std::hex << hres << std::endl;
  30. return;
  31. }
  32. // Configuring COM security level.
  33. hres = CoInitializeSecurity(
  34. NULL,
  35. -1,
  36. NULL,
  37. NULL,
  38. RPC_C_AUTHN_LEVEL_DEFAULT,
  39. RPC_C_IMP_LEVEL_IMPERSONATE,
  40. NULL,
  41. EOAC_NONE,
  42. NULL);
  43. if(hres == RPC_E_TOO_LATE)
  44. hres = S_OK;
  45. else if (FAILED(hres))
  46. {
  47. std::cout << "There was an error configuring COM security level. Error code = 0x" << std::hex << hres << std::endl;
  48. CoUninitialize();
  49. return;
  50. }
  51. // Getting a pointer to the WMI service.
  52. hres = CoCreateInstance(
  53. CLSID_WbemLocator,
  54. 0,
  55. CLSCTX_INPROC_SERVER,
  56. IID_IWbemLocator, (LPVOID*)&pLoc);
  57. if (FAILED(hres))
  58. {
  59. std::cout << "Error creating IWbemLocator instance. Error code = 0x" << std::hex << hres << std::endl;
  60. CoUninitialize();
  61. return;
  62. }
  63. // Connecting to WMI namespace.
  64. hres = pLoc->ConnectServer(
  65. ConvertStringToBSTR("ROOT\\CIMV2"),
  66. nullptr,
  67. nullptr,
  68. 0,
  69. 0,
  70. 0,
  71. 0,
  72. &pSvc);
  73. if (FAILED(hres))
  74. {
  75. std::cout << "There was an error connecting to WMI namespace. Error code = 0x" << std::hex << hres << std::endl;
  76. pLoc->Release();
  77. CoUninitialize();
  78. return;
  79. }
  80. // Setting the security level for WMI proxy.
  81. hres = CoSetProxyBlanket(
  82. pSvc,
  83. RPC_C_AUTHN_WINNT,
  84. RPC_C_AUTHZ_NONE,
  85. NULL,
  86. RPC_C_AUTHN_LEVEL_CALL,
  87. RPC_C_IMP_LEVEL_IMPERSONATE,
  88. NULL,
  89. EOAC_NONE);
  90. if (FAILED(hres))
  91. {
  92. std::cout << "Error setting security level. Error code = 0x" << std::hex << hres << std::endl;
  93. pSvc->Release();
  94. pLoc->Release();
  95. CoUninitialize();
  96. return;
  97. }
  98. }
  99. Q_INVOKABLE QString systeminfo::getGpuInfo() {
  100. HRESULT hres;
  101. IWbemLocator* pLoc = NULL;
  102. IWbemServices* pSvc = NULL;
  103. initializeCOM(hres, pLoc, pSvc);
  104. // Using WMI to retrieve information about the video controller.
  105. IEnumWbemClassObject* pEnumerator = NULL;
  106. hres = pSvc->ExecQuery(
  107. ConvertStringToBSTR("WQL"),
  108. ConvertStringToBSTR("SELECT * FROM Win32_VideoController"),
  109. WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
  110. NULL,
  111. &pEnumerator);
  112. if (FAILED(hres))
  113. {
  114. std::cout << "Query execution failed. Error code = 0x" << std::hex << hres << std::endl;
  115. pSvc->Release();
  116. pLoc->Release();
  117. CoUninitialize();
  118. return QString(); // Returns an empty string in case of an error.
  119. }
  120. IWbemClassObject* pclsObj = NULL;
  121. ULONG uReturn = 0;
  122. QString gpuInfo;
  123. while (pEnumerator)
  124. {
  125. HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
  126. if (0 == uReturn)
  127. {
  128. break;
  129. }
  130. VARIANT vtProp;
  131. // Getting the name of the GPU.
  132. hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
  133. gpuInfo = QString::fromWCharArray(vtProp.bstrVal);
  134. qDebug() << "GPU: " << gpuInfo;
  135. VariantClear(&vtProp);
  136. pclsObj->Release();
  137. }
  138. // Cleanup.
  139. pSvc->Release();
  140. pLoc->Release();
  141. pEnumerator->Release();
  142. CoUninitialize();
  143. return gpuInfo;
  144. }
  145. Q_INVOKABLE QString systeminfo::getDiskInfo() {
  146. // Using WMI to get retrieve information about the storage.
  147. HRESULT hres;
  148. IWbemLocator* pLoc = NULL;
  149. IWbemServices* pSvc = NULL;
  150. initializeCOM(hres, pLoc, pSvc);
  151. IEnumWbemClassObject* pEnumerator = NULL;
  152. hres = pSvc->ExecQuery(
  153. ConvertStringToBSTR("WQL"),
  154. ConvertStringToBSTR("SELECT * FROM Win32_DiskDrive"),
  155. WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
  156. NULL,
  157. &pEnumerator);
  158. if (FAILED(hres))
  159. {
  160. std::cout << "Query execution failed. Error code = 0x" << std::hex << hres << std::endl;
  161. pSvc->Release();
  162. pLoc->Release();
  163. CoUninitialize();
  164. return QString(); // Returns an empty string in case of an error.
  165. }
  166. QString storageModel;
  167. while (pEnumerator)
  168. {
  169. IWbemClassObject* pclsObj = NULL;
  170. ULONG uReturn = 0;
  171. HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
  172. if (0 == uReturn)
  173. {
  174. break;
  175. }
  176. VARIANT vtProp;
  177. // Getting the name of the disk.
  178. hr = pclsObj->Get(L"Model", 0, &vtProp, 0, 0);
  179. storageModel = QString::fromWCharArray(vtProp.bstrVal);
  180. qDebug() << "Storage: " << storageModel;
  181. VariantClear(&vtProp);
  182. pclsObj->Release();
  183. }
  184. // Cleanup.
  185. pSvc->Release();
  186. pLoc->Release();
  187. pEnumerator->Release();
  188. CoUninitialize();
  189. return storageModel;
  190. }


在头文件(systeminfo. h)中定义systeminfo类

  1. #ifndef SYSTEMINFO_H
  2. #define SYSTEMINFO_H
  3. #include <QObject>
  4. #include <comdef.h>
  5. #include <Wbemidl.h>
  6. interface IWbemLocator;
  7. interface IWbemServices;
  8. class systeminfo : public QObject
  9. {
  10. Q_OBJECT
  11. public:
  12. explicit systeminfo(QObject *parent = nullptr);
  13. Q_INVOKABLE QString getGpuInfo();
  14. Q_INVOKABLE QString getDiskInfo();
  15. private:
  16. void initializeCOM(HRESULT& hres, IWbemLocator*& pLoc, IWbemServices*& pSvc);
  17. };
  18. #endif // SYSTEMINFO_H


在你的main.cpp文件中定义一个sysInfo对象,并使它对QML可用,包括QQmlContext和systeminfo.h:

  1. #include <QGuiApplication>
  2. #include <QQmlApplicationEngine>
  3. #include <QQmlContext> // Add this line
  4. #include "app_environment.h"
  5. #include "import_qml_components_plugins.h"
  6. #include "import_qml_plugins.h"
  7. #include "../systeminfo.h" // And this
  8. int main(int argc, char *argv[])
  9. {
  10. set_qt_environment();
  11. QGuiApplication app(argc, argv);
  12. QQmlApplicationEngine engine;
  13. // Add these two lines below:
  14. systeminfo sysInfo;
  15. engine.rootContext()->setContextProperty("sysInfo", &sysInfo);
  16. const QUrl url(u"qrc:/qt/qml/Main/main.qml"_qs);
  17. QObject::connect(
  18. &engine,
  19. &QQmlApplicationEngine::objectCreated,
  20. &app,
  21. [url](QObject *obj, const QUrl &objUrl) {
  22. if (!obj && url == objUrl)
  23. QCoreApplication::exit(-1);
  24. },
  25. Qt::QueuedConnection);
  26. engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml");
  27. engine.addImportPath(":/");
  28. engine.load(url);
  29. if (engine.rootObjects().isEmpty()) {
  30. return -1;
  31. }
  32. return app.exec();
  33. }


请注意,我的代码中的#include ".../systeminfo.h"假设该文件位于项目的根目录中。如果您在其他地方创建了一个systeminfo类,则需要指定该路径-显然。
在Screen01.ui.qml中创建两个ID为gpuInfoFielddiskInfoField的文本区域,并禁用它们:

  1. import QtQuick 6.2
  2. import QtQuick.Controls 6.2
  3. import nameofyourproject
  4. Rectangle {
  5. id: rectangle
  6. width: Constants.width
  7. height: Constants.height
  8. color: Constants.backgroundColor
  9. TextArea {
  10. id: gpuInfoField
  11. x: 511
  12. y: 267
  13. width: 714
  14. height: 136
  15. enabled: false
  16. placeholderText: qsTr("Text Area")
  17. }
  18. TextArea {
  19. id: diskInfoField
  20. x: 511
  21. y: 453
  22. width: 714
  23. height: 136
  24. enabled: false
  25. placeholderText: qsTr("Text Area")
  26. }
  27. }


将值分配给App.qml中的文本区域:

  1. Component.onCompleted: {
  2. mainScreen.gpuInfoField.text = sysInfo.getGpuInfo();
  3. mainScreen.diskInfoField.text = sysInfo.getDiskInfo();
  4. }


最后,确保在CMakeLists.txt中链接wbemuuid库:

  1. target_link_libraries(MREApp PRIVATE
  2. Qt6::Core
  3. Qt6::Gui
  4. Qt6::Qml
  5. Qt6::Quick
  6. wbemuuid #Add this library
  7. )


在我的例子中,文本区域的值没有改变,控制台输出如下所示:

  1. GPU: "NVIDIA GeForce RTX 3080 Ti"
  2. qrc:/qt/qml/content/App.qml:19: TypeError: Value is undefined and could not be converted to an object

sxissh06

sxissh061#

你的问题与你正在阅读的C++对象无关。这是你试图向其分配文本的TextArea对象的问题。你不能访问对象的子对象的ID。相反,你需要通过属性公开对象。在我上面的评论中,我应该建议使用属性别名而不仅仅是属性,但它们在这种情况下都应该工作。
MainScreen.qml

  1. Rectangle {
  2. // Expose objects that are accessed from outside
  3. property alias gpuInfoField: gpuInfoField
  4. property alias diskInfoField: diskInfoField
  5. TextArea {
  6. id: gpuInfoField
  7. }
  8. TextArea {
  9. id: diskInfoField
  10. }
  11. }

字符串
App.qml

  1. Window {
  2. MainScreen {
  3. id: mainScreen
  4. }
  5. Component.onCompleted: {
  6. mainScreen.gpuInfoField.text = sysInfo.getGpuInfo();
  7. mainScreen.diskInfoField.text = sysInfo.getDiskInfo();
  8. }
  9. }


也就是说,你通常不需要访问整个对象。通常最好是简单地将需要的子对象的属性暴露在类之外:
MainScreen.qml

  1. Rectangle {
  2. // Just expose the text properties, not the whole object
  3. property alias gpuText: gpuInfoField.text
  4. property alias diskText: diskInfoField.text
  5. TextArea {
  6. id: gpuInfoField
  7. }
  8. TextArea {
  9. id: diskInfoField
  10. }
  11. }


App.qml

  1. Window {
  2. MainScreen {
  3. id: mainScreen
  4. }
  5. Component.onCompleted: {
  6. mainScreen.gpuText = sysInfo.getGpuInfo();
  7. mainScreen.diskText = sysInfo.getDiskInfo();
  8. }
  9. }

展开查看全部

相关问题