Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Подземелье Магов
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  20:19[Войти] | [Зарегистрироваться]

Обсуждение материала
Окна, WinAPI, Delphi
Полный текст материала


Другие публикации автора: Александр Бусаров

Цитата или краткий комментарий:

«... Бывают случаи, когда программисту хочется отказаться от VCL. Чаще всего конечно это бывает из-за экономии, чтобы exe получался не такой большой. Но у VCL есть еще одно неприятное ограничение – он однопоточный. Сама Windows прекрасно поддерживает многопоточность, и когда хочется честной параллельной работы окошек, приходится использовать API. ...»


Важно:
  • Страница предназначена для обсуждения материала, его содержания, полезности, соответствия действительности и так далее. Смысл не в разборке, а в приближении к истине :о) и пользе для всех.
  • Любые другие сообщения или вопросы, а так же личные эмоции в адрес авторов и полемика, не относящаяся к теме обсуждаемого материала, будут удаляться без предупреждения авторов, дабы не мешать жителям нормально общаться.
  • При голосовании учитывайте уровень, на который расчитан материал. "Интересность и полезность" имеет смысл оценивать относительно того, кому именно предназначался материал.
  • Размер одного сообщений не должен превышать 5К. Если Вам нужно сказать больше, сделайте это за два раза. Или, что в данной ситуации правильнее, напишите свою статью.
Всегда легче осудить сделанное, нежели сделать самому. Поэтому, пожалуйста, соблюдайте правила Королевства и уважайте друг друга.



Добавить свое мнение.

Результаты голосования
Оценка содержания

  Содержит полезные и(или) интересные сведения
[1]9100%
 
  Ничего особенно нового и интересного
[2]00%
 
  Написано неверно (обязательно укажите почему)
[3]00%
 
Всего проголосовали: 9

Оценка стиля изложения

  Все понятно, материал читается легко
[1]9100%
 
  Есть неясности в изложении
[2]00%
 
  Непонятно написано, трудно читается
[3]00%
 
Всего проголосовали: 9




Смотрите также материалы по темам:
[Потоки (нити) Threads] [Окна, оконные сообщения]

Комментарии жителей
Отслеживать это обсуждение

Всего сообщений: 70

12-08-2012 16:38
Вспомнил Сократа, и присоединяюсь к его мнению, что ничего не знаю :)
Пожалуй это лучшая статья которая поможет в оптимизации ПО.


24-04-2012 22:47



. . .

{ TWinForm }

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;

{ TEventHandler }

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;

{ TDummy }

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:08
1) Почему мы используем 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; // Глобальный контейнер TEventHandler'ов.


{ TEventHandler }

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); // 1 окно.
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;


 exo


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 есть пользовательский параметр - вот в него ссылку на объект можно пихать.

Как-то так:

// CreateWindow(Ex):
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);

// DialogBox(Param/Indirect/IndirectParam):
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:50
IsDialogMessage сначала отправляет окну с фокусом ввода сообщение 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
 Geo


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:48
wnd := GetActiveWindow;
GetForegroundWindow


15-06-2011 12:47
Kealon
Вы мне хорошую идею подкинули :-) Я у себя делал так:

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
http://www.delphisources.ru/pages/faq/base/win32api.html

похожая библиотечка


12-06-2011 02:19
с наследованием от стандартных классов получается довольно интересно
http://narod.ru/disk/15717281001/WinTest.rar.html

в принципе получается простая обёртка для создания классов окон, для минимальных приложений сойдёт
для чего то серьёзного надо придумать как оборачивать в классы получаемые хэндлы окон


11-06-2011 14:03
действительно проще хранить в структуре окна указатель на объект

http://narod.ru/disk/15697773001/WinTest.rar.html
тогда можно вообще избежать всяких списков (вернее переложить эту работу на windows)


11-06-2011 12:53
сообщение от автора материала
Ну вот, меня опередили... :/


11-06-2011 12:51
сообщение от автора материала
>>> в SetWindowLongPtr  подкавыка, плохо английский знаю
Там написано, что данные кешируются, и изменение SetWindowLongPtr вступит в силу только после вызова SetWindowPos.


11-06-2011 12:43
Certain 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
вот попробовал реализовать, правда с объектамы самим создавать нельзя
http://narod.ru/disk/15689614001/WinTest.rar.html


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:56
kealon
Делайте для каждого оконного класса свою оконную процедуру - и не будет никаких проблем. Получив 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:40
Kealon
При регистрации класса добавьте стиль 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
>>> Что делать с этой статьей (править или нет) не знаю.
На Ваше усмотрение.
Если все же решите, что надо исправить, то бросьте исправление мне в личку.
Смотрите, может быть, имеет смысл ограничиться приписыванием текста в конец материала. Типа, багфикс ;-)
Ну или правка самого материала. Лишь бы я смог понять, что на что нужна изменить :-)
 Geo


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 создать нельзя.

* * *
Я тоже отметил тот момент, что в статье смешались две идеи: окна в параллельных потоках и минимализм. Но, имхо, это не ухудшило материал. А если и ухудшило, то незначительно.
 Geo


19-05-2011 16:25
В целом интересный и полезный материал. Изложенные в нем идеи, в частности предложения по ООП-программированию, могут использоваться энтузиастами в своих проектах.

В качестве замечаний и предложений хотелось бы отметить:

- когда хочется честной параллельной работы окошек, приходится использовать API.
Все вопросы, связянные с многопоточностью, тем или иным образом разруливаются средствами VCL без необходимости создания окон на WINAPI, причем достаточно честно;

- нужно хранить список, у нас это будет массив, назовем его: Applications: array of TApp

Использование какого-нибудь TXXXList, на мой взгляд, было бы проще и нагляднее. Имхо, избегать стандартных модулей Classes, SysUtils и т.п. не следует, ведь без них в реальных проектах все равно не обойтись;

- я бы порекомендовал при работе с критическими секциями, особенно в предлагаемых примерах, использовать TRY...FINALLY, поскольку сложно предположить, кто и каким образом решит доработать этот код в дальнейшем;

- вы проверяли потокобезопасность методов RegClass и UnregClass в случае, если одновременно параллельно создаются и уничтожаются окна одного и того же класса? Ведь проверка GetClassInfoEx может и проскочить, но что будет дальше - вызывает вопросы;

- было бы здорово видеть более функциональные примеры, с соответствующей иерархией модулей и классов, но при этом - не перестараться, иначе идея минимализма - исчезнет :)



Добавьте свое cообщение

Вашe имя:  [Войти]
Ваш адрес (e-mail):На Королевстве все адреса защищаются от спам-роботов
контрольный вопрос:
Жил-был у бабушки серенький КТО?
в качестве ответа на вопрос или загадку следует давать только одно слово в именительном падеже и именно в такой форме, как оно используется в оригинале.
Надоело отвечать на странные вопросы? Зарегистрируйтесь на сайте.

Оценка содержания
 
Содержит полезные и(или) интересные сведения
 
Ничего особенно нового и интересного
 
Написано неверно (обязательно укажите почему)


Оценка стиля изложения
 
Все понятно, материал читается легко
 
Есть неясности в изложении
 
Непонятно написано, трудно читается

Текст:
Жирный шрифт  Наклонный шрифт  Подчеркнутый шрифт  Выравнивание по центру  Список  Заголовок  Разделительная линия  Код  Маленький шрифт  Крупный шрифт  Цитирование блока текста  Строчное цитирование
  • вопрос Круглого стола № XXX

  • вопрос № YYY в тесте № XXX Рыцарской Квинтаны

  • сообщение № YYY в теме № XXX Базарной площади
  • обсуждение темы № YYY Базарной площади
  •  
     Правила оформления сообщений на Королевстве
      
    Время на сайте: GMT минус 5 часов

    Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
    Функция может не работать в некоторых версиях броузеров.

    Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
    Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

     
    © При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
    Все используемые на сайте торговые марки являются собственностью их производителей.

    Яндекс цитирования