并行模式库 (PPL) 中的 COM STA 模型?
COM STA model in Parallel Patterns Library (PPL)?
我的 MFC 应用程序使用并行模式库来执行某些异步任务。其中一些使用 COM 对象,因此我需要在此类任务中初始化 COM 库。在所有这些情况下,我都使用 COM STA 模型初始化,因为主线程是 MFC 应用程序(MFC 应用程序线程只能是 STA)并且我不知道我的任务将在哪个线程上下文中被调用。
一些例子:
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 上运行正常。但是在使用 C++ 2012 平台工具集的 Windows 8.1 上,任务 1 和 2 无法正常工作,因为 CoInitializeEx() returns RPC_E_CHANGED_MODE 错误!任务 3 通常由 PPL 核心在 OLE 主 MFC 线程上下文中调用,他的 COM 已经初始化为 COINIT_APARTMENTTHREADED,因此 CoInitializeEx() returns 成功 S_FALSE 代码(双重初始化)。
对于任务 2 和 3,PPL 核心创建单独的线程,这些线程在 Windows 7/XP 上未预初始化为 COM,因此任务第一行成功初始化 COM。
但在 Windows 8.1 上,所有线程都被预初始化为 COM,带有 COINIT_MULTITHREADED 标志和随后的 CoInitializeEx(..., COINIT_APARTMENTTHREADED) 调用returns错误!
什么鬼!
如何在 Window 8.1 上定义正确的 COM 初始化规则?我的错误在哪里?
PPL 不能保证我的任务线程上下文,它可以是主线程,在 MFC 中必须是 STA。而且我无法定义何时应该使用 MTA 或 STA COM 初始化。
请帮助我。这可能是 2012 C++ 平台工具集的 PPL 核心代码错误或 Windows 8.1 的 PPL 使用错误?
更新:(提供新代码)
Hans Passant 100% 正确。 VC++ CRT在PPL库中初始化WinRT!并在 Windows 8 及更高版本上执行此操作。现在,所有 PPL 任务都在多线程模式(MTA/COINIT_MULTITHREADED 模式)下针对 COM 进行了预初始化。
因此,如果您想在 PPL 任务中初始化 COM,您应该非常小心。我为 COM 初始化编写了特殊的 class,可让您简化此任务。
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;
}
};
}
所以之前的代码应该是
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();
}}
我的 MFC 应用程序使用并行模式库来执行某些异步任务。其中一些使用 COM 对象,因此我需要在此类任务中初始化 COM 库。在所有这些情况下,我都使用 COM STA 模型初始化,因为主线程是 MFC 应用程序(MFC 应用程序线程只能是 STA)并且我不知道我的任务将在哪个线程上下文中被调用。
一些例子:
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 上运行正常。但是在使用 C++ 2012 平台工具集的 Windows 8.1 上,任务 1 和 2 无法正常工作,因为 CoInitializeEx() returns RPC_E_CHANGED_MODE 错误!任务 3 通常由 PPL 核心在 OLE 主 MFC 线程上下文中调用,他的 COM 已经初始化为 COINIT_APARTMENTTHREADED,因此 CoInitializeEx() returns 成功 S_FALSE 代码(双重初始化)。
对于任务 2 和 3,PPL 核心创建单独的线程,这些线程在 Windows 7/XP 上未预初始化为 COM,因此任务第一行成功初始化 COM。 但在 Windows 8.1 上,所有线程都被预初始化为 COM,带有 COINIT_MULTITHREADED 标志和随后的 CoInitializeEx(..., COINIT_APARTMENTTHREADED) 调用returns错误!
什么鬼! 如何在 Window 8.1 上定义正确的 COM 初始化规则?我的错误在哪里? PPL 不能保证我的任务线程上下文,它可以是主线程,在 MFC 中必须是 STA。而且我无法定义何时应该使用 MTA 或 STA COM 初始化。
请帮助我。这可能是 2012 C++ 平台工具集的 PPL 核心代码错误或 Windows 8.1 的 PPL 使用错误?
更新:(提供新代码)
Hans Passant 100% 正确。 VC++ CRT在PPL库中初始化WinRT!并在 Windows 8 及更高版本上执行此操作。现在,所有 PPL 任务都在多线程模式(MTA/COINIT_MULTITHREADED 模式)下针对 COM 进行了预初始化。 因此,如果您想在 PPL 任务中初始化 COM,您应该非常小心。我为 COM 初始化编写了特殊的 class,可让您简化此任务。
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;
}
};
}
所以之前的代码应该是
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();
}}