Как взять адрес метода класса?
Такая вот ситуация.
Создал класс BUTTONS, в нем описал различные данные, и методы также имеется метод
CreateControl и WndProc
[COLOR=darkblue]
typedef class _СBUTTONS: public _base_ctrl{
...
protected:
...
public:
...
int CreateControl( int id );
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
...
} CBUTTON, *lpCBUTTON;
[/COLOR]
в функции CreateControl делаю так
[COLOR=darkblue]
int _BUTTONS::CreateControl( int id )
{
ID = id;
hWnd_control = CreateWindowEx( 0L,
"Button",
"",
WS_VISIBLE | WS_CHILD | WS_BORDER,
pos.x,
pos.y,
size.cx,
size.cy,
hWnd_window,
(HMENU) ID,
hInst,
NULL );
SetWindowLong( hWnd_control, GWL_WNDPROC, (LONG)WndProc);
return 0;
};
[/COLOR]
так вот при компилировании выходит ошибка на строке SetWindowLong( hWnd_control, GWL_WNDPROC, (LONG)WndProc);
а вот ошибка:
[COLOR=red]'type cast' : cannot convert from 'long (__stdcall _button::*)(struct HWND__ *,unsigned int,unsigned int,long)' to 'long'
Conversion is a valid standard conversion, which can be performed implicitly or by use of static_cast, C-style cast or function-style cast
Error executing cl.exe.[/COLOR]
Че делать, как получить и правильно передать новую процедуру для обработки событий кнопки, хотелось бы инкапсулировать обработчик событий, для каждой кнопки свой, а не выносить его на ружу и там уже делать проверки всякие, кому сообщение и че тогда с ним делать, кстати так работает если вынисти функцию WndProc за пределы класса, но это както не через то место.
Кто знает как лучше поступить сообщите плз.
...
protected:
...
public:
...
int CreateControl( int id );
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
...
} CBUTTON, *lpCBUTTON;
Че делать, как получить и правильно передать новую процедуру для обработки событий кнопки, хотелось бы инкапсулировать обработчик событий, для каждой кнопки свой, а не выносить его на ружу и там уже делать проверки всякие, кому сообщение и че тогда с ним делать, кстати так работает если вынисти функцию WndProc за пределы класса, но это както не через то место.
Кто знает как лучше поступить сообщите плз.
Зачем так усложнять внешний вид описания класса?
Неужели такой вид кода удобен?
Кстати, слова с подчеркиванием зарезервированы для целей компилятора и стандартных библиотек.
Буду писать по-своему.
{
...
public:
static LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
...
};
typedef СBUTTON* LPCBUTTON;
Т.о. можно установить адрес функции обработки сообщений, но при этом не получится обращаться к нестатическим членам класса без некоторых махинаций, а иначе нельзя. Нужно объяснять почему?
Первый вариант махинаций:
Помещаем адрес объекта в аттрибуты окна с помощью
SetWindowLong c GWL_USERDATA.
Потом достаем его в оконной процедуре и применяем.
{
...
SetWindowLong( hWnd_control, GWL_USERDATA, (LONG)this);
SetWindowLong( hWnd_control, GWL_WNDPROC, (LONG)WndProc);
...
};
LRESULT CALLBACK CBUTTON::WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
LPCBUTTON pThis = (LPCBUTTON)GetWindowLong(hwnd, GWL_USERDATA);
// Далее все обращения к нестатическим членам только через pThis
pThis->val;
pThis->func();
...
}
Второй вариант махинаций:
Использование переходников (stub). Применяется в WTL. Об этом подробно можно прочитать здесь:
http://rsdn.ru/article/wtl/wtl-1.xml#XSLTSECTION129149120120
В принципе второй пособ это развитие первого.
В общем виде можно сделать так:
{
public:
static LRESULT CALLBACK WndProcStub(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
LRESULT WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
};
typedef СBUTTON* LPCBUTTON;
int CBUTTON::CreateControl( int id )
{
...
SetWindowLong( hWnd_control, GWL_USERDATA, (LONG)this);
SetWindowLong( hWnd_control, GWL_WNDPROC, (LONG)WndProcStub);
};
LRESULT CALLBACK CBUTTON::WndProcStub(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
LPCBUTTON pThis = (LPCBUTTON)GetWindowLong(hwnd, GWL_USERDATA);
return pThis->WndProc(hwnd, msg, wParam, lParam);
}
Зачем так усложнять внешний вид описания класса?
Неужели такой вид кода удобен?
Кстати, слова с подчеркиванием зарезервированы для целей компилятора и стандартных библиотек.
Буду писать по-своему.
{
...
public:
static LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
...
};
typedef СBUTTON* LPCBUTTON;
Т.о. можно установить адрес функции обработки сообщений, но при этом не получится обращаться к нестатическим членам класса без некоторых махинаций, а иначе нельзя. Нужно объяснять почему?
Первый вариант махинаций:
Помещаем адрес объекта в аттрибуты окна с помощью
SetWindowLong c GWL_USERDATA.
Потом достаем его в оконной процедуре и применяем.
{
...
SetWindowLong( hWnd_control, GWL_USERDATA, (LONG)this);
SetWindowLong( hWnd_control, GWL_WNDPROC, (LONG)WndProc);
...
};
LRESULT CALLBACK CBUTTON::WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
LPCBUTTON pThis = (LPCBUTTON)GetWindowLong(hwnd, GWL_USERDATA);
// Далее все обращения к нестатическим членам только через pThis
pThis->val;
pThis->func();
...
}
Второй вариант махинаций:
Использование переходников (stub). Применяется в WTL. Об этом подробно можно прочитать здесь:
http://rsdn.ru/article/wtl/wtl-1.xml#XSLTSECTION129149120120
В принципе второй пособ это развитие первого.
В общем виде можно сделать так:
{
public:
static LRESULT CALLBACK WndProcStub(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
LRESULT WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
};
typedef СBUTTON* LPCBUTTON;
int CBUTTON::CreateControl( int id )
{
...
SetWindowLong( hWnd_control, GWL_USERDATA, (LONG)this);
SetWindowLong( hWnd_control, GWL_WNDPROC, (LONG)WndProcStub);
};
LRESULT CALLBACK CBUTTON::WndProcStub(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
LPCBUTTON pThis = (LPCBUTTON)GetWindowLong(hwnd, GWL_USERDATA);
return pThis->WndProc(hwnd, msg, wParam, lParam);
}
Только ентот код надо портировать под win64.
Я считаю назначение GWL_USERDATA нужным в обработчике WM_CREATE, А передаю указатель в параметре lParam (там есть такая возможность). Это позволяет обойти ту проблему, что вызовешь ты например CreateWindow, надо обработать в классе WM_CREATE, а указатель еще не назначен, он назначается после.
Т.о. можно установить адрес функции обработки сообщений, [COLOR=darkred]но при этом не получится обращаться к нестатическим членам класса без некоторых махинаций, а иначе нельзя. Нужно объяснять почему?[/COLOR]
Если можно в кратце об этом, или укажи на инфу где об этом можно прочесть.
Если можно в кратце об этом, или укажи на инфу где об этом можно прочесть.
Прочитать об этом можно в стандарте:
http://www.csci.csusb.edu/dick/c++std/cd2/class.html#class.static
http://www.csci.csusb.edu/dick/c++std/cd2/class.html#class.static.mfct
Кратко говоря, из статических методов-членов нельзя обращаться к нестатическим членам (как методам, так и переменным). Это происходит потому, что статические методы по определению не содержат указатель this, который передается в нестатические методы неявно (обычно черех регистр ecx). Т.о. в статическом методе не ясно с каким экземпляром класса идет работа.
Передать нестатический метод в качестве оконной процедуры нельзя из-за несоответствия типов, а если проще, то потому, что в оконную процедуру передаются ОС четыре параметра (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam), а про необходимый пятый неявный параметр this ОС ничего не знает. Поэтому сделать оконной процедурой можно только статический метод, который не содержит this, но это накладывает упомянутые выше ограничения.
Только ентот код надо портировать под win64.
Я считаю назначение GWL_USERDATA нужным в обработчике WM_CREATE, А передаю указатель в параметре lParam (там есть такая возможность). Это позволяет обойти ту проблему, что вызовешь ты например CreateWindow, надо обработать в классе WM_CREATE, а указатель еще не назначен, он назначается после.
Извини, не понял, что ты хотел сказать этим постом.
Только ентот код надо портировать под win64.
Я считаю назначение GWL_USERDATA нужным в обработчике WM_CREATE, А передаю указатель в параметре lParam (там есть такая возможность). Это позволяет обойти ту проблему, что вызовешь ты например CreateWindow, надо обработать в классе WM_CREATE, а указатель еще не назначен, он назначается после.
Кстати я тоже не совсем понял что ты имел ввиду, у меня все сработало теперь одна оконная процедура для нескольких экземплятов кнопки, спасибо Green'у.
Теперь столкнулся с еще одной непоняткой, целый день потратил на выявление ошибки, так и ничего не нашел, обращаюсь за помощью.
начинаю заново но теперь с добавлением
1) есть функция создания кнопки
[COLOR=darkblue]
int base_CBUTTON::CreateControl( int id )
{
ID = id;
hWnd_control = CreateWindowEx( 0L,
"Button",
lpszText,
WS_VISIBLE | WS_CHILD | WS_BORDER,
pos.x,
pos.y,
size.cx,
size.cy,
hWnd_window,
(HMENU) ID,
hInst,
NULL );
SetWindowLong( hWnd_control, GWL_USERDATA, (LONG)this);
OldWinProc = (WNDPROC)SetWindowLong(hWnd_control, GWL_WNDPROC, (LONG)WndProc);
return 0;
};[/COLOR]
2) есть оконная функция для кнопки
[COLOR=darkblue]
LRESULT CALLBACK base_CBUTTON::WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
lpCBUTTON pThis = (lpCBUTTON)GetWindowLong(hwnd, GWL_USERDATA);
// Далее все обращения к нестатическим членам только через pThis
switch ( msg )
{
[COLOR=green]/*case WM_PAINT:
break;*/[/COLOR]
default :
pThis->OldWinProc(hwnd,msg,wParam,lParam);
}
return 0;
};[/COLOR]
На окне создал 3 кнопки. Заметь я ничего не рисую, а просто вызываю родную оконную процедуру, все очень хороше работает, как положено 3 кнопки, а если разкоментировать[COLOR=darkblue]
case WM_PAINT:
break;[/COLOR]
т.е. типа сам буду кнопку рисовать (хотя пока и ничего не рисую). Компилю, запускаю программу. Она работает, пока вроде все нормально. Потом запускаю тузлу Spy++ и смотрю какие события происзодят для кнопок, на 1 и 2 все нормально, события происходят тогда когда надо и какие надо, а вот с 3-ей, последней создаваемой, проблема. Для нее постоянно с бешенной скоростью летят события WM_PAINT, че бы я ни делал так и не нашел в чем тут дело :(
Она работает, пока вроде все нормально. Потом запускаю тузлу Spy++ и смотрю какие события происзодят для кнопок, на 1 и 2 все нормально, события происходят тогда когда надо и какие надо, а вот с 3-ей, последней создаваемой, проблема. Для нее постоянно с бешенной скоростью летят события WM_PAINT, че бы я ни делал так и не нашел в чем тут дело :(
Сложно сказать.
Возможно, из-за того, что постоянно возвращается 0. Наверное, лучше сделать так:
return pThis->OldWinProc(hwnd,msg,wParam,lParam);
Одним словом, надо смотреть код.
Сложно сказать.
Возможно, из-за того, что постоянно возвращается 0. Наверное, лучше сделать так:
return pThis->OldWinProc(hwnd,msg,wParam,lParam);
Одним словом, надо смотреть код.
Вот и я сижу парюсь. Ты прав, я тоже так пробовал, если сделать:
[COLOR=darkblue]LRESULT CALLBACK base_CBUTTON::WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
lpCBUTTON pThis = (lpCBUTTON)GetWindowLong(hwnd, GWL_USERDATA);
// Далее все обращения к нестатическим членам только через pThis
switch ( msg )
{
case WM_PAINT:
pThis->Paint();
break;
}
return pThis->OldWinProc(hwnd,msg,wParam,lParam);
};[/COLOR]
То все работает, но также работает и то что мне не нужно, т.е. кнопка прорисовывается моим методом [COLOR=darkblue]pThis->Paint();[/COLOR], а потом обязательно прорисовывается методом по умолчанию, при вызове [COLOR=darkblue]pThis->OldWinProc(hwnd,msg,wParam,lParam);[/COLOR]. За кнопкой проглядывается по кроям то что я нарисовал, все рисуется как надо вот только потом на мое художество стандартная кнопка накладывается :(
короче, я исходник приложил ;)
З.Ы.
Попрвлюсь, я сказал что видно за кнопкой как я рисую, в приложеном файле не видно, просто я раньше рисовал в координатах [0;0] относительно конопульки, а теперь рисую посередке, и она какраз за накладываемой поверху кнопкой.
Только ентот код надо портировать под win64.
Я считаю назначение GWL_USERDATA нужным в обработчике WM_CREATE, А передаю указатель в параметре lParam (там есть такая возможность). Это позволяет обойти ту проблему, что вызовешь ты например CreateWindow, надо обработать в классе WM_CREATE, а указатель еще не назначен, он назначается после.
Что непонятно? что WM_CREATE посылается во время вызова CreateWindow и если в WndProc на обработчике WM_CREATE получить указатель на класс с помощью GetWindowLong, то он будет NULL и затем будет access violation при попытке вызова метода?
Или что sizeof(LONG) == 4 bytes, sizeof(LPVOID) under win64 == 8 bytes?
Естественно, при создании кнопки нет необходимости обрабатывать WM_CREATE, а вот когда с нормальным окном работаешь, без этого не обойтись.
[COLOR=darkblue]LRESULT CALLBACK base_CBUTTON::WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
lpCBUTTON pThis = (lpCBUTTON)GetWindowLong(hwnd, GWL_USERDATA);
// Далее все обращения к нестатическим членам только через pThis
switch ( msg )
{
case WM_PAINT:
pThis->Paint();
break;
}
return pThis->OldWinProc(hwnd,msg,wParam,lParam);
};[/COLOR]
короче, я исходник приложил ;)
Исходник смогу посмотреть только в понедельник.
А вот по поводу приведенного куска кода, то там все просто:
{
lpCBUTTON pThis = (lpCBUTTON)GetWindowLong(hwnd, GWL_USERDATA);
// Далее все обращения к нестатическим членам только через pThis
switch(msg)
{
case WM_PAINT:
pThis->Paint();
return 0; // Это значит, что WM_PAINT отработан
}
return pThis->OldWinProc(hwnd,msg,wParam,lParam);
};
Что непонятно? что WM_CREATE посылается во время вызова CreateWindow и если в WndProc на обработчике WM_CREATE получить указатель на класс с помощью GetWindowLong, то он будет NULL и затем будет access violation при попытке вызова метода?
Посмотри внимательнее.
Сначала создается окно, а потом уже заменяется оконная процедура.
{
CreateWindowEx(.........);
SetWindowLong( hWnd_control, GWL_USERDATA, (LONG)this);
SetWindowLong(hWnd_control, GWL_WNDPROC, (LONG)WndProc);
.............
};
Т.о. WM_CREATE обрабатывается стандартной оконной процедурой, а не новой.
Естественно, при создании кнопки нет необходимости обрабатывать WM_CREATE, а вот когда с нормальным окном работаешь, без этого не обойтись.
Обработать WM_CREATE для стандартной кнопки не просто нет необходимости, а весьма проблематично. Приведи пример, как ты это делаешь?
А вот когда с нормальным окном работаешь и создаешь для него класс-обертку, я вообще не вижу острой необходимости обрабатывать WM_CREATE, т.к. все можно сделать сразу после вызова ::CreateWindow.
Или что sizeof(LONG) == 4 bytes, sizeof(LPVOID) under win64 == 8 bytes?
Ок. Тогда используем SetWindowLongPtr вместо SetWindowLong.
Исходник смогу посмотреть только в понедельник.
А вот по поводу приведенного куска кода, то там все просто:
{
lpCBUTTON pThis = (lpCBUTTON)GetWindowLong(hwnd, GWL_USERDATA);
// Далее все обращения к нестатическим членам только через pThis
switch(msg)
{
case WM_PAINT:
pThis->Paint();
return 0; // Это значит, что WM_PAINT отработан
}
return pThis->OldWinProc(hwnd,msg,wParam,lParam);
};
Походу дело в стилях окна-кнопки и/или главного окна, поэксперементировал со стиялями окна-кнопки, теперь все кнопки прорисовываются, но сообщение WM_PAINT попрежнему с огромной скоростью поступает к активному окну-кнопке.
[COLOR=darkblue]case WM_PAINT:
pThis->Paint();
return 0; // Это значит, что WM_PAINT отработан[/COLOR]
В этом я с тобой согласен. Только я делаю так
{
lpCBUTTON pThis = (lpCBUTTON)GetWindowLong(hwnd, GWL_USERDATA);
// Далее все обращения к нестатическим членам только через pThis
switch ( msg )
{
case WM_PAINT:
pThis->Paint();
break;
default :
return pThis->OldWinProc(hwnd,msg,wParam,lParam);
}
return 0;
};
Посмотри внимательнее.
Сначала создается окно, а потом уже заменяется оконная процедура.
{
CreateWindowEx(.........);
SetWindowLong( hWnd_control, GWL_USERDATA, (LONG)this);
SetWindowLong(hWnd_control, GWL_WNDPROC, (LONG)WndProc);
.............
};
Т.о. WM_CREATE обрабатывается стандартной оконной процедурой, а не новой.
Обработать WM_CREATE для стандартной кнопки не просто нет необходимости, а весьма проблематично. Приведи пример, как ты это делаешь?
А вот когда с нормальным окном работаешь и создаешь для него класс-обертку, я вообще не вижу острой необходимости обрабатывать WM_CREATE, т.к. все можно сделать сразу после вызова ::CreateWindow.
Ок. Тогда используем SetWindowLongPtr вместо SetWindowLong.
Для начала определим макросы для конвертации указателя, для того, чтобы не выводил некоторые варнинги
#ifdef _WIN64
#define WLP2PTR(ptr_type, wlong_ptr) \
reinterpret_cast<ptr_type>(wlong_ptr)
#define PTR2WLP(ptr) \
reinterpret_cast<LONG_PTR>(ptr)
#else
#define WLP2PTR(ptr_type, wlong) \
reinterpret_cast<ptr_type>((LONG_PTR)wlong)
#define PTR2WLP(ptr) \
(LONG)reinterpret_cast<LONG_PTR>(ptr)
#endif
Теперь определяем класс Window, базовый для всех окон, описываем виртуальные функции для используемых в проге сообщений и везде вставляем в методах
::DefWindowProc(hWnd, WM_???, wParam, lParam);
Все методы вида
LPARAM MsgHandler(WPARAM wParam, LPARAM lParam);
Описываем WndProc одну на всю программу
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
Window *pWnd;
LPCREATESTRUCT lpCreateStruct;
// Если не лень, можно уменьшить код,
// Отфильтровав все сообщения в начале, которые
// Посылаются при создании
// Тогда код получения указателя можно
// добавить сразу после их обработки
// а так получаем указатель в каждом Handler
switch (message)
{
case WM_COMMAND:
pWnd = WLP2PTR(Window *, ::GetWindowLongPtr(hWnd, GWLP_USERDATA));
_ASSERT(pWnd != NULL);
return pWnd->OnCommand(wParam, lParam);
case WM_CREATE:
// Здесь устанавливаем указатель
lpCreateStruct = WLP2PTR(LPCREATESTRUCT, lParam);
pWnd = WLP2PTR(Window *, lpCreateStruct->lpCreateParams);
_ASSERT(pWnd != NULL);
::SetWindowLongPtr(hWnd, GWLP_USERDATA, PTR2WLP(pWnd));
pWnd->hWnd = hWnd;
return pWnd->OnCreate(wParam, lParam);
case WM_DESTROY:
pWnd = WLP2PTR(Window *, ::GetWindowLongPtr(hWnd, GWLP_USERDATA));
_ASSERT(pWnd != NULL);
return pWnd->OnDestroy(wParam, lParam);
}
return ::DefWindowProc(hWnd, message, wParam, lParam);
}
метод класса Window
// Создание
HWND Create(
DWORD dwExStyle,
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x, int y,
int nWidth, int nHeight,
HWND hWndParent,
HMENU hMenu
)
{
return CreateWindowEx(
dwExStyle,
lpClassName,
lpWindowName,
dwStyle,
x, y,
nWidth, nHeight,
hWndParent,
hMenu,
hInst,
(LPVOID)this);
}
Работает 100%
Так и не понял, для чего ты вводишь макросы?
Они только загрязняют код. Тем более, что мешать преобразование типов C-style и reinterpret_cast как-то непрофессионально:
(LONG)reinterpret_cast<LONG_PTR>(ptr)
reinterpret_cast<ptr_type>((LONG_PTR)wlong)
Определять единый класс Window для всех окон в таком виде, да ещё и со всеми виртуальными методами для каждого сообщения, не имеет смысла.
Т.к. перечислить все виртуальные методы сначала невозможно, а в процессе разработки постоянно дорабатывать базовый класс - это нерационально. Посмотри в сторону уже реализованных оконных врапперов (MFC, WTL, VCL), там введены карты сообщений.
Привести ВСЕ обработчики к единому виду
LPARAM MsgHandler(WPARAM wParam, LPARAM lParam);
так же не получится без дополнительных извращений (см. WM_NOTIFY, ON_NOTIFY)
Этот класс не может быть использован для субклассинга.
Кроме того, базовый класс Window не имеет будущего, т.к. он ограничен тем, что пользователь не сможет больше работать с GWLP_USERDATA, этот атрибут навсегда занят базовым классом.
Описываем WndProc одну на всю программу
Как я уже сказал описывать одну оконную процедуру - нерационально.
// Отфильтровав все сообщения в начале, которые
// Посылаются при создании
Эту фразу я не понял. Ты имел в виду WM_CREATE?
Почему, тогда ты говоришь о множестве таких сообщений?
При вызове обработчиков ты сразу же возвращаешь их значение, это неверно, т.к. некоторые обработчики могут не полностью обрабатывать сообщения, поэтому необходимо ввести флаг необходимости дальнейшей обработки (bHandled).
Ещё раз прошу объяснить мне необходимость установки GWLP_USERDATA в обработчике WM_CREATE.
Это лишь усложняет код. Сравни свой:
{
Window *pWnd;
LPCREATESTRUCT lpCreateStruct;
switch (message)
{
case WM_CREATE:
// Здесь устанавливаем указатель
lpCreateStruct = WLP2PTR(LPCREATESTRUCT, lParam);
pWnd = WLP2PTR(Window *, lpCreateStruct->lpCreateParams);
_ASSERT(pWnd != NULL);
::SetWindowLongPtr(hWnd, GWLP_USERDATA, PTR2WLP(pWnd));
pWnd->m_hWnd = hWnd;
return pWnd->OnCreate(wParam, lParam);
}
}
//Создание
HWND Create(DWORD dwExStyle, LPCTSTR lpClassName,
LPCTSTR lpWindowName, DWORD dwStyle, int x, int y,
int nWidth, int nHeight, HWND hWndParent, HMENU hMenu)
{
return CreateWindowEx( dwExStyle, lpClassName,
lpWindowName, dwStyle, x, y, nWidth, nHeight,
hWndParent, hMenu, hInst, (LPVOID)this);
}
с таким:
HWND Create(DWORD dwExStyle, LPCTSTR lpClassName,
LPCTSTR lpWindowName, DWORD dwStyle, int x, int y,
int nWidth, int nHeight, HWND hWndParent, HMENU hMenu)
{
m_hWnd = CreateWindowEx( dwExStyle, lpClassName,
lpWindowName, dwStyle, x, y, nWidth, nHeight,
hWndParent, hMenu, hInst, (LPVOID)this);
_ASSERT(m_hWnd != NULL)
::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR)this);
}
За чем изобретать велосипед (трехколесный)?
Используйте уже придуманные и обкатанные оконные библиотеки. Я рекомендую WTL.
Столько критических замечаний возникло при просмотре твоего кода, что даже не знаю с чего начать...
Так и не понял, для чего ты вводишь макросы?
Они только загрязняют код. Тем более, что мешать преобразование типов C-style и reinterpret_cast как-то непрофессионально:
(LONG)reinterpret_cast<LONG_PTR>(ptr)
reinterpret_cast<ptr_type>((LONG_PTR)wlong)
Определять единый класс Window для всех окон в таком виде, да ещё и со всеми виртуальными методами для каждого сообщения, не имеет смысла.
Т.к. перечислить все виртуальные методы сначала невозможно, а в процессе разработки постоянно дорабатывать базовый класс - это нерационально. Посмотри в сторону уже реализованных оконных врапперов (MFC, WTL, VCL), там введены карты сообщений.
Привести ВСЕ обработчики к единому виду
LPARAM MsgHandler(WPARAM wParam, LPARAM lParam);
так же не получится без дополнительных извращений (см. WM_NOTIFY, ON_NOTIFY)
Этот класс не может быть использован для субклассинга.
Кроме того, базовый класс Window не имеет будущего, т.к. он ограничен тем, что пользователь не сможет больше работать с GWLP_USERDATA, этот атрибут навсегда занят базовым классом.
Как я уже сказал описывать одну оконную процедуру - нерационально.
Эту фразу я не понял. Ты имел в виду WM_CREATE?
Почему, тогда ты говоришь о множестве таких сообщений?
При вызове обработчиков ты сразу же возвращаешь их значение, это неверно, т.к. некоторые обработчики могут не полностью обрабатывать сообщения, поэтому необходимо ввести флаг необходимости дальнейшей обработки (bHandled).
Ещё раз прошу объяснить мне необходимость установки GWLP_USERDATA в обработчике WM_CREATE.
Это лишь усложняет код. Сравни свой:
{
Window *pWnd;
LPCREATESTRUCT lpCreateStruct;
switch (message)
{
case WM_CREATE:
// Здесь устанавливаем указатель
lpCreateStruct = WLP2PTR(LPCREATESTRUCT, lParam);
pWnd = WLP2PTR(Window *, lpCreateStruct->lpCreateParams);
_ASSERT(pWnd != NULL);
::SetWindowLongPtr(hWnd, GWLP_USERDATA, PTR2WLP(pWnd));
pWnd->m_hWnd = hWnd;
return pWnd->OnCreate(wParam, lParam);
}
}
//Создание
HWND Create(DWORD dwExStyle, LPCTSTR lpClassName,
LPCTSTR lpWindowName, DWORD dwStyle, int x, int y,
int nWidth, int nHeight, HWND hWndParent, HMENU hMenu)
{
return CreateWindowEx( dwExStyle, lpClassName,
lpWindowName, dwStyle, x, y, nWidth, nHeight,
hWndParent, hMenu, hInst, (LPVOID)this);
}
с таким:
HWND Create(DWORD dwExStyle, LPCTSTR lpClassName,
LPCTSTR lpWindowName, DWORD dwStyle, int x, int y,
int nWidth, int nHeight, HWND hWndParent, HMENU hMenu)
{
m_hWnd = CreateWindowEx( dwExStyle, lpClassName,
lpWindowName, dwStyle, x, y, nWidth, nHeight,
hWndParent, hMenu, hInst, (LPVOID)this);
_ASSERT(m_hWnd != NULL)
::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR)this);
}
За чем изобретать велосипед (трехколесный)?
Используйте уже придуманные и обкатанные оконные библиотеки. Я рекомендую WTL.
Если у меня на всю программу 4 окна, то это оптимальный вариант, чем тащить все что мне не надо из WTL. Конечно скопировать source тоже вариант, но разбираться сейчас нет времени, может потом когда появится свободное время, попробую.
Мешать reinterpret_cast и C-style cast непрофессионально? Ты лучше передай Microsoft, что непрофессионально писать в header
#ifndef _WIN64
#define SetWindowLongPtr SetWindowLong
#endif
Из за чего и вылазиет warning, если так коробит, можно исправить (LONG)(LONG_PTR)ptr, но так как-то некрасиво :) К тому же макрос вот я сейчас могу взять и исправить и он изменится во всем коде, а ты бы сейчас сидел Find/Replace
The WM_NCCREATE message is sent prior to the WM_CREATE message when a window is first created
Если ты обратишь внимание как реализовано в MFC, то с удивлением увидишь, что там также все в виртуальных функциях.
Занят GWLP_USERDATA, а у тебя он не занят.
Нет никакой проблемы для введения особого вида обработчика. Подразумевалось только, чтобы была возможность вызывать DefWindowProc из обработчика без извращений типа вызова функций получения последних параметров сообщения как это сделано в CWnd::Default в MFC
Если у меня на всю программу 4 окна, то это оптимальный вариант, чем тащить все что мне не надо из WTL.
Настоятельно советую разобраться с WTL.
"Тащить" ничего не придется, достаточно будет отнаследовать свой клас от WTL::CWindow.
И поверь, что результирующий код получится значительно меньше, чем твой с виртуальными таблицами и т.п.
Конечно скопировать source тоже вариант, но разбираться сейчас нет времени, может потом когда появится свободное время, попробую.
Мешать reinterpret_cast и C-style cast непрофессионально? Ты лучше передай Microsoft, что непрофессионально писать в header
#ifndef _WIN64
#define SetWindowLongPtr SetWindowLong
#endif
Из за чего и вылазиет warning, если так коробит, можно исправить (LONG)(LONG_PTR)ptr, но так как-то некрасиво :) К тому же макрос вот я сейчас могу взять и исправить и он изменится во всем коде, а ты бы сейчас сидел Find/Replace
А не парился бы... посмотри на мой код.
Если ты обратишь внимание как реализовано в MFC, то с удивлением увидишь, что там также все в виртуальных функциях.
У меня нет под боком MFC, но совсем не согласен, "что там также все в виртуальных функциях". Для чего они там?
Ты хочешь сказать, что в базовом оконном классе MFC учтены обработчики всех сообщений? :D
А для чего же там карта сообщений?
Кроме того, MFC и без этого неповоротливый монстр, которого давно пора снести на свалку. Смотри в сторону WTL.
Занят GWLP_USERDATA, а у тебя он не занят.
У меня он занят для одного единственного окна и то в целях примера. Я не стал бы использовать этот метод для создания некоторого базового класса.
Нет никакой проблемы для введения особого вида обработчика.
Ну вообще-то есть сложности в определении обработчика для конкретного экземпляра класса. Эта сложность по-разному решена в MFC и WTL. В WTL весьма оригинально и остроумно, хотя не без хакинга.
Подразумевалось только, чтобы была возможность вызывать DefWindowProc из обработчика без извращений типа вызова функций получения последних параметров сообщения как это сделано в CWnd::Default в MFC
Не понял фразы.
Пример:
_AFXWIN_INLINE int CWnd::OnCreate(LPCREATESTRUCT)
{ return (int)Default(); }
Обрати внимание : Default без параметров. Если передавать параметры, то получается надо обратно из параметров функции OnCreate создавать WPARAM и LPARAM. Однако хитрые программисты из Microsoft сделали так. Они сохраняют последние параметры в каком то хитром классе и потом их оттуда вытаскивают:
LRESULT CWnd::Default()
{
// call DefWindowProc with the last message
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
return DefWindowProc(pThreadState->m_lastSentMsg.message,
pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam);
}
Я тут оттрассировал все сообщения до WM_CREATE, новая версия:
Window *pWnd;
LPCREATESTRUCT lpCreateStruct;
switch (message)
{
case WM_GETMINMAXINFO:
case WM_NCCREATE:
case WM_CANCELMODE:
case WM_ENABLE:
case WM_ACTIVATEAPP:
case WM_NCCALCSIZE:
return ::DefWindowProc(hWnd, message, wParam, lParam);
case WM_CREATE:
lpCreateStruct = WLP2PTR(LPCREATESTRUCT, lParam);
pWnd = WLP2PTR(Window *, lpCreateStruct->lpCreateParams);
_ASSERT(pWnd != NULL);
::SetWindowLongPtr(hWnd, GWLP_USERDATA, PTR2WLP(pWnd));
pWnd->put_HWND(hWnd);
return pWnd->OnCreate(wParam, lParam);
}
pWnd = WLP2PTR(Window *, ::GetWindowLongPtr(hWnd, GWLP_USERDATA));
_ASSERT(pWnd != NULL);
switch (message)
{
case WM_COMMAND:
return pWnd->OnCommand(wParam, lParam);
case WM_DESTROY:
return pWnd->OnDestroy(wParam, lParam);
}
return ::DefWindowProc(hWnd, message, wParam, lParam);
На самом деле в MFC определены все MSG handlerы, не знал бы не говорил.
Ну, это обработчики далеко не ВСЕХ сообщений.
Но все равно, спасибо, что обратил внимание. Я в очередной раз убедился в тяжеловесности MFC и убогой его реализации. Представляешь, сколько лишнего кода тянется, а какие огромные таблицы виртуальных методов!!!
Однако хитрые программисты из Microsoft сделали так. Они сохраняют последние параметры в каком то хитром классе и потом их оттуда вытаскивают:
Это пример "хитрости", когда общая концепция полное Г.
Короче, УЖАС!
Я тут оттрассировал все сообщения до WM_CREATE, новая версия:
Ты лучше аргументируй, чем тебе не нравится установка GWLP_USERDATA после Create?
Зачем возиться со структурой CREATESTRUCT ?
Кстати, до WM_CREATE приходят:
WM_GETMINMAXINFO
WM_NCCREATE
WM_NCCALCSIZE
остальные не наблюдаются.
Настоятельно советую разобраться с WTL.
"Тащить" ничего не придется, достаточно будет отнаследовать свой клас от WTL::CWindow.
И поверь, что результирующий код получится значительно меньше, чем твой с виртуальными таблицами и т.п.
Ты мне это написал еще с самого начала, я тогда скачал себе эту библиотеку, и инфу по ее использованию. Ты прав нечего изобретать велосипед. Просто не знал о ее существовании, а на MFC и пробовать не хотел (организм не усваивает :) ).
Столько критических замечаний возникло при просмотре твоего кода...
Ты не первый замечание делаешь , я еще новичек, тока учусь, программирую с недавнего времени, да еще это нетерпение, охото побыстрей и все сразу, времени на проектирование не удялял, да и еще протому что задачька была маленькая, думал походу разберусь, а не тут то было. Но теперь с WTL почти все проблемы решились, за 1 день реализовал то, на что раньше неделя уходила, когда сам с нуля все писал. Так что в этом топике для меня все(или почти все) стало ясно, думаю можно закругляться.
Thank you all.
Ну, это обработчики далеко не ВСЕХ сообщений.
Но все равно, спасибо, что обратил внимание. Я в очередной раз убедился в тяжеловесности MFC и убогой его реализации. Представляешь, сколько лишнего кода тянется, а какие огромные таблицы виртуальных методов!!!
Это пример "хитрости", когда общая концепция полное Г.
Короче, УЖАС!
Ты лучше аргументируй, чем тебе не нравится установка GWLP_USERDATA после Create?
Зачем возиться со структурой CREATESTRUCT ?
Кстати, до WM_CREATE приходят:
WM_GETMINMAXINFO
WM_NCCREATE
WM_NCCALCSIZE
остальные не наблюдаются.
Наблюдаются. В MSDN написано : а также посылаются сообщения для отображения окна в зависимости от стилей. В общем я ручками проверял, именно эти посылаются (стиль WS_POPUP | WS_VISIBLE | WS_CAPTION).
Почему мне не нравится? Потому что я хочу сделать один базовый класс в моей убогой программе и писать в обработчике OnCreate все, что хочу :) В том числе возвращать -1, а не посылать сообщение WM_CLOSE.