Добро пожаловать! Это — архивная версия форумов на «Хакер.Ru». Она работает в режиме read-only.
 

MFC. Сообщения. Взаимодействие классов в потоке.

Пользователи, просматривающие топик: none

Зашли как: Guest
Все форумы >> [Компилируемые языки] >> MFC. Сообщения. Взаимодействие классов в потоке.
Имя
Сообщение << Старые топики   Новые топики >>
MFC. Сообщения. Взаимодействие классов в потоке. - 2012-02-18 04:26:52.856666   
Genco

Сообщений: 1662
Оценки: 90
Присоединился: 2007-12-16 23:11:22.003333
Вопрос простой и позорный, но мозг зашел в штопор и замылился. Врядли, но вдруг кто ответит.

Возникла у меня не очень понятная проблема по основам, ответ на которую не находится ну никак.
Что хочу/нужно: создать в программе отдельный от основного поток, там будут работать 2 класса. У одного будет 1 экземпляр, ожидающий сообщения, у другого - куча экземпляров, их отсылающих. Вроде бы ничего сверхъестественного, но…
class CContext: public CWnd { private: //тут всякое... int id_counter; protected: // (AFX_MSG_MAP(CContext) afx_msg int OnRecalc(TMSG *msg); //{(AFX_MSG_MAP) //тут всякое... DECLARE_MESSAGE_MAP() //!!! public: CContext(void): CWnd() { id_counter=0; } //тут всякое... ~CContext(void); }; Это такой вот класс (не важно, что в нем напихано), пришлось наследоваться от CWnd, чтоб слушать сообщения. В нем есть карта сообщений:
BEGIN_MESSAGE_MAP(CContext, CWnd) // (AFX_MSG_MAP(CContext) ON_KR_RECALC(OnRecalc) //ON_REGISTERED_MESSAGE(KR_RECALC,OnSample) //{(AFX_MSG_MAP) END_MESSAGE_MAP() Тоже стандартно. Далее хитрее - класс, который будет что-то слать, передавая инфу через lParam:
#define RECALC_TAG _T("WAKEUP") //Ерунда ниже, кстати, рабочая, взято с rsdn #define ON_KR_RECALC(memberFxn) \ { KR_RECALC, 0, 0, 0, AfxSig_is, (AFX_PMSG)(AFX_PMSGW) \ (int (CWnd::*)(TMSG*))(memberFxn) }, //---------------------------------------------------- typedef struct MyMSG { //бла-бла } TMSG; const int dt=700; class KR_Object { friend class CContext; protected: int ID; CContext *context; void send_event(int dest, UINT span=dt); public: virtual void do_job()=0; //да, я от него наследуюсь по-всякому virtual void wakeup(); //не суть, что это всё void receive_event(TMSG); }; //бла-бла static const UINT NEAR KR_RECALC = ::RegisterWindowMessage(RECALC_TAG); Непосредственно с самой посылкой всё вообще нехорошо, есть куча способов по идее, и ни один не работает((
SendMessage()/PostMessage() - неизвестен дескриптор, не катит
context->SendMessage() - внутри класса дескриптор невалидный, опять облом.
PostThreadMessage() - главная надежда, отсылка всему потоку, шлю, узнавая ID текущего потока и получаю облом.

CContext не реагирует, я замучался. Получается, что создавать полноценное окно мне не нужно, а вот обмениваться сообщениями, не вешая интерфейс (т.е. в отдельном потоке, где вся логика) - необходимо.

У кого есть идеи - помогите советом, что-я-делаю-не-так??
Post #: 1
RE: MFC. Сообщения. Взаимодействие классов в потоке. - 2012-02-18 17:00:39.480000   
_SaZ_

Сообщений: 4329
Оценки: 398
Присоединился: 2008-01-30 02:18:05.553333
Тебе ничто не мешает создать своё невидимое окно и кидать через него сообщения. С MFC я не дружу, а вот с WinAPI могу помочь.
Post #: 2
RE: MFC. Сообщения. Взаимодействие классов в потоке. - 2012-02-18 17:08:23.876666   
_SaZ_

Сообщений: 4329
Оценки: 398
Присоединился: 2008-01-30 02:18:05.553333
Вот тут для себя пару классов пишу. Суть следующая: крутить в отдельном потоке окно, которое будет как-то реагировать на внешние сообщения.

Использование:
WDToolWindow::instance(); // Создание окна WDToolWindow::Dispose(); // Освобождение ресурсов WDToolWindow::instance()-&gt;addWatcher( hWnd ); // Отправка сообщения между потоками (создаётся дополнительное окно, если hwnd валиден)
Ну думаю по коду там всё будет понятно.

WDCommon.h
#pragma once #include &lt;Windows.h&gt; #include &lt;tchar.h&gt; #include &lt;string&gt; #include &lt;vector&gt; namespace std { #if defined( UNICODE ) || defined( _UNICODE ) typedef wstring tstring; #else typedef string tstring; #endif }
WDHelpers.h
#pragma once #include "WDCommon.h" class WDHelpers { public: static void RegisterWndClass( HINSTANCE hInstance, LPCTSTR name, WNDPROC wndProc ); static bool isTopmost( HWND hWnd ); static void setTopmost( HWND hWnd, bool topmost ); private: WDHelpers(void); ~WDHelpers(void); }; class WDNonCopyble { public: WDNonCopyble(); ~WDNonCopyble(); private: WDNonCopyble( const WDNonCopyble& src ); WDNonCopyble& operator= ( const WDNonCopyble& src ); };
WDToolWindow.h
#pragma once #include "WDCommon.h" #include "WDHelpers.h" class WDChildInfo; class WDToolWindow : WDNonCopyble { public: ~WDToolWindow(); void addWatcher( HWND hwnd ); HWND hwnd() const; private: WDToolWindow(); void run(); bool addWatcherInternal( HWND hWnd ); void clearWatchers(); HANDLE m_thread; DWORD m_threadID; HWND m_hwnd; // Child windows HWND m_wndTopmost; typedef std::vector&lt; HWND &gt; ChildList; ChildList m_childs; public: static WDToolWindow* instance(); static void Dispose(); private: static DWORD CALLBACK ThreadProc( LPVOID lpParam ); static LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ); static WDChildInfo* hwndToWDChild( HWND hWnd ); static WDToolWindow *m_instance; // WM_USER messages static const int MSG_ADDWATCHER; };
WDChildInfo.h
#pragma once #include "WDCommon.h" #include "WDToolWindow.h" #include "WDHelpers.h" class WDChildInfo : WDNonCopyble { public: WDChildInfo( HWND hwndToWatch, WDToolWindow& parent ); ~WDChildInfo(); void show(); private: void updateInfo(); HWND m_watched; HWND m_hwnd; WDToolWindow& m_parent; std::tstring m_title; // Child windows HWND m_wndTitle; public: private: static void Init(); static LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ); static bool m_isInitialized; static int m_xPos; };
WDChildInfo.cpp
#include "StdAfx.h" #include "WDChildInfo.h" bool WDChildInfo::m_isInitialized = false; int WDChildInfo::m_xPos = 0; namespace { const std::tstring childInfoClass = _T( "WDChildInfoClass" ); const int width = 300; const int height = 100; const int idTitle = 1; } WDChildInfo::WDChildInfo( HWND hwndToWatch, WDToolWindow& parent ) : m_watched( hwndToWatch ) , m_hwnd( NULL ) , m_parent( parent ) , m_wndTitle( NULL ) { if ( !m_isInitialized ) Init(); } WDChildInfo::~WDChildInfo() { if ( IsWindow( m_hwnd ) ) DestroyWindow( m_hwnd ); } void WDChildInfo::show() { HINSTANCE hInstance = (HINSTANCE)GetModuleHandle( NULL ); DWORD exFlags = WS_EX_TOOLWINDOW; if ( WDHelpers::isTopmost( m_parent.hwnd() ) ) exFlags |= WS_EX_TOPMOST; HWND hWnd = CreateWindowEx( exFlags, childInfoClass.c_str(), NULL, WS_OVERLAPPEDWINDOW, m_xPos, 0, width, height, NULL, NULL, hInstance, this ); m_xPos += width; SetWindowLong( hWnd, GWL_USERDATA, (LONG)this ); ShowWindow( hWnd, SW_SHOW ); UpdateWindow( hWnd ); } void WDChildInfo::updateInfo() { int length = GetWindowTextLength( m_watched ) + 1; LPTSTR p = new TCHAR[ length ]; GetWindowText( m_watched, p, length ); m_title = p; delete [] p; } void WDChildInfo::Init() { if ( m_isInitialized ) return ; HINSTANCE hInstance = (HINSTANCE)GetModuleHandle( NULL ); WDHelpers::RegisterWndClass( hInstance, childInfoClass.c_str(), WndProc ); } LRESULT CALLBACK WDChildInfo::WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { WDChildInfo& instance = *reinterpret_cast&lt; WDChildInfo * &gt;( GetWindowLong( hWnd, GWL_USERDATA ) ); switch ( message ) { case WM_CREATE: { LPCREATESTRUCT p = (LPCREATESTRUCT)lParam; WDChildInfo& instance = *reinterpret_cast&lt; WDChildInfo * &gt;( p-&gt;lpCreateParams ); // Override for WM_CREATE instance.m_hwnd = hWnd; instance.m_wndTitle = CreateWindow( _T("EDIT"), NULL, WS_BORDER | WS_CHILD | WS_TABSTOP | WS_VISIBLE | ES_READONLY, 10, 10, 120, 20, hWnd, (HMENU)idTitle, (HINSTANCE)GetModuleHandle( NULL ), NULL ); } return 0; case WM_CLOSE: DestroyWindow( hWnd ); instance.m_hwnd = NULL; return 0; default: return DefWindowProc( hWnd, message, wParam, lParam ); } return 0; }
WDHelpers.cpp
#include "StdAfx.h" #include "WDHelpers.h" WDHelpers::WDHelpers() { } WDHelpers::~WDHelpers() { } void WDHelpers::RegisterWndClass( HINSTANCE hInstance, LPCTSTR name, WNDPROC wndProc ) { WNDCLASSEX wcex; wcex.cbSize = sizeof( WNDCLASSEX ); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = wndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon( NULL, IDI_APPLICATION ); wcex.hCursor = LoadCursor( NULL, IDC_ARROW ); wcex.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1 ); wcex.lpszMenuName = NULL; wcex.lpszClassName = name; wcex.hIconSm = NULL; RegisterClassEx( &wcex ); } bool WDHelpers::isTopmost( HWND hWnd ) { return ( GetWindowLong( hWnd, GWL_EXSTYLE ) & WS_EX_TOPMOST ) == WS_EX_TOPMOST; } void WDHelpers::setTopmost( HWND hWnd, bool topmost ) { SetWindowPos( hWnd, topmost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED ); } WDNonCopyble::WDNonCopyble() {} WDNonCopyble::WDNonCopyble( const WDNonCopyble& src ) {} WDNonCopyble::~WDNonCopyble() {} WDNonCopyble& WDNonCopyble::operator=( const WDNonCopyble& src ) { return *this; }
WDToolWindow.cpp
#include "StdAfx.h" #include "WDToolWindow.h" #include "WDChildInfo.h" namespace { const std::tstring toolWindowClass = _T( "WDToolWindowClass" ); const std::tstring toolWindowTitle = _T( "WD Host" ); const int x = 0; const int y = 400; const int width = 300; const int height = 100; const int idTopmost = 0x0001; } WDToolWindow* WDToolWindow::m_instance = NULL; const int WDToolWindow::MSG_ADDWATCHER = WM_USER + 1; WDToolWindow::WDToolWindow() : m_thread( NULL ) , m_threadID( 0 ) , m_hwnd( NULL ) , m_wndTopmost( NULL ) { int gg = 5; } WDToolWindow::~WDToolWindow() { PostThreadMessage( m_threadID, WM_QUIT, 0, 0 ); WaitForSingleObject( m_thread, INFINITE ); CloseHandle( m_thread ); m_thread = NULL; } void WDToolWindow::addWatcher( HWND hwnd ) { PostMessage( m_hwnd, MSG_ADDWATCHER, 0, (LPARAM)hwnd ); } bool WDToolWindow::addWatcherInternal( HWND hWnd ) { if ( !IsWindow( hWnd ) ) return false; WDChildInfo *childInfo = new WDChildInfo( hWnd, *this ); m_childs.push_back( hWnd ); childInfo-&gt;show(); return true; } void WDToolWindow::clearWatchers() { for ( size_t i = 0; i &lt; m_childs.size(); i++ ) { WDChildInfo* childInfo = hwndToWDChild( m_childs[i] ); delete childInfo; m_childs[i] = NULL; } m_childs.clear(); } WDToolWindow* WDToolWindow::instance() { if ( m_instance == NULL ) { m_instance = new WDToolWindow; m_instance-&gt;run(); } return m_instance; } void WDToolWindow::Dispose() { delete m_instance; m_instance = NULL; } void WDToolWindow::run() { if ( m_thread != NULL ) return ; WDToolWindow *p1 = m_instance; WDToolWindow *p2 = this; m_thread = CreateThread( NULL, 0, ThreadProc, this, 0, &m_threadID ); } DWORD CALLBACK WDToolWindow::ThreadProc( LPVOID lpParam ) { WDToolWindow* instance = reinterpret_cast&lt; WDToolWindow * &gt;( lpParam ); HINSTANCE hInstance = (HINSTANCE)GetModuleHandle( NULL ); WDHelpers::RegisterWndClass( hInstance, toolWindowClass.c_str(), WndProc ); HWND hWnd = CreateWindowEx( WS_EX_TOOLWINDOW | WS_EX_TOPMOST, toolWindowClass.c_str(), toolWindowTitle.c_str(), WS_OVERLAPPEDWINDOW, x, y, width, height, NULL, NULL, hInstance, lpParam ); SetWindowLong( hWnd, GWL_USERDATA, (LONG)lpParam ); ShowWindow( hWnd, SW_SHOW ); UpdateWindow( hWnd ); for ( ; ; ) { MSG msg; BOOL ret = GetMessage( &msg, NULL, 0, 0 ); switch ( ret ) { case -1: case 0: return msg.wParam; default: TranslateMessage( &msg ); DispatchMessage( &msg ); break; } } return 0; } LRESULT CALLBACK WDToolWindow::WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { WDToolWindow& instance = *reinterpret_cast&lt; WDToolWindow * &gt;( GetWindowLong( hWnd, GWL_USERDATA ) ); switch ( message ) { case WM_CREATE: { LPCREATESTRUCT p = (LPCREATESTRUCT)lParam; WDToolWindow& instance = *reinterpret_cast&lt; WDToolWindow * &gt;( p-&gt;lpCreateParams ); // Override for WM_CREATE instance.m_hwnd = hWnd; instance.m_wndTopmost = CreateWindow( _T( "BUTTON" ), _T( "Topmost" ), WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_CHECKBOX | BS_PUSHLIKE, 10, 10, 80, 25, hWnd, (HMENU)idTopmost, (HINSTANCE)GetModuleHandle( NULL ), NULL ); SendMessage( instance.m_wndTopmost, BM_SETCHECK, WDHelpers::isTopmost( hWnd ) ? BST_CHECKED : BST_UNCHECKED, 0 ); EnableWindow( instance.m_wndTopmost, FALSE ); // Topmost function is buggy =( } return 0; case WM_DESTROY: PostQuitMessage( 0 ); return 0; case WM_LBUTTONDOWN: return SendMessage( hWnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, 0 ); case MSG_ADDWATCHER: instance.addWatcherInternal( (HWND)lParam ); return 0; case WM_COMMAND: switch ( LOWORD( wParam ) ) { case idTopmost: { bool isTopMost = WDHelpers::isTopmost( hWnd ); for ( size_t i = 0; i &lt; instance.m_childs.size(); i++ ) WDHelpers::setTopmost( instance.m_childs[i], !isTopMost ); WDHelpers::setTopmost( hWnd, !isTopMost ); SendMessage( instance.m_wndTopmost, BM_SETCHECK, WDHelpers::isTopmost( hWnd ) ? BST_CHECKED : BST_UNCHECKED, 0 ); } return 0; default: break; } return 0; default: return DefWindowProc( hWnd, message, wParam, lParam ); } return 0; } WDChildInfo* WDToolWindow::hwndToWDChild( HWND hWnd ) { return reinterpret_cast&lt; WDChildInfo * &gt;( GetWindowLong( hWnd, GWL_USERDATA ) ); } HWND WDToolWindow::hwnd() const { return m_hwnd; }
Post #: 3
RE: MFC. Сообщения. Взаимодействие классов в потоке. - 2012-02-18 17:11:18.426666   
_SaZ_

Сообщений: 4329
Оценки: 398
Присоединился: 2008-01-30 02:18:05.553333
З.ы. вместо **** вставить L O N G
Post #: 4
RE: MFC. Сообщения. Взаимодействие классов в потоке. - 2012-02-20 18:40:50.636666   
_SaZ_

Сообщений: 4329
Оценки: 398
Присоединился: 2008-01-30 02:18:05.553333
Всё вышеприведенное очень сильно глючит xD,… Я тут вспомнил, гугл по message-only windows
Post #: 5
RE: MFC. Сообщения. Взаимодействие классов в потоке. - 2012-02-21 01:21:57.870000   
Genco

Сообщений: 1662
Оценки: 90
Присоединился: 2007-12-16 23:11:22.003333
Аааа!!! Пасиба огромное! И за код, и за наводку))

Тут, правда, я пришел к тому, что если сделать в рабочем потоке бесконечный цикл с выходом по нужному результату от WaitForSingleObject(), то почти всё, что можно в нем создать дополнительно, помимо голого кода, будет подвисать. Но это исправляется реорганизацией логики работы (можно по закрытию созданного окна поток завершать, например) , "зло гораздо меньшего порядка"))

Гугл ткнул на codeproject, там любопытные наработки на MFC в дополнение к этому. В общем, ещё раз спасибо!
Post #: 6
RE: MFC. Сообщения. Взаимодействие классов в потоке. - 2012-02-21 12:51:30.190000   
_SaZ_

Сообщений: 4329
Оценки: 398
Присоединился: 2008-01-30 02:18:05.553333
GetMessage умеет ждать прихода сообщения. Если уж ты реализуешь распараллеливание через сообщения - то юзай окна, а не объекты синхронизации.
Post #: 7
RE: MFC. Сообщения. Взаимодействие классов в потоке. - 2012-02-23 21:02:05.100000   
Genco

Сообщений: 1662
Оценки: 90
Присоединился: 2007-12-16 23:11:22.003333
С окнами плохо, так то можно полениться и поток зациклить, в стиле:
while (running) { //бла-бла result=::WaitForSingleObject(ThreadEnd.m_hObject,0); //проверка завершения if(result==WAIT_OBJECT_0) running=false; } А вот с окнами - сообщения все будут доходить от лукавого (не знаю точно почему, но сбоит система передачи), приходится наследоваться от CDialog и вызывать его через DoModal() + слать сообщения по хендлу + рисовку видимого окна через синхронизацию оберегать….
..а хотелось схалявить, не вышло))
message-only windows в принципе, проканало
Post #: 8
Страниц:  [1]
Все форумы >> [Компилируемые языки] >> MFC. Сообщения. Взаимодействие классов в потоке.







Связаться:
Вопросы по сайту / xakep@glc.ru

Предупреждение: использование полученных знаний в противозаконных целях преследуется по закону.