并行模式库(PEOPLE)中的COM STA模型?

时间:2015-03-13 19:28:28

标签: visual-c++ visual-studio-2012 mfc windows-8.1 ppl

我有MFC应用程序,它使用Parallel Patterns Library来执行某些异步任务。其中一些使用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 Platform Toolset的Windows 8.1上,任务1和2无法正常工作,因为CoInitializeEx()会返回RPC_E_CHANGED_MODE错误!任务3通常由主要MFC线程上下文中的PPL核心调用,它是OLE,并且他的COM已初始化为COINIT_APARTMENTTHREADED,因此CoInitializeEx()返回成功S_FALSE代码(双初始化)。

对于任务2和3,PPL核心会在Windows 7 / XP上创建未预先初始化为COM的单独线程,因此任务将成功初始化COM。 但是在Windows 8.1 上,所有内容都看起来是线程被预先初始化为COM,COINIT_MULTITHREADED标志和随后的CoInitializeEx(...,COINIT_APARTMENTTHREADED)调用返回ERROR!

到底是怎么回事! 如何在Window 8.1上定义正确的COM初始化规则?我的错误在哪里? 对于任务,PPL不保证我的线程上下文,它可以是MFC中必须是STA的主线程。我不能定义何时应该使用MTA或STA COM初始化。

请帮帮我。可能这是2012 C ++平台工具集的PPL核心代码中的错误或Windows 8.1的PPL使用错误?

1 个答案:

答案 0 :(得分:0)

更新:(提供新代码)

Hans Passant是正确的100%。 VC ++ CRT在PPL lib中初始化WinRT!并在Windows 8及更高版本上执行此操作。现在,所有PPL任务都​​在多线程模式下为COM预先初始化(MTA / COINIT_MULTITHREADED模式)。 所以,如果你想在你的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;
    }
};
}

所以普遍的代码应该是那个

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();
}}