Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Как написать класс который имеет свои события (callback методы)

388
01 сентября 2005 года
warezhka
129 / / 11.10.2004
Как мне написать класс без использования MFC, чтобы он мог вызывать свои события подтипу как в MFC например класс CWnd может вызывать OnEnable, OnMouseMove и проч.
Как мне это реализовать? моноли какнито заделать CALLBACK методы у класса?
292
02 сентября 2005 года
Matush
726 / / 14.01.2004
Цитата:
Originally posted by warezhka
Как мне написать класс без использования MFC, чтобы он мог вызывать свои события подтипу как в MFC например класс CWnd может вызывать OnEnable, OnMouseMove и проч.
Как мне это реализовать? моноли какнито заделать CALLBACK методы у класса?



Я так понял это будет класс какого-нить окна.
так вот.
описываешь в классе CALLBACK функцию как статическую
static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
и еще
WNDPROC OldWndProc;

где-то в конструкторе/криейте делаешь сабклассинг

SetWindowLong(hWnd, GWL_USERDATA, (LONG)this);
OldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC, (LONG)WndProc);

ну и реализация

LRESULT CALLBACK CSomeClass::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
CSomeClass* lpSomeClass = (CSomeClass*)GetWindowLong(hWnd, GWL_USERDATA);
switch(msg)
{
}
return CallWindowProc(lpSomeClass->OldWndProc, hWnd, msg, wParam, lParam);
}

вот и все. Ну и всередине CALLBACK функции обращение к методам класса через lpSomeClass->

3
02 сентября 2005 года
Green
4.8K / / 20.01.2000
Цитата:
Originally posted by Matush
где-то в конструкторе/криейте делаешь сабклассинг


Мне больше нравится, как это сделано в WTL.
Там используется не GWL_USERDATA, а создание callback-а для каждого конкретного экземпляра класса и указание адреса этого callback-а в GWL_WNDPROC.
Только это без небольшого хака не осуществимо.

292
02 сентября 2005 года
Matush
726 / / 14.01.2004
А Мне больше нравится, как это сделано у Меня.
388
03 сентября 2005 года
warezhka
129 / / 11.10.2004
Цитата:
Я так понял это будет класс какого-нить окна.


нет... окна может и не быть, если я пишу свой класс наподобии CSocket и хочу реализовать методы OnAccept, OnReceive и проч. В MFС юзаются всякие карты сообщений...а как тут быть?

3
03 сентября 2005 года
Green
4.8K / / 20.01.2000
Цитата:
Originally posted by Matush
А Мне больше нравится, как это сделано у Меня.


Ну это всем известный путь, и не всегда работающий в силу того, что некоторые приложения используют GWL_USERDATA для некоторых своих целей.
А вот методика использованная в WTL достаточно оригинальна, не думаю, что все про неё знают.

299
04 сентября 2005 года
3D Bob
885 / / 18.04.2005
Цитата:
Originally posted by Green
Ну это всем известный путь, и не всегда работающий в силу того, что некоторые приложения используют GWL_USERDATA для некоторых своих целей.
А вот методика использованная в WTL достаточно оригинальна, не думаю, что все про неё знают.



И наверное не узнаем если ты про неё не скажешь)))

А для создания событий используй бесконечные циклы, для проверки совершения действия.

292
05 сентября 2005 года
Matush
726 / / 14.01.2004
Цитата:
Originally posted by Green
Ну это всем известный путь,

согласен.

Цитата:
Originally posted by Green
не всегда работающий в силу того, что некоторые приложения используют GWL_USERDATA для некоторых своих целей.

Согласен. Но причем тут некоторые приложения, когда это моя программа, и я юзаю
свой класс и соответственно юзаю его корректно.

Цитата:
Originally posted by Green

А вот методика использованная в WTL достаточно оригинальна, не думаю, что все про неё знают.


А вот это если не затруднит, то поведай плз.

3
05 сентября 2005 года
Green
4.8K / / 20.01.2000
Цитата:
Originally posted by Matush
Согласен. Но причем тут некоторые приложения, когда это моя программа, и я юзаю
свой класс и соответственно юзаю его корректно.


GWL_USERDATA - это в принципе открытые данные, которые некоторые СТОРОННИЕ программы могут изменять. Например, различные хукеры, логеры и т.п.
GWL_WNDPROC - в принципе тоже открытые данные, но их использование понятно, поэтому обычно, если даже их кто-то переопределяет извне, то потом все же отдает управление по оригинальному указателю, что не скажешь про GWL_USERDATA.

Цитата:
Originally posted by Matush

А вот это если не затруднит, то поведай плз.


Ну в кратце я рассказал. Могу рассказать чуть подробнее, но за конкретикой обратитесь к исходникам WTL.

Идея простая, как 2 копейки.
Сначала окно у нас имеет некотрую стартовую WndProc, которая естественно вызывается на WM_CREATE. Собственно в этой процедуре есть единственный обработчик этого (WM_CREATE) сообщения, в который передается указатель на экземпляр соотв. класса.
Собственно, тут тоже интересная методика. Указатель можно было бы передать через CREATESTRUCT, т.е. через lpParam функции CreateWindow, но разработчики WTL, видимо, предположили, что пользователь захочет использовать этот параметр для своих нужд, поэтому они передают указатель иным способом - через массив, в который кладут его непосредственно перед вызовом CreateWindow. Известно, что WM_CREATE не совсем простое сообщение, оно не ставится в очередь, а вызывается сразу из тела CreateWindow, поэтому точно известно, что последний положенный указатель в массив будет однозначно соответствовать последнему созданному окну в этом потоке (те кто будет смотреть код: именно поэтому в коде проверяется ID потока).

После получения указателя на екземпляр начинается самое интересное. Каждый экземпляр содержит массив, называемый thunk (переходник). На самом деле это не массив данных, а код примерно след содержания:

 
Код:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
   CWnd* This = 0x00000000;
   return This->WindowProc(hwnd, uMsg, wParam, lParam);
}

Значение 0х00000000 подменяется конкретным значением указателя на экземпляр класса.
После этого вызывается SetWindowLong с GWL_WNDPROC, куда передается указатель на массив thunk, как указатель на оконную процедуру. Теперь у каждого окна есть своя оконная процедура, которая жестко привязана к конкретному экземпляру класса.

Вот собственно и всё.
299
11 сентября 2005 года
3D Bob
885 / / 18.04.2005
У меня Билдер вот на эту строку
 
Код:
CallWindowProc((WNDPROC)a->DefaultProcBut,hWnd,msg,wParam,lParam);
Ругается вот таким трехэтажным матом.
Цитата:
[C++ Error] WDB.cpp(198): E2034 Cannot convert 'long (__stdcall *)(void *,unsigned int,unsigned int,long)' to 'int (__stdcall *)()'


Я не понимаю почему так, надо чтоли преобразоывать во что-то иное нежели WNDPROC ?

Пришлось извращаться вот таким вот образом:

 
Код:
CallWindowProc((int (__stdcall *)())a->DefaultProcBut,hWnd,msg,wParam,lParam);
Заработало, но почему оно так я не понимаю:(
3
12 сентября 2005 года
Green
4.8K / / 20.01.2000
Цитата:
Originally posted by 3D Bob
У меня Билдер вот на эту строку
 
Код:
CallWindowProc((WNDPROC)a->DefaultProcBut,hWnd,msg,wParam,lParam);
Ругается вот таким трехэтажным матом.

Я не понимаю почему так, надо чтоли преобразоывать во что-то иное нежели WNDPROC ?

Пришлось извращаться вот таким вот образом:
 
Код:
CallWindowProc((int (__stdcall *)())a->DefaultProcBut,hWnd,msg,wParam,lParam);
Заработало, но почему оно так я не понимаю:(


Вопрос повторяется с завидной переодичностью...
Метод должен быть статическим.
А то что компилятор Билдера допускает такие преобразования - это его отрицательная сторона.
А то что у тебя заработало, то это по чистой случайности, жди беды.

299
12 сентября 2005 года
3D Bob
885 / / 18.04.2005
Цитата:
Originally posted by Green
Вопрос повторяется с завидной переодичностью...
Метод должен быть статическим.
А то что компилятор Билдера допускает такие преобразования - это его отрицательная сторона.
А то что у тебя заработало, то это по чистой случайности, жди беды.



Метод еще как статический, я очень долго плевался на то что нужно делать его именно статическим, но я СДЕЛАЛ его статическим, и в классе он объявлен как статический)

Подожи какой метод??
DefaultButProc Это вообще не метод, это переменная, хранящий адрес старого метода.

Примерно так

 
Код:
__fastcall TIdentForm::TIdentForm(void * hInst){
        HandleButton = CreateWindow("button","Войти",WS_CHILD|WS_VISIBLE|BS_FLAT,60,80,75,25,Form->Handle,NULL,hInst,NULL);
SetWindowLong(HandleButton,GWL_USERDATA,(long)this);
        DefaultProcBut = (WNDPROC)SetWindowLong(HandleButton, GWL_WNDPROC,(long)ButProc);

 
Код:
LRESULT CALLBACK TIdentForm::ButProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
        TIdentForm * IDForm = (TIdentForm * )GetWindowLong(hWnd, GWL_USERDATA);
        switch (msg){
                case 514:
                        IDForm->ClickID(NULL);
                        break;
                default: return CallWindowProc((int (__stdcall *)())IDForm->DefaultProcBut,hWnd,msg,wParam,lParam);
        }
        return 0;
}
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог