so_4: Диспетчер для GUI-приложений

Внимание:
Излагаемый ниже материал относится к MS Windows. Остальные оконные системы пока в данном диспетчере не поддерживаются.

Назначение

При создании GUI-приложений с использованием SObjectizer возникает необходимость в передаче информации между объектами-окнами и агентами. В идеале такая передача должна осуществляться посредством обычных сообщений в рамках SObjectizer. Т.е. чтобы объекты-окна были агентами.

Сделать это средствами обычных диспетчеров SObjectizer не представляется возможным, т.к. обработчики событий агентов вызываются на контекстах рабочих нитей диспетчера. А методы объекта-окна желательно вызывать на контексте главной нити приложения (либо нити, на которой осуществляется выборка и диспетчеризация оконных сообщений).

Диспетчер so_4::disp::win_ui предназначен для того, чтобы объекты-окна можно было делать агентами. Вызов обработчиков событий этих агентов будет осуществляться на контексте главной нити.

Способ работы

Все агенты делятся на два типа: агенты главной нити и обычные агенты. Агент считается агентом главной нити, если для него установлено свойство (trais) возвращаемое функцией so_4::disp::win_ui::query_main_thread_traits().

Диспетчер so_4::disp::win_ui нуждается в дополнительном диспетчере, который будет использоваться для диспетчеризации событий обычных агентов, а так же обслуживанием отложенных и переодических сообщений.

Когда создается объект диспетчера so_4::disp::win_ui автоматически создается невидимое окно и отдельная очередь заявок для диспетчеризации событий.

При диспетчерезации события диспетчер so_4::disp::win_ui определяет, является ли агент-владелец события агентом главной нити. Если нет, то событие диспетчеризируется дополнительным диспетчером. Если же событие относится к агенту главной нити, то событие помещается в отдельную очередь заявок, а невидимому окну отсылается специальное оконное сообщение. Получив такое сообщение функция невидимого окна проходит по отдельной очереди заявок и выполняет все заявки, которые в ней оказались.

Особенности

Объект диспетчер so_4::disp::win_ui должен быть в программе один.

Невидимое окно создается при создании объекта диспетчера. Поэтому важно, чтобы вызов so_4::disp::win_ui::create_disp осуществлялся на контексте главной нити приложения (либо на контексте той нити, на которой осуществляется выборка и диспетчерезация оконных сообщений). Но вызов so_4::api::start() нужно осуществлять на контексте любой другой нити, т.к. возврат из so_4::api::start() осуществляется только при завершении работы run-time SObjectizer. Поэтому, если вызвать so_4::api::start() на контексте главной нити, то приложение остановится в месте вызова so_4::api::start().

Диспетчер не является частью so_4 DLL

Диспетчер so_4::disp::win_ui располагается в отдельной DLL библиотеке. Для линковки к этой библиотеке в проекте нужно указать:

libs += "lib/so_disp_win_ui_4" + so_version;

Заметки:
В текущей версии SObjectizer эта библиотека доступна только под MS Windows.

Пример создания диспетчера

Пример создания диспетчера so_4::disp::win_ui в диалоговом MFC-приложении (базовый код которого сгенерировал MFC Wisard из Visual Studio 6.0):

// mfc_client.cpp : Defines the class behaviors for the application.
//

#include <sample/distribute/mfc_client/stdafx.h>
#include "mfc_client.h"
#include "mfc_clientDlg.h"

#include <memory>

#include <so_4/rt/h/rt.hpp>
#include <so_4/api/h/api.hpp>
#include <so_4/mutex/h/mutex.hpp>

//
// В качестве обычного диспетчера будет использоваться
// диспетчер с активными объектами. В качестве нити
// таймера -- простейшая нить таймера.
//

#include <so_4/timer_thread/simple/h/pub.hpp>
#include <so_4/disp/active_obj/h/pub.hpp>
#include <so_4/disp/win_ui/h/pub.hpp>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// CMfc_clientApp

BEGIN_MESSAGE_MAP(CMfc_clientApp, CWinApp)
  //{{AFX_MSG_MAP(CMfc_clientApp)
    // NOTE - the ClassWizard will add and remove mapping macros here.
    //    DO NOT EDIT what you see in these blocks of generated code!
  //}}AFX_MSG
  ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()

// CMfc_clientApp construction

CMfc_clientApp::CMfc_clientApp()
{
  // TODO: add construction code here,
  // Place all significant initialization in InitInstance
}

// The one and only CMfc_clientApp object

CMfc_clientApp theApp;

//
// Нить, на которой будет происходить запуск SObjectizer-а
//
// Конструктор этого объекта создает диспетчеры. Поэтому
// объект sobj_thread_t должен создаваться на контексте
// главной нити.
//
class sobj_thread_t :
  public so_4::mutex::thread_t
{
  public :
    sobj_thread_t();
    virtual ~sobj_thread_t();

  protected :
    std::auto_ptr< so_4::timer_thread::timer_thread_t >
      m_timer_ptr;

    std::auto_ptr< so_4::rt::dispatcher_t >
      m_disp_ptr;

    std::auto_ptr< so_4::rt::dispatcher_t >
      m_main_disp_ptr;

    virtual void
    body();
};

sobj_thread_t::sobj_thread_t()
  :
    // Создания нити таймера.
    m_timer_ptr( so_4::timer_thread::simple::create_timer_thread() ),
    // Создание дополнительного диспетчера, который будет
    // использовать нить таймера.
    m_disp_ptr( so_4::disp::active_obj::create_disp( *m_timer_ptr ) ),
    // Создание диспетчера главной нити. Он использует
    // дополнительный диспетчер для работы с обычными агентами.
    m_main_disp_ptr( so_4::disp::win_ui::create_disp( *m_disp_ptr ) )
{
}

sobj_thread_t::~sobj_thread_t()
{
}

void
sobj_thread_t::body()
{
  // Этот метод вызывается на контексте новой нити.
  // Можно запустить диспетчер главной нити.
  so_4::ret_code_t rc = so_4::api::start( *m_main_disp_ptr, 0 );
  if( rc ) {
    std::cerr << "start: " << rc << std::endl;
  }
}


// CMfc_clientApp initialization

BOOL CMfc_clientApp::InitInstance()
{

  // Приложение стартовало. Метод InitInstance вызывается
  // на контексте главной нити. Самое время создать диспетчер
  // и запустить SObjectizer.
  sobj_thread_t sobj_thread;
  sobj_thread.start();

  CMfc_clientDlg dlg;
  m_pMainWnd = &dlg;
  int nResponse = dlg.DoModal();
  if (nResponse == IDOK)
  {
    // TODO: Place code here to handle when the dialog is
    //  dismissed with OK
  }
  else if (nResponse == IDCANCEL)
  {
    // TODO: Place code here to handle when the dialog is
    //  dismissed with Cancel
  }

  // Завершаем работу SObjectizer...
  so_4::api::shutdown();
  // ... и ждем, пока завершится вспомогательная нить.
  sobj_thread.wait();

  // Since the dialog has been closed, return FALSE so that we exit the
  //  application, rather than start the application's message pump.
  return FALSE;
}

Документация по SObjectizer. Последние изменения: Thu Jan 12 10:52:50 2006. Создано системой  doxygen 1.4.6-NO
Hosted by uCoz