Здравствуйте IT, 20.04.2001 17:06:30 вы писали:
>>Может ли быть оконная процедура главного окна членом класса? >>Как это реализовать? Все мои попытки заканчивались сообщениями об ошибках :( > >Может, но только статическим, иначе ты всегда получишь первым параметром указатель на this.
Это не совсем так! ATL и WTL спокойно используют для этого нормальные функции, но не напрямую, а через разные увертки и ужимки. Подробнее смотри реализацию CWindowImpl<>.
Если окно свое, то можно записать указатель на объект нужного класс в область данных окна. При этом в качестве ф-ии окна можно использовать или глобальную ф-ю или статическую. Эта ф-я вынимает адрес объекта и производит вызов нужного метода.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
>Может, но только статическим, иначе ты всегда получишь первым параметром указатель на this.
Э-э-э... А каким таким образом статическая фнукция может получать доступ к нестатическим полям? По-моему все можно сделать двумя методами. Первый (дубовый, используктся в Borland VCL и по-моему в MFC) — это написать для каждого класса свою собственную функцию (не член класса), содержащая нечто вроде:
// за правильность констант не ручаюсь, пишу по памяти
if (uMsg == WM_CREATE) {
// присоединить к окну адрес его класса, который должен передаваться как параметр
SetWindowLong (hwndWindow, GWL_USER, lParam);
}
MyClass = (MYCLASS*) GetWindowLong (hwndWindow, GWL_USER);
MyClass->MyMethod (uMsg, wParam, lParam);
}
void MyClass::CreateWindow (...)
{
...
hwndWindow = ::CreateWindow (... ,
(LPARAM) this); // параметр созания — как раз указатель на класс
}
Второй способ — поизящнее (но я его по памяти уже не напишу, изложу только идею). Смысл прост: создается класс/структура/массив/да_неавжо_что, содержащее в себе инструкции запихивания в стек адрес класса (то есть this). После этого просто выполняется jump на метод данного класса. И тогда вместо указателя на процедуру передаем указатель на _класс_ — все остальное он сделает сам. Минус — придется повозиться с реализацией. Плюс — один объект на всю программу. Если кому сильно надо — поищу равлизацию (где-то в архивах валялась).
Вот вроде все. Если у кого винды свалятся в результате применения написнного — ногами только не бейте! :-)
Здравствуйте av, 23.04.2001 18:39:40 вы писали:
>Второй способ — поизящнее (но я его по памяти уже не напишу, изложу только >идею). Смысл прост: создается класс/структура/массив/да_неавжо_что, содержащее >в себе инструкции запихивания в стек адрес класса (то есть this). После этого >просто выполняется jump на метод данного класса. И тогда вместо указателя на >процедуру передаем указатель на _класс_ — все остальное он сделает сам. Минус ->придется повозиться с реализацией. Плюс — один объект на всю программу. Если >кому сильно надо — поищу равлизацию (где-то в архивах валялась).
Если не затруднит, plz на deltaone@mail.ru
Заранее thx ;)
Здравствуйте deltaone, 25.04.2001 07:32:43 вы писали:
>Здравствуйте av, 23.04.2001 18:39:40 вы писали: > >>Второй способ — поизящнее (но я его по памяти уже не напишу, изложу только >идею). Смысл прост: создается класс/структура/массив/да_неавжо_что, содержащее >в себе инструкции запихивания в стек адрес класса (то есть this). После этого >просто выполняется jump на метод данного класса. И тогда вместо указателя на >процедуру передаем указатель на _класс_ — все остальное он сделает сам. Минус ->придется повозиться с реализацией. Плюс — один объект на всю программу. Если >кому сильно надо — поищу равлизацию (где-то в архивах валялась). > >Если не затруднит, plz на deltaone@mail.ru >Заранее thx ;) >
и плиз на форум, интересно
Здравствуйте deltaone, 25.04.2001 07:32:43 вы писали:
>Здравствуйте av, 23.04.2001 18:39:40 вы писали: > >>Второй способ — поизящнее (но я его по памяти уже не напишу, изложу только >идею). Смысл прост: создается класс/структура/массив/да_неавжо_что, содержащее >в себе инструкции запихивания в стек адрес класса (то есть this). После этого >просто выполняется jump на метод данного класса. И тогда вместо указателя на >процедуру передаем указатель на _класс_ — все остальное он сделает сам. Минус ->придется повозиться с реализацией. Плюс — один объект на всю программу. Если >кому сильно надо — поищу равлизацию (где-то в архивах валялась). > >Если не затруднит, plz на deltaone@mail.ru >Заранее thx ;) >И на dimedrol_2002@mail.ru.Заранее благодарен
Здравствуйте av, 23.04.2001 18:39:40 вы писали:
>Э-э-э... А каким таким образом статическая фнукция может получать доступ к нестатическим полям? По-моему все можно сделать двумя методами. Первый (дубовый,
Согласен, и его дубовость может сослужить плохую службу, поскольку GWL_USERDATA может пользоваться совершенно другой кусок кода... И тогда все просто рухнет.
>Второй способ — поизящнее
... только я что-то ничего не понял, как он работает.
Суть проблемы — статическая функция не получает this, а ей все же нужно тем или иным способом получить его. Поскольку наша оконная функция стандартным способом получает хэндл окна, для которого нужно обработать сообщение, именно его ей нужно использовать для получения указателя на объект класса, ассоциированного с данным hwnd. Следовательно, нужно иметь статический (глобальный) указатель на массив указателей на объекты и прямым поиском искать объект, связанный с полученным хэндлом окна(дубовый способ), либо иметь карту <хэндл_окна-указатель_на_объект> (изящный способ).
Ну ловите, коль интересно (я так его в архивах и не нашел. Написал по новой и снабдил некоторыми комментариями)
--- TEST_CALLER.CPP -------------------------------------------------------------------
// Method caller
// Преобразовывает __stdcall (aka CALLBACK, WINAPI, etc) в thiscall.
// Created on April 26-27, 2001 by AV.
// Use free, but please уважайте копирайты! :-)
// Да, чуть не забыл: у MSVC++ и Borland C++ Builder'a разные методы вызова
// thiscall-функций (то бишь методов): MSVC++ запихивает this в ECX, а
// Builder — последним параметром в стек. В связи с этим на Builder'е для реализации
// нижеприведенного придется повозиться побольше. А пока что хочу только заметить:
// *** INCOMPATIBLE WITH C++ BUILDER! FOR USE ONLY IN MICROSOFT VISUAL C++!!! ***
// В любом случае за последствия от использования кода ответственности никакой не несу.
// Сами проверяйте! :-)
#include <windows.h>
// Итак, эдесь начинается объект Caller.
//-----------------------------------------------------------
//
// Создание и привязка: wc.lpfnWndProc = CreateCaller (wnd, CWnd::WndProc);
// Удаление: DeleteCaller (Caller);
// Ввиду того, что C++ не разрешает casting из
// LRESULT (CClass::*_thiscall)(HWND, UINT, WPARAM, LPARAM) в void*, приходится
// извращаться. Данная функция просто возвращает первый параметр как void*, а
// многоточие (...) отключает проверку типов.
// Необходимо для отключения выравнивания переменных внутри структуры TCallerSuruct
#pragma pack (push, 1)
// Итак, собственно главная функция
char* __CreateCaller (void* Class, void* Method)
{
// Реально Caller представляет из себя две инструкции:
// mov ecx, address_of_class_instance
// jmp class_method
// Восстановить предыдущий параметр выравнивания
#pragma pack (pop)
//--------------------------------------------------------------
// Все. Дальше — простая иллюстрация работы
class CWindow {
private:
HWND hwnd; // no comments
static ATOM WClass; // класс этого окна. Один на все экземпляры, потому и static
static WNDPROC Caller; // экземпляр Caller'a. Так как один на класс, то тоже один.
static int UsageCount; // количество экземпляров данного окна. Как только оно достигает
// нуля, класс и Caller освобождаются.
if (hwnd) {
ShowWindow (hwnd, SW_SHOW);
UpdateWindow (hwnd);
// Увеличим счетчик
UsageCount++;
}
}
CWindow::~CWindow ()
{
// Уменьшим счетчик. Если он равен нулю (окон больне не осталось), освободим
// занятые ресурсы.
if (!--UsageCount) {
UnregisterClass ((LPSTR) WClass, (HINSTANCE) GetModuleHandle (NULL));
WClass = NULL;
DeleteCaller (Caller);
}
}
// Это наша НУ ПРОСТО СУПЕР навороченная оконная процедура :-)
LRESULT CWindow::WndProc (HWND hwndWindow, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// Вообще-то этого здесь не надо...
if (uMsg == WM_DESTROY)
PostQuitMessage (0);
return DefWindowProc (hwndWindow, uMsg, wParam, lParam);
}
// А теперь посмотрите на WinMain. Просто создаем экземпляр CWindow и радуемся жизни!
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
{
MSG msg;
CWindow wnd;
> ... только я что-то ничего не понял, как он работает. > Суть проблемы — статическая функция не получает this, а ей все же нужно тем или иным способом получить его. Поскольку наша оконная функция стандартным способом получает хэндл окна, для которого нужно обработать сообщение, именно его ей нужно использовать для получения указателя на объект класса, ассоциированного с данным hwnd. Следовательно, нужно иметь статический (глобальный) указатель на массив указателей на объекты и прямым поиском искать объект, связанный с полученным хэндлом окна(дубовый способ), либо иметь карту <хэндл_окна-указатель_на_объект> (изящный способ).
(протяжно так) Не-е-е... Ты, в общем, source посмотри, я его тут куда-то запостил. Там hwnd для определения экземпляра не используется вообще (да и геморно это — представь себе: есть, скажем, 10 окон одного класса (хотя так не бывает), так что же, элемент твоего списка 10 раз дублировать?)
Здравствуйте Anrie, 20.04.2001 15:06:08 вы писали:
>Может ли быть оконная процедура главного окна членом класса? >Как это реализовать? Все мои попытки заканчивались сообщениями об ошибках :(