【问题标题】:QT QFileDialog create unknown COM Object and securityQT QFileDialog 创建未知的 COM 对象和安全性
【发布时间】:2017-10-05 08:28:02
【问题描述】:

我需要有关此问题的帮助。我想在我的项目中使用 WMI,这样我就可以使用 WQL 从 Windows 操作系统中获取一些数据。 这是我的代码

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 (stdout) 中有消息输出。

CoCreateInstance failed ()
CoCreateInstance failed ()

如果我在该函数之前先调用QFileDialog 对象,则不会出现这些消息,并且该函数仍然不起作用。我需要该函数正常运行,不受任何 COM 对象的干扰。

我的问题:

  1. 有什么方法可以检测出 QT 创建的 COM 对象是什么?

  2. 我如何知道 QT 初始化了哪个安全性 (CoInitializeSecurity)?

已编辑 在阅读了HansDaniel 的答案后,我更改了我的代码。所以我在第一次使用COINIT_APARTMENTTHREADED 运行我的应用程序时将CoInitializeEx 放入,然后我初始化CoInitializeSecurity,然后在该函数中删除所有CoInitializeExCoInitializeSecurity 和'CoUninitialize'。它有效,我可以在打开QFileDialog后调用该函数。

现在,

  1. 我应该在哪里调用“CoUninitialize”?我应该在我的应用关闭之前调用它吗?
  2. 如何确保我创建的所有 COM 接口(@98​​7654340@ 和 QFileDialog)正确销毁?假设我像在我的代码中一样从 WMI 释放所有接口和枚举,对于QFileDialog,我在堆栈内存中调用它。

【问题讨论】:

  • QFileDialog 用于使用系统的 (Windows) 文件对话框。可能这个本机对话框有 COM 组件。为了实验,请尝试使用QFileDialog::DontUseNativeDialog 选项来查看它是否仍然失败。
  • 这很早就出轨了,CoInitializeEx() 调用根本上是错误的。是的,它把 QFileDialog 搞砸了,因为它总是需要 COINIT_APARTMENTTHREADED。或者换句话说,它总是需要在 UI 线程上使用。只有初始化线程的代码才能确定 CoInitializeEx 的正确值。用于泵送消息循环的任何线程的单元,通常仅适用于应用程序的主线程。任何工作线程的多线程。无法在工作线程上显示 shell 对话框。
  • @vahancho 谢谢你的回复。我以前试过,还是失败了。
  • CoUninitialize 在离开最外层函数之前调用。对于所有对象,请记住在不再需要时将其销毁。为自动管理 (RAII) 编写简单的包装类是个好主意。
  • @Daniel 谢谢。它完成了。我在我的应用关闭之前输入了CoUninitialize

标签: c++ windows qt wmi


【解决方案1】:

CoInitializeSecurity 应该只为一个进程调用一次RPC_E_TOO_LATE 表示它已经被调用。更早地调用调用,可能进入进程的初始化部分。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-19
    • 1970-01-01
    • 2013-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-16
    • 2016-08-17
    相关资源
    最近更新 更多