| | | | |
Полный текст материала
Другие публикации автора: Александр Бусаров
Цитата или краткий комментарий: «... Бывают случаи, когда программисту хочется отказаться от VCL. Чаще всего конечно это бывает из-за экономии, чтобы exe получался не такой большой. Но у VCL есть еще одно неприятное ограничение – он однопоточный. Сама Windows прекрасно поддерживает многопоточность, и когда хочется честной параллельной работы окошек, приходится использовать API. ...» |
Важно:- Страница предназначена для обсуждения материала, его содержания, полезности, соответствия действительности и так далее. Смысл не в разборке, а в приближении к истине :о) и пользе для всех.
- Любые другие сообщения или вопросы, а так же личные эмоции в адрес авторов и полемика, не относящаяся к теме обсуждаемого материала, будут удаляться без предупреждения авторов, дабы не мешать жителям нормально общаться.
- При голосовании учитывайте уровень, на который расчитан материал. "Интересность и полезность" имеет смысл оценивать относительно того, кому именно предназначался материал.
- Размер одного сообщений не должен превышать 5К. Если Вам нужно сказать больше, сделайте это за два раза. Или, что в данной ситуации правильнее, напишите свою статью.
Всегда легче осудить сделанное, нежели сделать самому. Поэтому, пожалуйста, соблюдайте правила Королевства и уважайте друг друга.
Добавить свое мнение.
| | Содержит полезные и(или) интересные сведения | [1] | 9 | 100% | | | | Ничего особенно нового и интересного | [2] | 0 | 0% | | | | Написано неверно (обязательно укажите почему) | [3] | 0 | 0% | | Всего проголосовали: 9 | | | Все понятно, материал читается легко | [1] | 9 | 100% | | | | Есть неясности в изложении | [2] | 0 | 0% | | | | Непонятно написано, трудно читается | [3] | 0 | 0% | | Всего проголосовали: 9 |
[Потоки (нити) Threads] [Окна, оконные сообщения]
Отслеживать это обсуждение
Всего сообщений: 7012-08-2012 16:38Вспомнил Сократа, и присоединяюсь к его мнению, что ничего не знаю :)
Пожалуй это лучшая статья которая поможет в оптимизации ПО.
|
|
24-04-2012 22:47
. . .
destructor TWinForm.Destroy;
begin
UnregisterClass(FClass, HInstance);
inherited;
end;
procedure TWinForm.WMDestroy(var Msg: TMessage);
begin
Quit;
Msg.Result:= 0;
end;
procedure TWinForm.WMLButtonDown(var Msg: TMessage);
begin
ShowMessage('TWinForm');
Msg.Result:= 0;
end;
function WndProc(Wnd: HWND; Msg: UINT; wP, lP: LongInt): LRESULT; stdcall; forward;
procedure TWinForm.Init;
var
Desc: TWndClassEx;
begin
FClass:= 'WinForm';
ZeroMemory(@Desc, SizeOf(Desc));
Desc.cbSize := SizeOf(Desc);
Desc.Style := (CS_DBLCLKS or CS_OWNDC or CS_HREDRAW or CS_VREDRAW);
Desc.lpfnWndProc := @WndProc;
Desc.lpszClassName:= FClass;
Desc.hbrBackground:= GetStockObject(BLACK_BRUSH);
Desc.hCursor := LoadCursor(HInstance, IDC_ARROW);
If (RegisterClassEx(Desc) = 0) then Exit;
FWnd:= CreateWindowEx(0, FClass, nil, WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX,
100, 100, 800, 600, GetDesktopWindow, 0, HInstance, nil);
ShowWindow(FWnd, SW_SHOW);
SetForegroundWindow(FWnd);
end;
procedure TWinForm.Update;
var
Msg: TMsg;
IsQuit: boolean;
begin
IsQuit:= False;
while not IsQuit do
begin
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
begin
if Msg.Message = WM_QUIT
then IsQuit:= True
else begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end else
begin
end;
end;
end;
procedure TWinForm.Quit;
begin
PostQuitMessage(0);
end;
function WndProc(Wnd: HWND; Msg: UINT; wP, lP: LongInt): LRESULT; stdcall;
var
i: LongInt;
begin
Result:= 0;
For i:= 0 to List.Count - 1 do
with TEventHandler(List[i]) do
Result:= DispatchEx(Msg, wP, lP);
end;
var
WinForm: TWinForm;
Dummy : TDummy;
function GetWnd: HWND;
begin
Result:= WinForm.FWnd;
end;
begin
List:= TObjectList.Create;
WinForm:= TWinForm.Create;
Dummy:= TDummy.Create;
WinForm.Init;
WinForm.Update;
List.Free;
end.
P.S. Ну ёмаё, целыъ 20 минут отправлял этот текст, я зашел на сайт, а как тут отправлять ответ,
так форма выглядит будто я даже и не заходил, приходиться перелогиниться.
Во 2-й раз кнопка "Отправить" была вдавленной, обновил страницу, опять пришлось перелогиниться, т.к.
опять форма преобрела прежний вид. Отпривил, пишет "Нет ответа на ключевой вопрос", хотя я уже залогинился. Ужос.
Почините это. |
|
24-04-2012 22:44Вот полный пример:
(Отправляю частями, тут ограничения на объем текста)
program my_project_2;
uses
Dialogs,
Contnrs,
HE_Utils,
Messages,
Windows;
type
TEventHandler = class
constructor Create;
public
procedure DefaultHandler(var Message); override;
function DispatchEx(Id: UINT; wP, lP: LongInt): LongInt;
end;
TDummy = class (TEventHandler)
protected
procedure WMLButtonDown(var Msg: TMessage); message WM_LBUTTONDOWN;
end;
TWinForm = class (TEventHandler)
destructor Destroy; override;
private
FWnd : HWND;
FQuit : boolean;
FClass: PChar;
protected
procedure WMDestroy (var Msg: TMessage); message WM_DESTROY;
procedure WMLButtonDown(var Msg: TMessage); message WM_LBUTTONDOWN;
public
procedure Init;
procedure Update;
procedure Update2;
procedure Quit;
end;
var
List: TObjectList;
function GetWnd: HWND; forward;
constructor TEventHandler.Create;
begin
inherited;
List.Add(Self);
end;
procedure TEventHandler.DefaultHandler(var Message);
begin
with TMessage(Message) do
Result:= DefWindowProc(GetWnd, Msg, WParam, lParam)
end;
function TEventHandler.DispatchEx(Id: UINT; wP, lP: LongInt): LongInt;
var
Msg: TMessage;
begin
Msg.Msg := Id;
Msg.wParam:= wP;
Msg.lParam:= lP;
Msg.Result:= 0;
Dispatch(Msg);
Result:= Msg.Result;
end;
procedure TDummy.WMLButtonDown(var Msg: TMessage);
begin
ShowMessage('TDummy');
Msg.Result:= 0;
end;
. . .
Смысл такой, наследуемся от TEventHandler и получаем возможность обрабатывать сообщения исходящие от WndProc. |
|
24-04-2012 09:49сообщение от автора материала 1) Почему мы используем Dispatch, если можно сделать прямой вызов в WndProc:
function TEventHandler.Handler(Msg: UINT; wP, lP: LongInt): LongInt;
Дело в накоплении мессаг в Dispatch? Т.е. оно ждет пока обработается сообщение, потом приступает к следующему?
Дело в устройстве работы этого самого Dispatch. Он тесно связан с message методами. Сами message методы - это по своей сути динамические методы, которые в наследниках можно перекрывать. Индекс динамического метода зависит от констаны после ключевого слова message. Dispatch ищет такой метод, в случае если он существует - то вызывает его, если нет - то вызывает TObject.DefaultHandler.
Это помогает избежать километровых кейсов в оконной функции, а так же дает более гибкие возможности для наследования.
Рекомендую почитать вот эту статью: http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1390
2) Объясните, что за IsGlobalClass.
В системе есть некоторые стандартные контролы, которые можно создавать и использовать. Например кнопки, чекбоксы, радиобуттоны, трекбары, прогессбары, списки и прочее. Создавая такой контрол нас не заботит "отрисовка", обработка кликов и прочее. Все уже реализовано в "чужой" оконной процедуре которая находится в системной длл и все это прекрасно работает. Но в этом случае выходит что мы не можем получать никакие сообщения, в том числе клики по кнопке, изменение позиции трекбара и прочее. Windows в этом случае посылает родительскому окну WM_COMMAND сообщение, которое должно обрабатывать родительское окно. Нам это крайне неудобно, т.к. мы используем ооп подход. Поэтому в случае получения WM_COMMAND мы перенаправляем его самой нашей кнопке. Кроме того может быть полезно получать и другие сообщения, типа WM_MOUSEMOVE. Для этого у кнопки подменяется оконная процедура через SetWindowLong. Так вот, для того чтобы в момент создания базовый класс мог узнать создаем мы стандартный контрол или делаем свой, подменять или не подменять оконную процедуру, регистрировать или не регистрировать оконный класс - для этого и создан динамический метод IsGlobalClass, который для стандартных контролов нужно перекрыть и вернуть true.
3) Ткните, где (место в коде, название методов) обрабатывает сообщение TWinControl.
Обработка сообщений начинается от TControl. Смотрите TControl.WndProc
4) Посмотрите на такую реализацию:
Условия:
- У программы всегда 1 окно.
- 1 нить.
- Должен быть общий класс для обработки сообщений, он может не содержать HWND.
Думаю не правильно сделан цикл в WndProc, как правильно?
Извините, я пока не пойму что должен делать ваш класс.
Во-первых я вижу что TEventHandler наследуюется от TObject, но WndProc оверрайдится. Оно как минимум некомпилябельно.
Во-вторых внутри класса WndProc не вызывается, а значит он должен вызываться снаружи.
Так же я не вижу как использутся DispatchEx. В общем не видя то, как используется класс сложно сказать что либо. Кроме того я вообще не понимаю зачем тут класс. |
|
24-04-2012 09:081) Почему мы используем Dispatch, если можно сделать прямой вызов в WndProc:
function TEventHandler.Handler(Msg: UINT; wP, lP: LongInt): LongInt;
Дело в накоплении мессаг в Dispatch? Т.е. оно ждет пока обработается сообщение, потом приступает к следующему?
2) Объясните, что за IsGlobalClass.
3) Ткните, где (место в коде, название методов) обрабатывает сообщение TWinControl.
4) Посмотрите на такую реализацию:
Условия:
- У программы всегда 1 окно.
- 1 нить.
- Должен быть общий класс для обработки сообщений, он может не содержать HWND.
Думаю не правильно сделан цикл в WndProc, как правильно?
type
TEventHandler = class
constructor Create;
public
procedure DefaultHandler(var Message); override;
function DispatchEx(Id: UINT; wP, lP: LongInt): LongInt;
end;
var
List: TObjectList;
constructor TEventHandler.Create;
begin
inherited;
List.Add(Self);
end;
procedure TEventHandler.DefaultHandler(var Message);
begin
with TMessage(Message) do
Result:= DefWindowProc(GetWnd, Msg, WParam, lParam);
end;
function TEventHandler.DispatchEx(Id: UINT; wP, lP: LongInt): LongInt;
var
Msg: TMessage;
begin
Msg.Msg := Id;
Msg.wParam:= wP;
Msg.lParam:= lP;
Msg.Result:= 0;
Dispatch(Msg);
Result:= Msg.Result;
end;
function WndProc(Wnd: HWND; Msg: UINT; wP, lP: LongInt): LRESULT; stdcall;
var
i: LongInt;
begin
Result:= 0;
For i:= 0 to List.Count - 1 do
with TEventHandler(List[i]) do
Result:= DispatchEx(Msg, wP, lP);
end;
|
|
28-06-2011 15:09идейка насчёт обработки сообщений в объекте реализующем сабклассинг
Например,
procedure WMMouseDown(var msg:TWMMouseDown);message WM_MOUSEDOWN;
procedure TObj.WMMouseDown(var msg:TWMMouseDown);
begin
if Asigned(OnMouseDown) then begin
OnMouseDown(self,...);
end;
end;
- OnMouseDown может вообще не заполняться а место в объекте на неё выделено
- код WMMouseDown обязательно вставится в итоговый файл программы (правда для создания класса окна очень удобно, и уместно - там всё надо предусматривать, но этот код можно в dll-ку выкинуть)
и таких методов в VCL много
если сделать что то вроде
procedure TObj.WMMouseDown(var msg:TWMMessage;OnMouseDown:TMouseEvent);
begin
if Asigned(OnMouseDown) then begin
OnMouseDown(self,...);
end;
end;
procedure Tobj.SetOnMouseDown(Evn:TMouseEvent);
begin
setWMAction(WM_MOUSEDOWN,@WMMouseDown,Evn);
end;
компилятор сможет хорошо выкинуть неиспользуемый код, да и процедура сабклассинга объекта не будет обрабатывать горы пустого кода
|
|
28-06-2011 14:43хм, а если сделать по аналогии с IsDialogMessage, тогда и KeyPreview у главного окна можно сделать, а то VCL кажется что бы это сделать отправляет наверх проверку нажатий клавиш
procedure App_Run;
function IsAccel(hDlg: HWND; lpMsg: TMsg): boolean;
begin
Result:=isKeyCombination(lpMsg);
if Result then begin
Result:=BOOL(SendMessage(hDlg, APPM_TRANSLATEACCEL, 0, LPARAM(@Message)));
end;
end;
var
msg: TMsg;
MainWindow:HWND;
begin
while GetMessage(msg, 0, 0, 0) do
begin
MainWindow:=GetMainWnd(msg.hwnd);
if IsAccel(MainWindow,msg) then continue;
if IsDialogMessage(MainWindow,msg) then continue;
TranslateMessage(msg);
DispatchMessage(msg);
end;
end;
код APPM_TRANSLATEACCEL наверное лучше получать через RegisterWindowMessage, так будет безопаснее, кто его знает что там на WM_APP+ задали |
|
28-06-2011 02:22сообщение от автора материала >>> Я по поводу TApp: вариант ThreadVar вместо списка не рассматривали?
Спасибо. Действительно через threadvar проще и красивее. Не привык просто пользоваться threadvar-ом, поэтому как то не сообразил :) У меня как правило все переменные подобного типа - поля класса наследника TThread, а beginthread использую по великим праздникам. |
|
27-06-2011 15:15кстати, как использовать TranslateAccelerator, особенно в рантайме ?
У меня есть две идеи:
1) списком ускорителей управляет TApp и тогда он вызывает TranslateAccelerator с Msg.hwnd и дескриптором таблицы ускорителей. Минусы: нужно как-то фильтровать, каким окнам какие ускорители транслировать; если этим управляет TApp, то все окна нити будут обрабатывать одни и те же ускорители; либо надо делать инфраструктуру чтобы можно было назначить разным окнам разные ускорители;
2) исходя из недостатков предыдущего пункта, напрашивается идея чтобы ускорителями управляло окно, это вполне логично. Тогда можно применить следующий подход:
APPM_TRANSLATEACCEL = WM_APP + 123;
function TranslateAccel(var Message: TMsg): Boolean;
begin
Result := BOOL(SendMessage(Message.hwnd, APPM_TRANSLATEACCEL, 0, LPARAM(@Message)));
end; и в цыкле выборки сообщений вызывать TranslateAccel. Если окно будет обрабатывать APPM_TRANSLATEACCEL, оно само загрузило все нужные таблицы ускорителей, может вызвать TranslateAccelerator так как ему нужно, может и несколько раз, и вернуть TRUE если обработало сообщение. Сообщение APPM_TRANSLATEACCEL выбрано из диапазона сообщений приложения, поэтому оно гарантировано будет проигнорировано стандартными оконными классами |
|
27-06-2011 13:34Я по поводу TApp: вариант ThreadVar вместо списка не рассматривали?
Я пользовался во весь рост, и сбоев не наблюдал.
Опять же - не нужна никакая синхронизация :) |
|
21-06-2011 21:56немного исправил GetMainWnd, надесь теперь правильно
function GetMainWinOf(p:HWND):HWND;
var s:DWORD;
begin
repeat
Result:=p;
s:=Windows.GetWindowLongW(p,GWL_STYLE);
s :=s and (WS_CHILD or WS_POPUP);
if(s=0)then break;
p:=Windows.GetParent(Result);
until (p=0);
end;
Parent у Диалогов как правило окно |
|
21-06-2011 11:46кстати, как использовать TranslateAccelerator, особенно в рантайме ? |
|
21-06-2011 09:36Ну вобщем то вот что у меня получилось, даже диалоги добавил
более менее серьёзное приложение написать уже получится
http://narod.ru/disk/16706274001/WinTest.rar.html
Вопрос как нормально обрабатывать диалоги без диалоговой процедуры, да и с определением необходимого размера текста - STATIC, например, большие проблемы (InputQuery из WinDlgs) |
|
21-06-2011 07:05А можно ли корректно SetProp выставлять при обработке WM_NCCREATE
Думаю можно. По крайней мере это явно не запрещено в msdn, да и у меня не возникало никогда проблем. При получении WM_NCCREATE окно уже достаточно создано, чтобы можно было с ним работать. Окно получает WM_NCCREATE сразу после создания, потом CreateWindow(Ex) еще с ним работает, и отправляет WM_CREATE непосредственно перед тем как вернуть управление |
|
21-06-2011 06:51сообщение от автора материала >>> Первый WM_GETMINMAXINFO можно, наверное, смело игнорировать - смысла в нём не много (какие ещё размеры окна до его создания?). Больше похоже на какую-то обратную совместимость.
Ок. Впринципе ничего не остается кроме как игнорировать :)
>>> А в WM_NCCREATE есть пользовательский параметр - вот в него ссылку на объект можно пихать.
Да оно то понятно. А можно ли корректно SetProp выставлять при обработке WM_NCCREATE? Окно нормально проинициализировалось? Я просто помню гдето в глубине подсознательно, что что-то нельзя было использовать, как раз из-за того что окно еще не доконца инициализировалось. |
|
21-06-2011 03:05Первый WM_GETMINMAXINFO можно, наверное, смело игнорировать - смысла в нём не много (какие ещё размеры окна до его создания?). Больше похоже на какую-то обратную совместимость. WM_GETMINMAXINFO в любом случае будет отправляться при изменении размеров окна.
А в WM_NCCREATE есть пользовательский параметр - вот в него ссылку на объект можно пихать.
Как-то так:
function WndProcStub(Wnd: HWnd; Msg, wParam, lParam: DWord): Integer; stdcall;
var
Window: TWinAPIWindow;
begin
Result := 0;
if Msg = WM_INITDIALOG then
SetWindowLong(Wnd, GWL_USERDATA, TCreateStruct(lParam^).lpCreateParams);
Window := TWinAPIWindow(Pointer(GetWindowLong(Wnd, GWL_USERDATA)));
if Assigned(Window) then
begin
if Window.FHandle = 0 then
Window.FHandle := Wnd
else
if Dialog.FHandle <> Wnd then
Exit;
Result := Window.DialogProc(Msg, wParam, lParam);
end;
end;
CreateWindowEx(..., Self);
function DialogProcStub(Wnd: HWnd; Msg, wParam, lParam: DWord): Integer; stdcall;
var
Dialog: TWinAPIDialog;
begin
Result := 0;
if Msg = WM_INITDIALOG then
SetWindowLong(Wnd, GWL_USERDATA, lParam);
Dialog := TWinAPIDialog(Pointer(GetWindowLong(Wnd, GWL_USERDATA)));
if Assigned(Dialog) then
begin
if Dialog.FHandle = 0 then
Dialog.FHandle := Wnd
else
if Dialog.FHandle <> Wnd then
Exit;
Result := Dialog.DialogProc(Msg, wParam, lParam);
end;
end;
DialogBoxParam(..., Integer(Self));
|
|
21-06-2011 01:13сообщение от автора материала >>> в CreateWindow есть последний параметр LPVOID lpParam - pointer to window-creation data, он передаётся в оконную процеду с сообщением WM_CREATE
Ну он же идет только с WM_NCCREATE и WM_CREATE. Там еще перед этим всем приходит WM_GETMINMAXINFO и в промежутке WM_NCCALCSIZE.
Фактически WM_GETMINMAXINFO через TObject.Dispatch обработать вообще без шансов. А внутри WM_NCCALCSIZE можно или нет SetProp сделать не совсем понятно... в мсдн-е ничего такого нету, так что нет уверенности что будет работать, хотя у меня вроде работает.
>>> SetWindowSubclass -хорошая функция, всё новое, это хорошо забытое старое :-)
Судя потому что она доступна начиная с WinXP это либо не совсем старое, либо настолько хорошо забытое, что выглядит как новое :) |
|
20-06-2011 22:14Что это за мессаги - не разбирался. Как направить в TObject.Dispatch - тоже не смог пока придумать.
SetWindowSubclass судя по декларации - тут не поможет. У него первый параметр - хендл окна, но оконная функция вызывается прямо внутри CreateWindow, и хендла у меня понятное дело нету.
В общем как быть - пока еще не придумал. Хотелось бы конечно диспатчить эти сообщения.
в CreateWindow есть последний параметр LPVOID lpParam - pointer to window-creation data, он передаётся в оконную процеду с сообщением WM_CREATE, с помощью него можно передать ссылку на объект
а в оконной процедуре на WM_CREATE установить хэндл окна и соответственно вызывать Dispatch объекта
но это сразу закрывает путь к автономному созданию оконных классов, например для выноса их в dll
SetWindowSubclass -хорошая функция, всё новое, это хорошо забытое старое :-)
с заменой прерываний под досом было тож самое, установить легко, а вот вычленить обработчик тяжело, там тож что то подобное придумывали
|
|
20-06-2011 16:54сообщение от автора материала >>> Хотя, опять же, есть например SetWindowSubclass который сводит обе проблемы практически на нет
Вот за это отдельная спасибка. А то я думал как избавится от этих проблем, подозревал что должны быть апи фукнции, но не знал какие.
>>> Думаю это не связано напрямую с проблемами сабклассинга, а скорее всего с идеологией VCL. Ведь от момента создания окна до момента сабклассинга окно получит некоторое количество сообщений, которые не будут диспетчеризированы и обработаны классом
Кстати о диспатче оконных сообщений. Обнаружил что оконная процедура вызывается несколько раз прямо внутри CreateWindow. У меня для окон без WS_VISIBLE приходят:
WM_GETMINMAXINFO
WM_NCCREATE
WM_NCCALCSIZE
WM_CREATE
Есстественно у меня эти сообщения не диспатчатся объекту, т.к. я не знаю как получить ссылку на объект. Ведь CreateWindow на этот момент мне даже хендл еще не вернула :)
Если окно со стилем WS_VISIBLE, то на момент вызова CreateWindow приходит целый букет:
36 WM_GETMINMAXINFO
129 WM_NCCREATE
131 WM_NCCALCSIZE
1 WM_CREATE
24
70
28
134
127
6
641
642
7
133
20
71
5
3
36
129
Что это за мессаги - не разбирался. Как направить в TObject.Dispatch - тоже не смог пока придумать.
SetWindowSubclass судя по декларации - тут не поможет. У него первый параметр - хендл окна, но оконная функция вызывается прямо внутри CreateWindow, и хендла у меня понятное дело нету.
В общем как быть - пока еще не придумал. Хотелось бы конечно диспатчить эти сообщения. |
|
20-06-2011 13:27теперь вопрос, почему сабклассинг это плохо, какие есть мнения?
Думаю плохого ничего :-) Лично мне не нравится что нужно хранить ссылку на оригинальную процедуру + возможные проблемы если надо восстановить исходную процедуру. Хотя, опять же, есть например SetWindowSubclass который сводит обе проблемы практически на нет
почему VCL делает фактически не сабклассинг, а "наследование" от стандартных компонент?
Думаю это не связано напрямую с проблемами сабклассинга, а скорее всего с идеологией VCL. Ведь от момента создания окна до момента сабклассинга окно получит некоторое количество сообщений, которые не будут диспетчеризированы и обработаны классом |
|
20-06-2011 11:03хм, это фича значит
рано запаниковал, моё изучение API продолжается :-)
теперь вопрос, почему сабклассинг это плохо, какие есть мнения?
почему VCL делает фактически не сабклассинг, а "наследование" от стандартных компонент?
|
|
20-06-2011 08:50IsDialogMessage сначала отправляет окну с фокусом ввода сообщение WM_GETDLGCODE и "забирает" клавишы зависимо от результата этого сообщения. Если контрол например вернет DLGC_WANTARROWS, то ему будут отправлены стрелки, если нет - IsDialogMessage использует стрелки для навигации по контролам |
|
20-06-2011 05:11обнаружились выкрутасы IsDialogMessage
она забирает WM_KEYDOWN клавиш
VK_LEFT = 37;
VK_UP = 38;
VK_RIGHT = 39;
VK_DOWN = 40;
а может и ещё каких, как побороть - непонятно
|
|
18-06-2011 11:52Опс, а слона то не заметил
странное человек существо, замечает только то что хочет |
|
18-06-2011 11:25сообщение от автора материала >>> не все обработчики должны возвращать 0 в случае успешной работы
Тут меня уже поправил ~AQUARIUS~. См. пост от 23-05-2011 06:57
Оконная функция сейчас имеет вид:
function WndProc(handle: HWND; Msg: Cardinal; wPrm: WPARAM; lPrm: LPARAM): integer; stdcall;
var wnd: TBaseWindow;
wndMsg: TWindowMsg;
begin
wnd:=TBaseWindow(GetProp(handle, PROP_OBJDATA));
if assigned(wnd) then
begin
wndMsg.Msg:=Msg;
wndMsg.wParam:=wPrm;
wndMsg.lParam:=lPrm;
wndMsg.Result:=-1;
wnd.Dispatch(wndMsg);
Result:=wndMsg.Result;
end
else
begin
Result:=DefWindowProc(handle, msg, wPrm, lPrm);
end;
end; Используются возможнсти TObject.Dispatch; Если TObject.Dispatch; не находит соответсвующий message метод, то вызывается TObject.DefaultHandler внутри которого собственно DefWindowProc |
|
18-06-2011 10:43У нас получилась маленькая недоработка в Dispatch
if wndMsg.Result<>0 then
Result:=DefWindowProc(handle, msg, wPrm, lPrm)
else
Result:=0;
не все обработчики должны возвращать 0 в случае успешной работы
Можно изменить вот так
TWindowMsg = packed record
Msg : Cardinal;
wParam: WPARAM;
lParam: LPARAM;
Result: Integer;
DefProc:function (handle: HWND; Msg: Cardinal; wPrm: WPARAM; lPrm: LPARAM): integer;stdcall;
end;
procedure DefaultHandler(var Message);override;
function WndProc(handle: HWND; Msg: Cardinal; wPrm: WPARAM; lPrm: LPARAM): integer; stdcall;
var O: TObject;
wnd: TWindow;
wndMsg: TWindowMsg;
begin
O:=GetOBJ(Handle);
wndMsg.Msg:=Msg;
wndMsg.wParam:=wPrm;
wndMsg.lParam:=lPrm;
wndMsg.Result:=-1;
wndMsg.DefProc:=DefWindowProc;
O.Dispatch(wndMsg);
Result:=wndMsg.Result;
end;
procedure T~~~.DefaultHandler(var Message);
var P:^TWindowMsg;
begin
P:=@Message;
P.Result:=P.DefProc(Handle,P.Msg,P.wParam,P.lParam);
end;
|
|
17-06-2011 10:05Хотя нет, WM_WINDOWPOSCHANGING/WM_WINDOWPOSCHANGED - не знал что з ними можно отследить изменение z-order
ими самыми, в VCL и KOL именно так и делается, наверх отправляется событие либо сообщением либо напрямую, а я хочу что бы по возможности меньше связываться с описанием собственных сообщений (в идеале - ноль), что бы оболочка была оболочкой, а не надстройкой монстроподобной
WM_PARENTNOTIFY и WM_RESIZE недостаточно
а WM_PARENTNOTIFY с параметрами создания и удаления окна к тому же вызывается до того как это действие произойдёт - но это легко обойти отправкой самому себе POST Message
Это к тому что для LayoutManager-а нужно отлавливать изменения позиций дочерних окон |
|
17-06-2011 09:08Хотя нет, WM_WINDOWPOSCHANGING/WM_WINDOWPOSCHANGED - не знал что з ними можно отследить изменение z-order |
|
17-06-2011 09:06как отследить изменение позиций, ZOrder и размеров дочерних окон не делая их сабклассинг?
Я не знаю как такое сделать даже с сабклассингом :о) |
|
17-06-2011 08:47>>> насчёт Ctl3D прихожу к выводу что это "отсебятина" Borland до появления тем
Не-а... Темы — это "отсебятина" Microsoft после появления Ctl3D :D
ну скажем так
Ctl3D - отсебятина борланда
Темы - отсебятина микрософт
второе что б не заморачиваться будем игнорировать
~AQUARIUS~
Кстати, с помощью SetWindowPos можно поменять z-order окна, tab order системных диалогов вроде как раз на нем базируется; узнать окно, перед которым нужно поместить контрол после пересоздания, можно перед пересозданием с помощью GetWindow с GW_HWNDPREV, но скажу честно - не делал, так что надо экспериментировать :-) - проверил, работает
SetWindowPos(wnd, 0, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE or SWP_NOZORDER or SWP_FRAMECHANGED); - помогает, можно все свойства выставить без пересоздания окна
По этому решил отказаться от пересоздания окон, слишком много проблем при этом возникает.
Добавил что то похожее на LaуoutManager
жалко исходники почему то на мыло не пришли, в понедельник выложу
попутно возник вопрос, как отследить изменение позиций, ZOrder и размеров дочерних окон не делая их сабклассинг? |
|
17-06-2011 00:38>>> насчёт Ctl3D прихожу к выводу что это "отсебятина" Borland до появления тем
Не-а... Темы — это "отсебятина" Microsoft после появления Ctl3D :D |
|
16-06-2011 14:09 А вот ваш чем мне понравился - 100% гарантия что с окном не промажем ;-)
наскоро посмотрел, не учёл что нужно проверить принадлежность нити
насчёт Ctl3D прихожу к выводу что это "отсебятина" Borland до появления тем, в принципе неадекватный вид получался только у EDIT-а, а WS_BORDER и WS_EX_CLIENTEDGE это решают так может и не стоит мучиться, лишний код ни к чему хорошему не приведёт, пусть система сама решает свои проблемы, меньше кода - меньше проблем
Кстати, с помощью SetWindowPos можно поменять z-order окна, tab order системных диалогов вроде как раз на нем базируется; узнать окно, перед которым нужно поместить контрол после пересоздания, можно перед пересозданием с помощью GetWindow с GW_HWNDPREV, но скажу честно - не делал, так что надо экспериментировать :-)
видел описание но сам не дошёл до этого, завтра попробую
я решил разделить классы по функциональности:
первая - поддерживает вызов методов окон,
вторая - сабклассинг в котором вместо HWND ctr на объект из первой
а создание своих классов окон через наследование от объектов первой ветки
Ещё никак не могу придумать как сделать автоматический вызов Destroy объектов второй ветки без использования списков или массивов, пока кроме перехватывания WM_DESTROY ничего на ум не приходит |
|
16-06-2011 13:20чесно искать через GetParent я думаю дольше будет, спасибо за идею
Замерил время выполнения моего и вашего варианта - практически нет разницы. А вот ваш чем мне понравился - 100% гарантия что с окном не промажем ;-) Мой вроде как тоже ничего, но вот тот момент, что окно, возвращаемое GetForegroundWindow, может не пренадлежать нашему процессу и нити, несколько смущал. А если мы взяли окно из сообщения - оно гарантировано принадлежит только нашей нити и за этим следит система.
Ctl3D в VCL сделан через собственную обработку WM_NCPAINT, там суть кода в этой строке:
Ctl3DStyles: array[Boolean] of Integer = (BF_MONO, 0);
DrawEdge(DC, RW, InnerStyles[BevelInner] or OuterStyles[BevelOuter],
Byte(BevelEdges) or EdgeStyles[BevelKind] or Ctl3DStyles[Ctl3D] or BF_ADJUST); Тоесть рамка задается не оконными стилями, а рисуется вручную. Причем сначала выполняется этот код, потом унаследованная обработка, а потом отрисовка темы:
if ThemeControl(Self) and (csNeedsBorderPaint in ControlStyle) then
ThemeServices.PaintBorder(Self, False); 3D-рамка будет видима, только если отключены темы и никто не перекроет WM_NCPAINT :-) Не знаю можно ли оконными стилями добиться такой рамки; наверное можно, но оформить это в таком виде как в VCL сложновато будет.
и стиль поменять SetWindowLong(h,GWL_STYLE,v) приводит к повисанию почему то, если пересоздавать окно как в VCL - TabOrdering меняется
SetWindowLong может зависать если менять стили окна другой нити и эта нить не обрабатывает сообщения, - SetWindowLong отправляет окну WM_STYLECHANGING и потом WM_STYLECHANGED. Больше причин зависания не вижу. Ну и надо не забывать вызывать SetWindowPos(wnd, 0, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE or SWP_NOZORDER or SWP_FRAMECHANGED);
Кстати, с помощью SetWindowPos можно поменять z-order окна, tab order системных диалогов вроде как раз на нем базируется; узнать окно, перед которым нужно поместить контрол после пересоздания, можно перед пересозданием с помощью GetWindow с GW_HWNDPREV, но скажу честно - не делал, так что надо экспериментировать :-) |
|
16-06-2011 11:39идея не моя, на сишном форуме каком то нашёл, когда искал почему табы не работают
посмотрел описание GetForegroundWindow - вобщем то идея здравая, таб ведь на активном окне нажимают :-)
чесно искать через GetParent я думаю дольше будет, спасибо за идею
у меня вот затыки с Ctl3D пошли, никак не могу понять как он по нормальному ставится без замены WM_NCPAINT? весь KOL.pas уже перерыл, как в KOL выставить WS_BORDER и WS_EX_CLIENTEDGE - фигня получается
и стиль поменять SetWindowLong(h,GWL_STYLE,v) приводит к повисанию почему то, если пересоздавать окно как в VCL - TabOrdering меняется
и с темами наверное затык чудовищный будет |
|
15-06-2011 12:48wnd := GetActiveWindow;
GetForegroundWindow |
|
15-06-2011 12:47Kealon
Вы мне хорошую идею подкинули :-) Я у себя делал так:
function IsDialogMessage(var Message: TMsg): Boolean;
var
wnd: HWND;
begin
wnd := GetActiveWindow;
if wnd = 0 then Exit(false);
if GetWindowThreadProcessId(wnd, nil) <> GetCurrentThreadId then Result := false
else Result := Windows.IsDialogMessage(wnd, Message);
end; Тоже работает, но ваш вариант мне кажется лучше |
|
15-06-2011 11:55для поддержки Tab и Shift+Tab процедуру App_Run надо изменить
function GetMainWnd(h:HWND):HWND;
begin
repeat
Result:=h;
h:=GetParent(h);
until h=0;
end;
procedure App_Run;
var
msg: TMsg;
MainWindow:HWND;
begin
while GetMessage(msg, 0, 0, 0) do
begin
MainWindow:=GetMainWnd(msg.hwnd);
if IsDialogMessage(MainWindow,msg) then continue;
TranslateMessage(msg);
DispatchMessage(msg);
end;
end;
|
|
13-06-2011 10:24Т.е. просто хочу этим сказать, что библиотека MrShoor более пригодна к практическому использованию чем эта :-)
VCL куда более пригодна :-)
а в этой кроме создания окна пока ничего нет, но там это можно сделать, что будет очень полезно для развития в этом её плюс
насчёт 64-битных версий всё куда хуже, метод Dispatch тоже не расчитан на 64-битную версию, 64-битной версии дельфи пока нет, а если охота всё таки писать под 64-бита, надо писать под FPC, там он уже есть
и что бы не наткнуться на проблемы с 64-битным процессором, желательно локализовать вещи привязанные к разрядности |
|
13-06-2011 05:53похожая библиотечка
По сути та же VCL, даже немного хуже - лучше бы автор перетянул полностью код MakeObjectInstance/FreeObjectInstance, - выделять на каждую оконную процедуру целую страницу и ипользовать только 12 байт не есть хорошо. Одно родительское окно, пусть еще 15-20 контролов - и программа уже выделила больше 1 Мб вместо реально нужных 500 байт. Не понравилось также использование ассемблера, то, что используется общаяя оконная процедура - во-первых, прототипы оконной и диалоговой процедуры хоть и похожы, но немного не похожы - в моем понимании BOOL <> LRESULT (при переходе на х64 думаю так и будет - результатом иногда нужно возвращать хэндлы, а они "подростут" до 8 байт), ну и неправильно сделана обработка сообщений для диалогов - сообщения WM_CHARTOITEM, WM_COMPAREITEM, WM_CTLCOLORBTN, WM_CTLCOLORDLG, WM_CTLCOLOREDIT, WM_CTLCOLORLISTBOX, WM_CTLCOLORSCROLLBAR, WM_CTLCOLORSTATIC, WM_INITDIALOG, WM_QUERYDRAGICON, WM_VKEYTOITEM для диалогов обрабатываются особо, для возвращения результата из всех остальных нужно вызывать SetWindowLongPtr с DWL_MSGRESULT. Т.е. просто хочу этим сказать, что библиотека MrShoor более пригодна к практическому использованию чем эта :-) |
|
13-06-2011 05:13
12-06-2011 02:19с наследованием от стандартных классов получается довольно интересно
http://narod.ru/disk/15717281001/WinTest.rar.html
в принципе получается простая обёртка для создания классов окон, для минимальных приложений сойдёт
для чего то серьёзного надо придумать как оборачивать в классы получаемые хэндлы окон
|
|
11-06-2011 14:03
11-06-2011 12:53сообщение от автора материала Ну вот, меня опередили... :/ |
|
11-06-2011 12:51сообщение от автора материала >>> в SetWindowLongPtr подкавыка, плохо английский знаю
Там написано, что данные кешируются, и изменение SetWindowLongPtr вступит в силу только после вызова SetWindowPos. |
|
11-06-2011 12:43Certain window data is cached, so changes you make using SetWindowLongPtr will not take effect until you call the SetWindowPos function.
Это касается только GWL_STYLE и GWL_EXSTYLE. Об этом упоминается в описании флагов SetWindowPos:
SWP_FRAMECHANGED
Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed |
|
11-06-2011 12:32в SetWindowLongPtr подкавыка, плохо английский знаю
Remarks
Certain window data is cached, so changes you make using SetWindowLongPtr will not take effect until you call the SetWindowPos function. |
|
11-06-2011 12:26забыл в одном месте про критическую секцию, подправил
http://narod.ru/disk/15691248001/WinTest.rar.html
WM_DESTROY:
begin
EnterCriticalSection(FSection);
try
b:=HandleList.FindKey(Handle,P);
finally
LeaveCriticalSection(FSection);
end;
if b then begin
O:=P;
EnterCriticalSection(FSection);
try
HandleList.Remove(handle);
finally
LeaveCriticalSection(FSection);
end;
FreeAndNil(O);
end;
Result:=Params.DefProc(handle,Msg,wPrm,lPrm);
end;
|
|
11-06-2011 12:22А хранить указатели гораздо удобнее не через GWL_USERDATA, а через GetProp SetProp (правда появляется ограничение на Win2000 и выше)
SetProp медленее SetWindowLongPtr примерно в 2.5 раза, GetProp от GetWindowLongPtr - в три раза. ИМХО по удобству - как одно, так и другое почти одинаково |
|
11-06-2011 12:05
11-06-2011 10:52сообщение от автора материала kealon
Вы хотите сделать сразу 2 вещи, но они не получатся ну никак. В моем случае механизм можно назвать инкапсуляцией апишных окон. А удобство моего подхода заключается в том что создаем новый класс наследованием:
TMyNewForm = class (TWindow)
end;
Вы хотите чтобы это можно было запихнуть в длл, с дальше CreateWindow и использовать как глобальный класс. А это совершенно другое. У нас не будет вышеописанной возможности пронаследоваться и впихнуть свой код, да и как бы нам вообще объект не нужен этот.
Посему если хочется делать глобалклассы нужно походить совершенно с другой стороны, и писать новый модуль. Нам вообще ненужен Application, т.к. нам ненужен оконный цикл. Нам нужна лишь правильно написанная оконная функция, и создавать/удалять объекты именно в ней.
А хранить указатели гораздо удобнее не через GWL_USERDATA, а через GetProp SetProp (правда появляется ограничение на Win2000 и выше) |
|
11-06-2011 09:17а вот это не получится, для Get(Set)ClassLong нужен хэндл окна, а для того что бы его содать нужно описание класса окна, которое уже содержит записанные данные - патовая ситуация
Хм, а если сделать так:
1) регистрируем оконный класс, оконная процедура = DefWindowProc;
2) не отходя от кассы создаем окно нашего класса;
3) SetClassLong + 0 (ну вообще нужное смещение - для примера припустим что указатель на класс будет хранится с индексом 0);
3) SetClassLong + GCL_WNDPROC - меняем оконную проседуру на нужную
4) уничтожаем окно.
Не такая уж и патовая получилась эта ситуация |
|
11-06-2011 08:28А делая подобные участки на ассемблере вы делаете немного непереносимый код - все ожидают появления 64-битного компилятора :-) а этот код либо не откомпилируется, либо будет работать неправильно.
ну это дело поправимое, просто сделать код для 32-биного и для 64-битного режима, одна вставка легко конролируется, это проще чем воротить для каждого класса свою функцию окна - в объектный подход это не вписывается, ведь объектную обёртку делаем ???
Чтобы не делать несколько оконных процедур, надо еще добавить SizeOf(Pointer) байт к дополнительным байтам класса и записать туда ссылку на класс, в WM_CREATE получить ее с помощью GetClassLong и использовать для создания обьекта
а вот это не получится, для Get(Set)ClassLong нужен хэндл окна, а для того что бы его содать нужно описание класса окна, которое уже содержит записанные данные - патовая ситуация |
|
11-06-2011 07:33с одной стороны объект желательно создавать в функции окна на WM_CREATE и удалять там же на WM_DESTROY, и это будет правильно - окно созданное таким образом можно использовать в другой программе через CreateWindow и CreateWindowEx и в диалогах. Но тогда встаёт вопрос как в оконной функции по хэндлу окна получить наш объект (в принципе ситуация решаемая - те же красно-чёрные деревья, можно использовать для этих целей Set(Get)WindowLong, но нет никаких гарантий что приложение само не захочет использовать эти структуры и не перетрёт их, для своих целей)
Для хранения ссылки на обьект совсем необязательно использовать GWL_USERDATA - можно при регистрации класса добавить еще SizeOf(Pointer) байт к дополнительным байтам окна и использовать их. Получить же обьект по хендлу окна можно в любой момент с помощью GetWindowLongPtr. Если же приложение случайно перетрет эти данные - это ошибка, примерно как любая другая ошибка при работе с указателями или еще с чем либо - ее надо просто не допускать, либо исправлять. Наконец, если другое приложение изменит эти данные - от этого ведь вообще никто не застрахован, верно?
А делая подобные участки на ассемблере вы делаете немного непереносимый код - все ожидают появления 64-битного компилятора :-) а этот код либо не откомпилируется, либо будет работать неправильно. Ну и поддерживать его будет тяжело. Чтобы не делать несколько оконных процедур, надо еще добавить SizeOf(Pointer) байт к дополнительным байтам класса и записать туда ссылку на класс, в WM_CREATE получить ее с помощью GetClassLong и использовать для создания обьекта |
|
11-06-2011 07:20>>> Кстати, вот такое удаление объектов как то не очень
Честно говоря не вижу ничего костыльного в таком уничтожении объекта. Что объект крайне не рекомендуется уничтожать внтури методов самого объекта - вполне логично. Мы же перенаправляем все сообщения в message методы нашего объекта. С таким же успехом message методы можно считать костылем, обрабатывать же их должен Application.
Так что имхо - это не костыль, а механизм уничтожения объектов.
вот кстати в VCL с этим и идут все беды, когда из длл формы пытаются пришить к основному приложению
Например, удаление окон подсказок в VCL идёт двояко: в приложение - в App, в dll - создают дополнительный поток |
|
11-06-2011 07:16я полез глубже
я пытался использовать подсчёт ссылок для автоматической дерегистрации класса окна, соответственно как удалился объект на WM_DESTROY он удалился а это вызвало где-то ошибку
ситуация с созданием своих оконных классов выходит двоякая:
с одной стороны объект желательно создавать в функции окна на WM_CREATE и удалять там же на WM_DESTROY, и это будет правильно - окно созданное таким образом можно использовать в другой программе через CreateWindow и CreateWindowEx и в диалогах. Но тогда встаёт вопрос как в оконной функции по хэндлу окна получить наш объект (в принципе ситуация решаемая - те же красно-чёрные деревья, можно использовать для этих целей Set(Get)WindowLong, но нет никаких гарантий что приложение само не захочет использовать эти структуры и не перетрёт их, для своих целей)
с другой стороны в прикладной программе удобно сначала создать объект а потом прикрепить к нему хэндл окна (в VCL в принципе так и делается), так же можно на каждый объект создавать свой класс окна, например с именем format('%s%p',[ClassName,Pointer(self)]), более менее добьёмся уникальности названий. Но тогда всю работу по удалению объектов придётся брать на себя - что VCL в принципе и делает
а не писать функцию окна для каждого класса довольно легко, обычный полиморф (в VCL так и сделано), добавить динамическое создание например такого кода
asm
pop EAX //адресс возврат
push $FFFF //здесь заменяется на адресса объекта
push EAX //вернём адресс возврата
push $FFFE //здесь на адресс функции метода класса
ret 0 // переход на него, с джампами возиться муторно
end;
Type
TClassWndProc=function (handle: HWND; Msg: Cardinal; wPrm: WPARAM; lPrm: LPARAM): integer of object; stdcall;
|
|
11-06-2011 07:00сообщение от автора материала >>> Кстати, вот такое удаление объектов как то не очень
Честно говоря не вижу ничего костыльного в таком уничтожении объекта. Что объект крайне не рекомендуется уничтожать внтури методов самого объекта - вполне логично. Мы же перенаправляем все сообщения в message методы нашего объекта. С таким же успехом message методы можно считать костылем, обрабатывать же их должен Application.
Так что имхо - это не костыль, а механизм уничтожения объектов. |
|
11-06-2011 05:34Можно переложить уничтожение окна на оконную процедуру:
function ConcreteWindowProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM;
lParam: LPARAM): LRESULT; stdcall;
var
Inst: TObject;
Msg: TMessage;
begin
if uMsg = WM_DESTROYWND then
begin
DestroyWindow(hWnd);
Result := 0;
Exit;
end;
if uMsg = WM_NCCREATE then ...
* * *
procedure TWindow.WMDestroy(var msg: TWindowMsg);
begin
PostMessage(Handle, WM_DESTROYWND, 0, 0);
msg.Result:=0;
end; Тогда нет необходимости вносить изменения в цыкл выборки сообщений |
|
11-06-2011 04:56kealon
Делайте для каждого оконного класса свою оконную процедуру - и не будет никаких проблем. Получив WM_NCCREATE создаем обьект, WM_NCDESTROY - уничтожаем:
function ConcreteWindowProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM;
lParam: LPARAM): LRESULT; stdcall;
var
Inst: TObject;
Msg: TMessage;
begin
if uMsg = WM_NCCREATE then
begin
Inst := TMyConcreteWindowClass.Create;
TMyConcreteWindowClass(Inst).FHandle := hwnd;
SetWindowLongPtr(hWnd, GWL_USERDATA, Inst);
end;
Obj := Pointer(GetClassLong(hWnd, GWL_USERDATA));
if Assigned(Obj) then
begin
Msg.Msg := uMsg;
Msg.WParam := wParam;
Msg.LParam := lParam;
Msg.Result := 0;
Obj.Dispatch(Msg);
Result := Msg.Result;
end else Result := DefWindowProc(hWnd, uMsg, wParam, lParam);
if uMsg = WM_NCDESTROY then
begin
Inst := Pointer(SetWindowLongPtr(hWnd, GWL_USERDATA, NativeInt(nil)));
FreeAndNil(Inst);
end;
end; Тогда окна можно будет уничтожать с помощью DestroyWindow. Но даже в этом случае обьект не сможет уничтожить себя сам - внутри DestroyWindow оконная процедура получит WM_NCDESTROY и разрушит обьект, и после этого продолжится выполнение методов уничтоженного обьекта. Правда в таком варианте можно обойтись без списка обьектов, но уничтожать окна все равно придется Application'у:
const
WM_DESTROYWND = WM_APP + 11;
procedure TApp.Run;
var
msg: TMsg;
begin
while GetMessage(msg, 0, 0, 0) do
begin
if (Msg.message = WM_DESTROYWND) and (Msg.hwnd = 0) then
DestroyWindow(Msg.wParam);
TranslateMessage(msg);
* * *
end;
procedure TWindow.WMDestroy(var msg: TWindowMsg);
begin
PostThreadMessage(GetCurrentThreadId, WM_DESTROYWND, Handle, 0);
msg.Result:=0;
end; |
|
11-06-2011 02:39Кстати, вот такое удаление объектов как то не очень
for i := 0 to Length(FDestroyArr) - 1 do FDestroyArr[i].Free;
SetLength(FDestroyArr, 0);
windows как то же удаляет свои объекты, без таких костылей
тем более если они в другой dll созданы
Hint-ы в VCL через такой же костыль сделаны |
|
10-06-2011 18:27сообщение от автора материала Все будет, и без массивов, и с глобальными классами. Сначала хотел сделать дополнение к этой статье, но вышло много мелких изменений в модуле. Сейчас готовлю статью "Окна, WinAPI, Delphi. Работа над ошибками". Статья впринципе уже готова, надо только перечитать ее и в некоторых местах немного поправить, но руки не доходят, работы поднавалилось. |
|
10-06-2011 06:47При регистрации класса добавьте стиль CS_GLOBALCLASS - это сделает оконный класс доступным во всех модулях приложения
~AQUARIUS~
Это я знаю, просто очень бы хотелось видеть обёртку которая это делает сама. |
|
10-06-2011 02:40Kealon
При регистрации класса добавьте стиль CS_GLOBALCLASS - это сделает оконный класс доступным во всех модулях приложения |
|
10-06-2011 01:10Было бы неплохо что бы обёртка поддерживала регистрацию классов windows
Н-р, написал очень неплохой компонентик и вынес его основные функции в dll
library m1;
type
TMySuperComponent=class(TAnyComponent)
...
end;
begin
TMySuperComponent.RegisterAs('MySuperComponent');
end;
а в коде приложения его использовать как стандартные классы windows - BUTTON, RICHEDIT и т.д.
|
|
10-06-2011 00:36а нельзя сделать без массивов окон и массивов APP?
что бы использование было в контексте ООП, наследованием
TMyWin1=class(TWin)
end;
TMyWin2=class(TWin)
end;
TMyApp1=class(TApp)
w1:TMyWin1;
w2:TMyWin1;
constructor Create;
destructor Destroy;
end;
...
constructor TMyApp1.Create;
begin
inherited;
w1:=TMyWin1.Create;
w2:=TMyWin1.Create;
end;
destructor TMyApp1.Destroy;
begin
w1.free;
w2.free;
inherited;
end;
...
|
|
23-05-2011 13:19>>> Что делать с этой статьей (править или нет) не знаю.
На Ваше усмотрение.
Если все же решите, что надо исправить, то бросьте исправление мне в личку.
Смотрите, может быть, имеет смысл ограничиться приписыванием текста в конец материала. Типа, багфикс ;-)
Ну или правка самого материала. Лишь бы я смог понять, что на что нужна изменить :-) |
|
23-05-2011 09:34сообщение от автора материала >>> Есть замечание по поводу оконной процедуры, а именно фрагмента:
Полностью согласен, уже столкнулся вот с этим
Что делать с этой статьей (править или нет) не знаю. В новой статье этого "бага" уже не будет. |
|
23-05-2011 06:57Есть замечание по поводу оконной процедуры, а именно фрагмента:
if wndMsg.Result<>0 then
Result:=DefWindowProc(handle, msg, wPrm, lPrm)
else
Result:=0; Использовать 0 в качестве признака, не было ли обработано сообщение, нельзя - некотороые сообщения используют возврщаемое значение напрямую ( например , или это ), следовательно, рискуете получить в лучшем случае странное поведение, в худшем - утечки ресурсов. ИМХО лучше DefWindowProc вызывать из перекрытого DefaultHandler, а в оконной процедуре просто вызывать wnd.Dispatch (как это сделано в TWinControl). Тогда в DefWindowProc попадут действительно только необработанные классом сообщения |
|
20-05-2011 10:27сообщение от автора материала >>> Все вопросы, связянные с многопоточностью, тем или иным образом разруливаются средствами VCL без необходимости создания окон на WINAPI, причем достаточно честно;
Тоже не совсем понял. Задача была показать как честно работать в нескольких потоках с окошками. С VCL это можно разрулить только через Synchronize, а Synсhronize - это SendMessage окну и ожидание когда он его обработает. Я лично очень не люблю использовать этот костыль. Извините, по другому это сложно как то назвать.
>>> Использование какого-нибудь TXXXList, на мой взгляд, было бы проще и нагляднее. Имхо, избегать стандартных модулей Classes, SysUtils и т.п. не следует, ведь без них в реальных проектах все равно не обойтись;
Я в статье показал лишь пример написания. У меня было 2 цели. Минимизация (пробую себя в демосцене, если честно) + многопоточность (хочу в дальнейшем эти модули использовать для еще одного своего проекта). А статья была создана лишь чтобы донести идею.
>>> - я бы порекомендовал при работе с критическими секциями, особенно в предлагаемых примерах, использовать TRY...FINALLY, поскольку сложно предположить, кто и каким образом решит доработать этот код в дальнейшем;
Я думаю выложить в скором времени доработанный модуль с описанием классов и оформить все это в статью. Но подумаю об использований TRY...FINALLY.
>>> вы проверяли потокобезопасность методов RegClass и UnregClass в случае, если одновременно параллельно создаются и уничтожаются окна одного и того же класса? Ведь проверка GetClassInfoEx может и проскочить, но что будет дальше - вызывает вопросы;
А вот за этот нюанс особое спасибо. Сейчас пойду почитаю мсдн, подумаю.
>>> было бы здорово видеть более функциональные примеры, с соответствующей иерархией модулей и классов, но при этом - не перестараться, иначе идея минимализма - исчезнет :)
В следующей статье. Постараюсь реализовать большинство стандартных классов Windows и соответственно подкрепить более функциональными примерами. |
|
19-05-2011 23:38>>> Все вопросы, связянные с многопоточностью, тем или иным образом разруливаются средствами VCL без необходимости создания окон на WINAPI, причем достаточно честно
Поясните это утверждение, а то пока это выглядит несколько непонятно. Вроде бы, два разных окна в двух разных потоках средствами VCL создать нельзя.
* * *
Я тоже отметил тот момент, что в статье смешались две идеи: окна в параллельных потоках и минимализм. Но, имхо, это не ухудшило материал. А если и ухудшило, то незначительно. |
|
19-05-2011 16:25В целом интересный и полезный материал. Изложенные в нем идеи, в частности предложения по ООП-программированию, могут использоваться энтузиастами в своих проектах.
В качестве замечаний и предложений хотелось бы отметить:
- когда хочется честной параллельной работы окошек, приходится использовать API.
Все вопросы, связянные с многопоточностью, тем или иным образом разруливаются средствами VCL без необходимости создания окон на WINAPI, причем достаточно честно;
- нужно хранить список, у нас это будет массив, назовем его: Applications: array of TApp
Использование какого-нибудь TXXXList, на мой взгляд, было бы проще и нагляднее. Имхо, избегать стандартных модулей Classes, SysUtils и т.п. не следует, ведь без них в реальных проектах все равно не обойтись;
- я бы порекомендовал при работе с критическими секциями, особенно в предлагаемых примерах, использовать TRY...FINALLY, поскольку сложно предположить, кто и каким образом решит доработать этот код в дальнейшем;
- вы проверяли потокобезопасность методов RegClass и UnregClass в случае, если одновременно параллельно создаются и уничтожаются окна одного и того же класса? Ведь проверка GetClassInfoEx может и проскочить, но что будет дальше - вызывает вопросы;
- было бы здорово видеть более функциональные примеры, с соответствующей иерархией модулей и классов, но при этом - не перестараться, иначе идея минимализма - исчезнет :)
|
|
|
|