如何确定未自行声明或调用CoCreate创建的COM对象的线程模型
核心结论
你从IFolderView2::GetSelection拿到的IShellItemArray对象默认支持直接跨线程访问,不需要手动封送,该行为具备普适性,你的测试结果是符合官方实现逻辑的。
相关原理解释
- 从Windows Vista版本开始,
IShellItem、IShellItemArray的系统默认实现内置了自由线程封送器(FTM),本身就是线程安全的自由线程对象,不受创建线程的套间类型限制。 - 你调用
CoGetApartmentType返回APTTYPE_STA,仅代表你执行调用的Explorer主UI线程是单线程套间,和你拿到的COM对象的线程模型没有直接关联,STA线程完全可以返回自由线程对象。
验证方法(可选)
如果你需要自行确认对象的线程模型,可以通过查询IMarshal接口判断是否为自由线程实现:
CComPtr<IMarshal> pMarshal; if (SUCCEEDED(psia->QueryInterface(IID_PPV_ARGS(&pMarshal)))) { CLSID clsidUnmarshal; HRESULT hr = pMarshal->GetUnmarshalClass(IID_IShellItemArray, nullptr, MSHCTX_CROSSCTX, nullptr, MSHLFLAGS_NORMAL, &clsidUnmarshal); if (SUCCEEDED(hr) && IsEqualCLSID(clsidUnmarshal, CLSID_StdFreeThreadedMarshaler)) { // 确认是自由线程对象,可直接跨线程传递指针 } }
实操建议
方案1:直接传递指针(推荐,适配99%场景)
只要确保后台线程提前初始化COM环境即可,建议后台线程用MTA模式初始化:
// 后台线程入口处执行 CoInitializeEx(nullptr, COINIT_MULTITHREADED); // 直接使用主线程传递过来的psia指针即可 // 线程退出前执行 CoUninitialize();
方案2:全局接口表(GIT)传递(最稳妥,兼容所有场景)
如果担心极端场景下的兼容性,可以用系统提供的全局接口表传递指针,不需要提前判断对象线程模型:
- 主线程注册接口到GIT,拿到Cookie传递给后台线程:
CComPtr<IGlobalInterfaceTable> pGIT; pGIT.CoCreateInstance(CLSID_StdGlobalInterfaceTable); DWORD dwGitCookie = 0; pGIT->RegisterInterfaceInGlobal(psia, IID_IShellItemArray, &dwGitCookie); // 将dwGitCookie作为参数传递给后台线程
- 后台线程从GIT获取接口指针:
CComPtr<IGlobalInterfaceTable> pGIT; pGIT.CoCreateInstance(CLSID_StdGlobalInterfaceTable); CComPtr<IShellItemArray> psiaBg; pGIT->GetInterfaceFromGlobal(dwGitCookie, IID_PPV_ARGS(&psiaBg)); // 正常使用psiaBg即可
- 指针使用完毕后,在任意线程调用以下代码释放资源:
pGIT->RevokeInterfaceFromGlobal(dwGitCookie);
内容的提问来源于stack exchange,提问作者user15284017
相关产品推荐
相关产品推荐

