Как написать класс который имеет свои события (callback методы)
Как мне это реализовать? моноли какнито заделать CALLBACK методы у класса?
Как мне написать класс без использования 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->
где-то в конструкторе/криейте делаешь сабклассинг
Мне больше нравится, как это сделано в WTL.
Там используется не GWL_USERDATA, а создание callback-а для каждого конкретного экземпляра класса и указание адреса этого callback-а в GWL_WNDPROC.
Только это без небольшого хака не осуществимо.
нет... окна может и не быть, если я пишу свой класс наподобии CSocket и хочу реализовать методы OnAccept, OnReceive и проч. В MFС юзаются всякие карты сообщений...а как тут быть?
А Мне больше нравится, как это сделано у Меня.
Ну это всем известный путь, и не всегда работающий в силу того, что некоторые приложения используют GWL_USERDATA для некоторых своих целей.
А вот методика использованная в WTL достаточно оригинальна, не думаю, что все про неё знают.
Ну это всем известный путь, и не всегда работающий в силу того, что некоторые приложения используют GWL_USERDATA для некоторых своих целей.
А вот методика использованная в WTL достаточно оригинальна, не думаю, что все про неё знают.
И наверное не узнаем если ты про неё не скажешь)))
А для создания событий используй бесконечные циклы, для проверки совершения действия.
Ну это всем известный путь,
согласен.
не всегда работающий в силу того, что некоторые приложения используют GWL_USERDATA для некоторых своих целей.
Согласен. Но причем тут некоторые приложения, когда это моя программа, и я юзаю
свой класс и соответственно юзаю его корректно.
А вот методика использованная в WTL достаточно оригинальна, не думаю, что все про неё знают.
А вот это если не затруднит, то поведай плз.
Согласен. Но причем тут некоторые приложения, когда это моя программа, и я юзаю
свой класс и соответственно юзаю его корректно.
GWL_USERDATA - это в принципе открытые данные, которые некоторые СТОРОННИЕ программы могут изменять. Например, различные хукеры, логеры и т.п.
GWL_WNDPROC - в принципе тоже открытые данные, но их использование понятно, поэтому обычно, если даже их кто-то переопределяет извне, то потом все же отдает управление по оригинальному указателю, что не скажешь про GWL_USERDATA.
А вот это если не затруднит, то поведай плз.
Ну в кратце я рассказал. Могу рассказать чуть подробнее, но за конкретикой обратитесь к исходникам WTL.
Идея простая, как 2 копейки.
Сначала окно у нас имеет некотрую стартовую WndProc, которая естественно вызывается на WM_CREATE. Собственно в этой процедуре есть единственный обработчик этого (WM_CREATE) сообщения, в который передается указатель на экземпляр соотв. класса.
Собственно, тут тоже интересная методика. Указатель можно было бы передать через CREATESTRUCT, т.е. через lpParam функции CreateWindow, но разработчики WTL, видимо, предположили, что пользователь захочет использовать этот параметр для своих нужд, поэтому они передают указатель иным способом - через массив, в который кладут его непосредственно перед вызовом CreateWindow. Известно, что WM_CREATE не совсем простое сообщение, оно не ставится в очередь, а вызывается сразу из тела CreateWindow, поэтому точно известно, что последний положенный указатель в массив будет однозначно соответствовать последнему созданному окну в этом потоке (те кто будет смотреть код: именно поэтому в коде проверяется ID потока).
После получения указателя на екземпляр начинается самое интересное. Каждый экземпляр содержит массив, называемый thunk (переходник). На самом деле это не массив данных, а код примерно след содержания:
{
CWnd* This = 0x00000000;
return This->WindowProc(hwnd, uMsg, wParam, lParam);
}
Значение 0х00000000 подменяется конкретным значением указателя на экземпляр класса.
После этого вызывается SetWindowLong с GWL_WNDPROC, куда передается указатель на массив thunk, как указатель на оконную процедуру. Теперь у каждого окна есть своя оконная процедура, которая жестко привязана к конкретному экземпляру класса.
Вот собственно и всё.
Я не понимаю почему так, надо чтоли преобразоывать во что-то иное нежели WNDPROC ?
Пришлось извращаться вот таким вот образом:
У меня Билдер вот на эту строку
Я не понимаю почему так, надо чтоли преобразоывать во что-то иное нежели WNDPROC ?
Пришлось извращаться вот таким вот образом:
Вопрос повторяется с завидной переодичностью...
Метод должен быть статическим.
А то что компилятор Билдера допускает такие преобразования - это его отрицательная сторона.
А то что у тебя заработало, то это по чистой случайности, жди беды.
Вопрос повторяется с завидной переодичностью...
Метод должен быть статическим.
А то что компилятор Билдера допускает такие преобразования - это его отрицательная сторона.
А то что у тебя заработало, то это по чистой случайности, жди беды.
Метод еще как статический, я очень долго плевался на то что нужно делать его именно статическим, но я СДЕЛАЛ его статическим, и в классе он объявлен как статический)
Подожи какой метод??
DefaultButProc Это вообще не метод, это переменная, хранящий адрес старого метода.
Примерно так
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);
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;
}