Re[5]: Оконная функция - ЧЛЕН КЛАССА?!
От: av Россия  
Дата: 27.04.01 12:29
Оценка: 9 (2)
>и плиз на форум, интересно

Ну ловите, коль интересно (я так его в архивах и не нашел. Написал по новой и снабдил некоторыми комментариями)

--- 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);

#define CreateCaller(Class, Method) ((WNDPROC) (__CreateCaller ((void*) (Class), ConvertAnything (Method))))
#define DeleteCaller(Caller) (delete [] ((char*) (Caller)))

// Ввиду того, что C++ не разрешает casting из
// LRESULT (CClass::*_thiscall)(HWND, UINT, WPARAM, LPARAM) в void*, приходится
// извращаться. Данная функция просто возвращает первый параметр как void*, а
// многоточие (...) отключает проверку типов.

__declspec (naked) void* _cdecl ConvertAnything (...)
{
_asm {
mov eax, [esp+4] // :-)
ret
}
}

// Необходимо для отключения выравнивания переменных внутри структуры TCallerSuruct
#pragma pack (push, 1)

// Итак, собственно главная функция
char* __CreateCaller (void* Class, void* Method)
{
// Реально Caller представляет из себя две инструкции:
// mov ecx, address_of_class_instance
// jmp class_method

struct TCallerStruct {
unsigned char Mov_ECX_imm32;
void* Class_Addr;
unsigned char Jmp_rel32;
unsigned long Method_Offset;
} *Caller = (TCallerStruct*) new char [sizeof (TCallerStruct)];

// Эти две инструкции мы в него и запихиваем

Caller->Mov_ECX_imm32 = 0xB9;
Caller->Class_Addr = Class;
Caller->Jmp_rel32 = 0xE9;
Caller->Method_Offset = (unsigned long) Method — ((unsigned long)
&Caller->Method_Offset + sizeof (Caller->Method_Offset));

return (char*) Caller;
}

// Восстановить предыдущий параметр выравнивания
#pragma pack (pop)

//--------------------------------------------------------------
// Все. Дальше — простая иллюстрация работы


class CWindow {
private:
HWND hwnd; // no comments

static ATOM WClass; // класс этого окна. Один на все экземпляры, потому и static
static WNDPROC Caller; // экземпляр Caller'a. Так как один на класс, то тоже один.
static int UsageCount; // количество экземпляров данного окна. Как только оно достигает
// нуля, класс и Caller освобождаются.

public:
CWindow ();
LRESULT WndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
~CWindow ();


};

ATOM CWindow::WClass = NULL;
WNDPROC CWindow::Caller = NULL;
int CWindow::UsageCount = 0;

// Итак, собственно пример использования.

CWindow::CWindow ()
{
// Если еще не создано ни одного экземпляра, надо создать класс окна.
if (!WClass) {
WNDCLASS wc;

wc.style = CS_HREDRAW | CS_VREDRAW;

// Watch this!
wc.lpfnWndProc = Caller = CreateCaller (this, WndProc);

wc.cbClsExtra = wc.cbWndExtra = 0;
wc.hInstance = (HINSTANCE) GetModuleHandle (NULL);
wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW);
wc.lpszMenuName = NULL;
wc.lpszClassName = "CLASS_WIN";

// Сохраним класс
WClass = RegisterClass (&wc);
}

hwnd = CreateWindow ((LPSTR) WClass, "A Window", WS_OVERLAPPEDWINDOW, 100, 100,
400, 200,
NULL, NULL, (HINSTANCE) GetModuleHandle (NULL), NULL);

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;

while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}

return msg.wParam;
}

---------------------------------------------------------------------------------------
Все замечания просьба направлять сюда и/или на av_13@imail.ru
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.