2015-03-13 3 views
0

У меня есть приложение MFC, использующее библиотеку параллельных шаблонов для некоторых асинхронных задач. Некоторые из них используют COM-объекты, поэтому мне нужно инициализировать библиотеку COM в таких задачах. Во всех таких случаях я использую инициализацию модели COM STA, потому что основной поток - приложение MFC (поток приложений MFC может быть только STA), и я не знаю, в каком контексте протектора будут вызваны мои задачи.COM STA модель в Parallel Patterns Library (PPL)?

Некоторые примеры:

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 Platform Toolset задачи 1 и 2 не работают, потому что CoInitializeEx() возвращает ошибку RPC_E_CHANGED_MODE! Задача 3 обычно называется ядром PPL в основном контексте потока MFC, который является OLE, а его COM уже инициализирован как COINIT_APARTMENTTHREADED, поэтому CoInitializeEx() возвращает код успеха S_FALSE (двойная инициализация).

Для задач 2 и 3 ядро ​​PPL создает отдельные потоки, которые не являются PRE-инициализированными как COM в Windows 7/XP, поэтому задачи firts line инициализируют COM с успехом. BUT on Windows 8.1 все выглядят как потоки PRE-INITIALIZED, поскольку COM с флагом COINIT_MULTITHREADED и последующим вызовом CoInitializeEx (..., COINIT_APARTMENTTHREADED) возвращает ERROR!

Какого черта! Как я могу определить правильное правило инициализации COM в окне 8.1? Где моя ошибка? PPL не гарантируется моим контекстом потока для задач, и это может быть основной поток, который в MFC ДОЛЖЕН быть STA. И я не могу определить, когда я должен использовать инициализацию MTA или STA COM.

Пожалуйста, помогите мне. Может быть, это ошибка в коде ядра PPL из набора инструментов платформы C++ 2012 года или ошибки в использовании PPL с Windows 8.1?

+1

Мда, жесткие печенье. Вы можете увидеть его в функции VC \ crt \ src \ InternalContextBase.cpp, InternalContextBase :: Dispatch(). Обратите внимание на вызов WinRT :: RoInitialize (RO_INIT_MULTITHREADED); Не случайно. Требование к потоку STA для жестких требований заключается в том, что он должен перекачать контур сообщения. Задачи этого не делают. –

+0

Какой смысл инициализировать WinRT в настольных приложениях? – 23W

ответ

0

ОБНОВЛЕНО: (новый код предлагаются)

Hans Passant прав на 100%. VC++ CRT инициализирует WinRT в PPL lib! И сделать это в Windows 8 и более поздних версиях. И теперь все задачи PPL предварительно инициализируются для COM в режиме многопоточности (режим MTA/COINIT_MULTITHREADED). Итак, если вы хотите инициализировать COM в своих задачах PPL, вы должны быть очень осторожны. Я написал специальный класс для инициализации 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(); 
}}