Visual c++ 并行模式库(PPL)中的COM STA模型?
我有一个MFC应用程序,它使用并行模式库来执行一些异步任务。其中一些使用COM对象,因此我需要在此类任务中初始化COM库。在所有这些情况下,我都使用COM STA模型初始化,因为主线程是MFC app(MFC app线程只能是STA),我不知道在哪个上下文中调用我的任务 例如:Visual c++ 并行模式库(PPL)中的COM STA模型?,visual-c++,visual-studio-2012,mfc,windows-8.1,ppl,Visual C++,Visual Studio 2012,Mfc,Windows 8.1,Ppl,我有一个MFC应用程序,它使用并行模式库来执行一些异步任务。其中一些使用COM对象,因此我需要在此类任务中初始化COM库。在所有这些情况下,我都使用COM STA模型初始化,因为主线程是MFC app(MFC app线程只能是STA),我不知道在哪个上下文中调用我的任务 例如: BOOL CMyApp::InitInstance() { // base initialization CWinAppEx::InitInstance(); AfxOleInit(
BOOL CMyApp::InitInstance() {
// base initialization
CWinAppEx::InitInstance();
AfxOleInit();
// ... some code ...
// PPL usage
{
Concurrency::task_group aTasks;
// Task1
aTasks.run([&](){
HRESULT hRes = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hRes)) {
Sleep(100);
::CoUninitialize();
}
});
// Task2
aTasks.run([&](){
HRESULT hRes = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hRes)) {
Sleep(100);
::CoUninitialize();
}
});
// Task3
aTasks.run([&](){
HRESULT hRes = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hRes)) {
Sleep(100);
::CoUninitialize();
}
});
aTasks.wait();
}
}
此代码在Windows 7/XP上正常工作。但是在Windows 8.1上,C++ 2012平台工具集任务1和2不工作,因为CONPROCELIZEXE()返回RPCYEYEXEDEDY模式错误!任务3通常由PPL core在主MFC线程上下文中调用,主MFC线程上下文是OLE,其COM已初始化为Conit_APARTMENTTHREADED,因此CoInitializeEx()返回成功的S_假代码(双重初始化)
对于任务2和任务3,PPL core创建单独的线程,这些线程在Windows 7/XP上未预先初始化为COM,因此任务第一行初始化COM成功。
但在Windows 8.1上所有“外观为”线程都预先初始化为COM,带有Conit_多线程标志,随后的Conitializex(…,Conit_APARTMENTTHREADED)调用返回错误
见鬼!
如何在Windows 8.1上定义正确的COM初始化规则?我的错在哪里?
PPL不能保证任务的线程上下文,它可以是主线程,在MFC中必须是STA。我无法定义何时应该使用MTA或STA COM初始化
请帮帮我。这可能是来自2012 C++平台工具集的PPL核心代码中的错误还是PPL使用Windows 8.1的错误? < P> <强>更新:(新代码提供)
100%正确。VC++CRT在PPL库中初始化WinRT!并在Windows 8及更高版本上执行此操作。现在所有PPL任务都是在多线程模式(MTA/Conit\u多线程模式)下为COM预先初始化的。
因此,如果您想在PPL任务中初始化COM,您应该非常小心。我为COM初始化编写了一个特殊的类,它允许您简化这个任务
namespace Concurrency {
/**
* COM MultiThreading initialization for ConcRT
*/
class com_init
{
protected:
const HRESULT m_hRes;
public:
com_init(bool bInit = true)
: m_hRes(bInit ? (isWinRT() ? ERROR_ALREADY_INITIALIZED : CoInitializeEx(NULL, COINIT_MULTITHREADED)) : ERROR_CANCELLED)
{}
~com_init()
{
if (SUCCEEDED(m_hRes)) {
CoUninitialize();
}
}
inline static bool isWinRT() {
const bool bRes = (::Concurrency::GetOSVersion() == ::Concurrency::IResourceManager::Win8OrLater) && (::Concurrency::CurrentScheduler::GetPolicy().GetPolicyValue(WinRTInitialization) == ::Concurrency::InitializeWinRTAsMTA);
return bRes;
}
};
}
所以previos代码应该是这样的
BOOL CMyApp::InitInstance() {
// base initialization
CWinAppEx::InitInstance();
AfxOleInit();
// ... some code ...
// PPL usage
{
Concurrency::task_group aTasks;
// Task1
aTasks.run([&](){
const Concurrency::com_init objInitCOM;
// ... to do COM work.
});
// Task2
aTasks.run([&](){
const Concurrency::com_init objInitCOM;
// ... to do COM work.
});
// Task3
aTasks.run([&](){
const Concurrency::com_init objInitCOM;
// ... to do COM work.
});
aTasks.wait();
}}
嗯,硬饼干。您可以在VC\crt\src\InternalContextBase.cpp的InternalContextBase::Dispatch()函数中看到它。注意对WinRT::RoInitialize(RO_INIT_多线程)的调用;这不是意外。STA线程的一个硬要求是它必须泵送一个消息循环。任务不会这样做。在桌面应用程序中初始化WinRT有什么意义?您可以在桌面应用程序中初始化Windows运行时,原因与在UWP应用程序中初始化Windows运行时的原因相同:能够使用作为Windows运行时类型公开的API。@IInspectable,是的,我现在看到了。谢谢