我需要幫助解決此問題。我想在我的項目中使用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