![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Заюзать метод класса как WindowProc
SpellCaster © (2008-03-26 13:47:00)Возникла такая надобность: создать окошко для приёма сообщений для каждого экземпляра класса. По определённым причинам пользоваться AllocateWnd не хочется. Решил воплотить такую схему:
1) При создании объекта создается окно с WindowProc, указывающим на
function InnerWndProc(wnd: hWnd; msg, wParam, lParam: Longint): Longint; stdcall;
2) После этого в пользовательскую инфу окна заносится Self
3) При поступлении мессаги в InnerWndProc из польз. инфы окна извлекается Self и вызывается Self.WndProc
ИМХО, вполне неплохое и удобное решение. Проблема в том, что окно создаваться не желает, причём как-то хаотически. Код создания окна:
constructor TMyClass.Create;
var WndClass: TWndClass;
ClName: string;
begin
inherited;
FillChar(WndClass,SizeOf(WndClass),0);
ClName:=ClassName;
if not GetClassInfo(HInstance,PChar(ClName),WndClass) then
begin
WndClass.hInstance := HInstance;
WndClass.lpfnWndProc := @InnerWndProc;
WndClass.lpszClassName := PChar(ClName);
if windows.RegisterClass(WndClass)=0 then Error(SysErrorMessage(GetLastError));
end;
fHwnd:=CreateWindowEx(WS_EX_TOOLWINDOW,WndClass.lpszClassName,'',WS_POPUP,
0,0,0,0,0,0,HInstance,nil);
if fHwnd=0 then Error(SysErrorMessage(GetLastError));
SetWindowLong(fHwnd,GWL_USERDATA,Integer(Self));
end;
Вот такая процедура работает
function InnerWndProc(wnd: hWnd; msg, wParam, lParam: Longint): Longint; stdcall;
var m: TMessage;
res: Integer;
begin
m.Msg:=msg; m.WParam:=wParam; m.LParam:=lParam;
res:=GetWindowLong(wnd,GWL_USERDATA);
if res=0 then
begin
res:=GetLastError;
MessageBox(0,pchar(SysErrorMessage(res)),'',mb_ok);
end
else TMyClass(pointer(res)).WndProc(m);
end;
но стоит закомментить строки с MessageBox - CreateWindowEx возвращает 0, причем код ошибки - тоже 0!
Вот такая конструкция тоже работает
function InnerWndProc(wnd: hWnd; msg, wParam, lParam: Longint): Longint; stdcall;
var m: TMessage;
res: Integer;
begin
m.Msg:=msg; m.WParam:=wParam; m.LParam:=lParam;
end;
но толку с нее, очевидно, нету.
Что тут за грабли могут быть?
SpellCaster (2008-03-26 17:03:00) [21]> [2] Игорь Шевченко © (26.03.08 14:13)
Спасибо, не додумался до такого! Сейчас попробую
> [3] Reindeer Moss Eater © (26.03.08 14:19)
Это как раз то, от чего я хочу уйти
> [6] DVM © (26.03.08 16:20)
У тебя, насколько я понимаю, некий аналог AllocateWnd - то же выделение объекта и тот же хак с подменой адресов. Благодарю, однако хочется без читов :)
----
Блин, я балбес :( совсем забыл WinAPI, там ведь надо дефолтную процедуру вызывать, если мессага не обработана. Конечно, мне приходила WM_NCCREATE, а я на нее ничего не возвращал (т.е. 0 по дефолту), вот окно и удалялось
DVM (2008-03-26 16:50:00) [22]
> Я только вижу добавленую проблему пре переходе на другую
> архитектру процессора.
А какая еще может быть другая архитектура у связки Windows + Delphi, кроме x86-32 и x86-64 ?
guav (2008-03-26 16:38:00) [23]> [5] Игорь Шевченко © (26.03.08 15:35)
Я не про то, где это реализовано, я про саму реализацию.
Чем реализация вроде [6] оказалась лучше реализации вроде [2] ?
Я только вижу добавленую проблему пре переходе на другую архитектру процессора.
DVM (2008-03-26 16:22:00) [24]WndProc - метод класса TSCMPBaseClient в моем случае
DVM (2008-03-26 16:21:00) [25]procedure TSCMPBaseClient.DeAllocWnd;
begin
if FWindowHandle <> 0 then DestroyWindow(FWindowHandle);
VirtualFree(FObjectInstance, 0, MEM_RELEASE);
end;
function WndProc(Wnd: THandle; Msg: Cardinal; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
DVM (2008-03-26 16:20:00) [26]function TSCMPBaseClient.AllocWnd: HWND;
var
wc: TWndClassEx;
WndClassName: string;
begin
result := 0;
FObjectInstance := VirtualAlloc(nil, 12, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
asm
mov EAX, Self
mov ECX, [EAX].TSCMPBaseClient.FObjectInstance
mov word ptr [ECX+0], $6858
mov dword ptr [ECX+2], EAX
mov word ptr [ECX+6], $E950
mov EAX, OFFSET(TSCMPBaseClient.WndProc)
sub EAX, ECX
sub EAX, 12
mov dword ptr [ECX+8], EAX
end;
Str(DWord(Self), WndClassName);
WndClassName := ClassName + ':' + WndClassName;
ZeroMemory(@wc, SizeOf(wc));
wc.cbSize := SizeOf(Wc);
wc.style := CS_HREDRAW or CS_VREDRAW;
wc.hInstance := hInstance;
wc.lpfnWndProc := FObjectInstance;
wc.lpszClassName := pchar(WndClassName);
wc.cbClsExtra := 0;
wc.cbWndExtra := 0;
if Windows.RegisterClassEx(wc) = 0 then exit;
result := CreateWindowEx(WS_EX_TOOLWINDOW, pchar(WndClassName), 'clientwnd', WS_POPUP, 0, 0, 0, 0, 0, 0, HInstance, nil);
end;
Игорь Шевченко (2008-03-26 15:35:00) [27]
> Вот почему разрабочики Delphi не пошли по такому пути, а
> создают код оконного метода динамически ?
А им uses Classes незазорно втыкать
guav (2008-03-26 14:47:00) [28]Вот почему разрабочики Delphi не пошли по такому пути, а создают код оконного метода динамически ?
Reindeer Moss Eater (2008-03-26 14:19:00) [29]AllocateHWnd
Игорь Шевченко (2008-03-26 14:13:00) [30]Уж сколько раз твердили миру:
MyWnd := CreateWindow ('myclass', 'mycaption',
WS_OVERLAPPEDWINDOW,
Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT),
HWND_DESKTOP, 0,
HInstance, Self);
в оконной процедуре:
function MyWndProc (Window: HWND; Message, WParam: Cardinal;
LParam: Cardinal): Longint; stdcall;
var
MainClass : TMainWindow;
...
begin
MainClass := TMainWindow(GetWindowLong (Window, GWL_USERDATA));
case Message of
WM_CREATE:
begin
MainClass := TMainWindow(PCreateStruct(LParam)^.lpCreateParams);
SetWindowLong (Window, GWL_USERDATA, Integer(MainClass));
Result := 0;
end;
...........
end;
SpellCaster (2008-03-26 13:52:00) [31]А, и самое странное в том, что если я это делаю в главном модуле, то все прекрасно действует
function InnerWndProc(wnd: hWnd; msg, wParam, lParam: Longint): Longint; stdcall;
var m: TMessage;
res: Integer;
begin
m.Msg:=msg; m.WParam:=wParam; m.LParam:=lParam;
res:=GetWindowLong(wnd,GWL_USERDATA);
if res=0 then
form1.Memo1.Lines.Add((SysErrorMessage(GetLastError)))
else
tform1(pointer(res)).Somemethod(0);
end;
procedure wnd;
var WndClass: TWndClass;
ClassRegistered: Boolean;
ClName: string;
w: TWndMethod;
fHwnd: HWND;
begin
FillChar(WndClass,SizeOf(WndClass),0);
ClName:='qwe';
ClassRegistered := GetClassInfo(HInstance,PChar(ClName),WndClass);
if not ClassRegistered then
begin
WndClass.hInstance := HInstance;
WndClass.lpfnWndProc := @InnerWndProc;
WndClass.lpszClassName := PChar(ClName);
if windows.RegisterClass(WndClass)=0 then
raise exception.Create(SysErrorMessage(GetLastError));
end;
fHwnd:=CreateWindowEx(WS_EX_TOOLWINDOW, WndClass.lpszClassName,'', WS_MINIMIZE,
0, 0, 0, 0, 0, 0, HInstance, nil);
if fHwnd=0 then
raise exception.Create(SysErrorMessage(GetLastError));
SetWindowLong(fHwnd,GWL_USERDATA,Integer(form1));
sendmessage(fhwnd,WM_MOVE,0,0);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
wnd;
end;