我需要帮助解决此问题。我想在我的项目中使用WMI,以便我可以使用WQL从Windows OS获取一些数据。 这里是我的代码QT QFileDialog创建未知的COM对象和安全
QList<Drive> SystemDrive::getSystemDrive()
{
QList<Drive> list;
#ifdef _WIN64
HRESULT hRes;
hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(hRes == RPC_E_CHANGED_MODE)
{
//qDebug() << "Unable to launch COM: 0x" << QString::number(hRes, 16) << endl;
//OleUninitialize();
CoUninitialize();
hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(FAILED(hRes))
{
qDebug() << "Unable to launch COM: 0x" << QString::number(hRes, 16);
}
//return 1;
}
qDebug() << "CoInitializeEx result 0x" << QString::number(hRes, 16);
if((FAILED(hRes = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0))))
{
qDebug() << "Unable to initialize security: 0x" << QString::number(hRes, 16);
}
IWbemLocator* pLocator = NULL;
if(FAILED(hRes = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pLocator))))
{
qDebug() << "Unable to create a WbemLocator: " << std::hex << hRes << endl;
//return 1;
}
IWbemServices* pService = NULL;
if(FAILED(hRes = pLocator->ConnectServer(L"root\\CIMV2", NULL, NULL, NULL, WBEM_FLAG_CONNECT_USE_MAX_WAIT, NULL, NULL, &pService)))
{
pLocator->Release();
qDebug() << "Unable to connect to \"CIMV2\": " << std::hex << hRes << endl;
//return 1;
}
IEnumWbemClassObject* pEnumerator = NULL;
if(FAILED(hRes = pService->ExecQuery(L"WQL", L"SELECT * FROM Win32_LogicalDisk", WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumerator)))
{
pLocator->Release();
pService->Release();
qDebug() << "Unable to retrive Logical Disk: " << QString::number(hRes, 16) << endl;
//return 1;
}
IWbemClassObject* clsObj = NULL;
int numElems;
while((hRes = pEnumerator->Next(WBEM_INFINITE, 1, &clsObj, (ULONG*)&numElems)) != WBEM_S_FALSE)
{
if(FAILED(hRes))
break;
Drive *tmpDrive;
VARIANT vRet;
VariantInit(&vRet);
if(SUCCEEDED(clsObj->Get(L"Caption", 0, &vRet, NULL, NULL)) && vRet.vt == VT_BSTR)
{
tmpDrive = new Drive(QString((QChar*) vRet.bstrVal, SysStringLen(vRet.bstrVal)));
tmpDrive->setType(Drive::DRIVE_TYPE::DRIVE_LOGICAL);
VariantClear(&vRet);
}
VariantInit(&vRet);
if(SUCCEEDED(clsObj->Get(L"DeviceID", 0, &vRet, NULL, NULL)) && vRet.vt == VT_BSTR)
{
QString id = "\\\\.\\";
id.append(QString((QChar*) vRet.bstrVal, SysStringLen(vRet.bstrVal)));
tmpDrive->setId(id);
VariantClear(&vRet);
}
clsObj->Release();
list.append(*tmpDrive);
}
pEnumerator->Release();
pService->Release();
pLocator->Release();
CoUninitialize();
#endif
return list;
}
代码运行完美我每次调用它。我可以得到我需要的(逻辑驱动器列表)。问题是当我调用QFileDialog
对象然后调用该函数时,该代码将无法工作。我得到的错误为coInitializeSecurity
,错误号为RPC_E_TOO_LATE
,结果我的ExecQuery
将失败,错误代码为0x80041003
(WBEM_E_ACCESS_DENIED)。
我认为QFileDialog
创建COM对象,因为当我呼叫QFileDialog
对象时,有Aplication Output Tab
(标准输出)中的消息输出。
CoCreateInstance failed()
CoCreateInstance failed()
如果我那个函数之前调用QFileDialog
对象首先,将不会出现这些消息,该功能仍然无法正常工作。我需要该函数正常运行,而不需要任何COM对象的中断。
我的问题:
还有什么办法来检测一下COM对象,通过QT产生的?
我怎么知道QT初始化了哪些安全(CoInitializeSecurity)?
编辑 从Hans和Daniel读答案后,我改变我的代码。所以我第一次把CoInitializeEx
与COINIT_APARTMENTTHREADED
一起运行,之后我初始化了CoInitializeSecurity
,然后在该函数中清除了所有CoInitializeEx
,CoInitializeSecurity
和'CoUninitialize'。它的工作原理,我打开QFileDialog
后可以调用该功能。
现在,
- 我应该在哪里称之为 'CoUninitialize'?我应该在应用程序关闭之前调用它吗?
- 如何确保我创建的所有COM接口(
WMI
和QFileDialog
)都能正确销毁?假设我释放WMI中的所有接口和枚举,就像在我的代码中那样,并且我在堆栈内存中调用它。
'QFileDialog'用于使用系统(Windows)文件对话框。可能这个本机对话框有COM组件。为了实验的缘故,尝试使用'QFileDialog :: DontUseNativeDialog'选项来查看它是否仍然失败。 – vahancho
这会导致问题出现,CoInitializeEx()调用从根本上说是错误的。是的,它严重地搞砸了QFileDialog,因为它总是需要COINIT_APARTMENTTHREADED。或者换句话说,它总是需要在UI线程中使用。只有初始化线程的代码才能确定CoInitializeEx的正确值。公寓为任何线程泵送消息循环,通常只有应用程序的主线程。多线程的任何工作线程。在工作线程上显示外壳对话框是不可能的。 –
@vahancho谢谢你的回复。我之前尝试过,但仍然失败。 – serius777