MFC. Сообщения. Взаимодействие классов в потоке.
Пользователи, просматривающие топик: none
|
Зашли как: Guest
|
Имя |
Сообщение |
<< Старые топики Новые топики >> |
|
|
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 не реагирует, я замучался. Получается, что создавать полноценное окно мне не нужно, а вот обмениваться сообщениями, не вешая интерфейс (т.е. в отдельном потоке, где вся логика) - необходимо. У кого есть идеи - помогите советом, что-я-делаю-не-так??
|
|
|
RE: MFC. Сообщения. Взаимодействие классов в потоке. - 2012-02-18 17:00:39.480000
|
|
|
_SaZ_
Сообщений: 4329
Оценки: 398
Присоединился: 2008-01-30 02:18:05.553333
|
Тебе ничто не мешает создать своё невидимое окно и кидать через него сообщения. С MFC я не дружу, а вот с WinAPI могу помочь.
|
|
|
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()->addWatcher( hWnd ); // Отправка сообщения между потоками (создаётся дополнительное окно, если hwnd валиден)
Ну думаю по коду там всё будет понятно. WDCommon.h
#pragma once
#include <Windows.h>
#include <tchar.h>
#include <string>
#include <vector>
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< HWND > 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< WDChildInfo * >( GetWindowLong( hWnd, GWL_USERDATA ) );
switch ( message )
{
case WM_CREATE:
{
LPCREATESTRUCT p = (LPCREATESTRUCT)lParam;
WDChildInfo& instance = *reinterpret_cast< WDChildInfo * >( p->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->show();
return true;
}
void WDToolWindow::clearWatchers()
{
for ( size_t i = 0; i < 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->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< WDToolWindow * >( 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< WDToolWindow * >( GetWindowLong( hWnd, GWL_USERDATA ) );
switch ( message )
{
case WM_CREATE:
{
LPCREATESTRUCT p = (LPCREATESTRUCT)lParam;
WDToolWindow& instance = *reinterpret_cast< WDToolWindow * >( p->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 < 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< WDChildInfo * >( GetWindowLong( hWnd, GWL_USERDATA ) );
}
HWND WDToolWindow::hwnd() const
{
return m_hwnd;
}
|
|
|
RE: MFC. Сообщения. Взаимодействие классов в потоке. - 2012-02-18 17:11:18.426666
|
|
|
_SaZ_
Сообщений: 4329
Оценки: 398
Присоединился: 2008-01-30 02:18:05.553333
|
З.ы. вместо **** вставить L O N G
|
|
|
RE: MFC. Сообщения. Взаимодействие классов в потоке. - 2012-02-20 18:40:50.636666
|
|
|
_SaZ_
Сообщений: 4329
Оценки: 398
Присоединился: 2008-01-30 02:18:05.553333
|
Всё вышеприведенное очень сильно глючит xD,… Я тут вспомнил, гугл по message-only windows
|
|
|
RE: MFC. Сообщения. Взаимодействие классов в потоке. - 2012-02-21 01:21:57.870000
|
|
|
Genco
Сообщений: 1662
Оценки: 90
Присоединился: 2007-12-16 23:11:22.003333
|
Аааа!!! Пасиба огромное! И за код, и за наводку)) Тут, правда, я пришел к тому, что если сделать в рабочем потоке бесконечный цикл с выходом по нужному результату от WaitForSingleObject(), то почти всё, что можно в нем создать дополнительно, помимо голого кода, будет подвисать. Но это исправляется реорганизацией логики работы (можно по закрытию созданного окна поток завершать, например) , "зло гораздо меньшего порядка")) Гугл ткнул на codeproject, там любопытные наработки на MFC в дополнение к этому. В общем, ещё раз спасибо!
|
|
|
RE: MFC. Сообщения. Взаимодействие классов в потоке. - 2012-02-21 12:51:30.190000
|
|
|
_SaZ_
Сообщений: 4329
Оценки: 398
Присоединился: 2008-01-30 02:18:05.553333
|
GetMessage умеет ждать прихода сообщения. Если уж ты реализуешь распараллеливание через сообщения - то юзай окна, а не объекты синхронизации.
|
|
|
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 в принципе, проканало
|
|
|
|
|