Delphi: обнаружение создания новой формы

Я хотел бы определить, когда была создана новая форма.

Теперь я использую событие Screen.ActiveFormChange и проверяю наличие новых форм в Screen.CustomForms, но ActiveFormChange запускается после события OnShow формы.

Я хотел бы обнаружить форму еще до того, как OnShow был запущен. Есть ли способ сделать это, не изменяя блок Vcl.Forms?

Я хотел бы обнаружить все формы (также модальные сообщения Delphi и т. Д.), Поэтому наследование всех форм из настраиваемого класса невозможно (поправьте меня, если я ошибаюсь).

Как вариант, можно ли определить, что новый компонент был добавлен в какой-то TComponent.FComponents список?


person oxo    schedule 20.07.2012    source источник
comment
Обычно у вас есть контроль над созданием форм, почему необходимость в обнаружении?   -  person whosrdaddy    schedule 20.07.2012
comment
не совсем. если вы вызываете ShowMessage, диалоговое окно создается где-то внутри модуля Dialogs.   -  person oxo    schedule 20.07.2012
comment
ваш код вызывает showmessage, верно? так что вы ИМЕЕТЕ контроль ... вы можете создать для этого функцию-оболочку. (DetectShowMessage или что-то в этом роде)   -  person whosrdaddy    schedule 20.07.2012
comment
@whosrdaddy Это приводит к очень большому количеству кода оболочки   -  person David Heffernan    schedule 20.07.2012
comment
@whosrdaddy Дэвид прав. ShowMessage был только примером. Этот метод должен использоваться в различных приложениях delphi, которые запускают всевозможные диалоги, поэтому обертка невозможна. Я хочу обнаружить, когда новая форма добавляется в Screen.CustomForms и подключается с помощью stackoverflow.com/questions/8743876/ к ней.   -  person oxo    schedule 20.07.2012
comment
Интересно, что нет Screen.OnFormCreated или чего-то подобного. Было бы, может быть, 5 лишних строк кода в модуле Forms ...   -  person oxo    schedule 20.07.2012
comment
@oxo Я не думаю, что есть что-то встроенное. Вам нужно немного взломать VCL.   -  person David Heffernan    schedule 20.07.2012
comment
@DavidHeffernan: да, я этого ожидал. Я искал в модуле Forms сообщение, которое было бы отправлено объекту Application или что-то в этом роде, но пока ничего не нашел ... Как вы думаете, можно ли достичь моей цели, не изменяя код VCL?   -  person oxo    schedule 20.07.2012
comment
@oxo Вы, конечно, можете сделать это, не изменяя код VCL, но для этого вам может потребоваться применить некоторые ловушки кода времени выполнения!   -  person David Heffernan    schedule 20.07.2012
comment
@DavidHeffernan Проблема в том, что мне нужно подключиться к частной процедуре внутри объекта (TScreen.AddForm). Я уже использовал stackoverflow.com/questions/6905287/ - но только для замены обычной процедуры, а не процедуры объекта. Есть ли аналогичный крючок, который я мог бы применить к TScreen.AddForm?   -  person oxo    schedule 20.07.2012
comment
Я думаю, вам нужно использовать помощник класса, чтобы взломать этот частный метод   -  person David Heffernan    schedule 20.07.2012
comment
Спасибо за информацию - я нашел ваш ответ на SO, попробую.   -  person oxo    schedule 20.07.2012
comment
Да, я помню, как однажды писал здесь что-то на эту тему, но я не мог найти этого. Молодцы, что нашли его! Надеюсь, поможет.   -  person David Heffernan    schedule 20.07.2012
comment
Да, мне удалось это сделать с вашей помощью! Большое спасибо. Вы можете написать свои комментарии в качестве ответа, чтобы я мог принять его и получить несколько баллов :) Ключевым моментом было использование stackoverflow.com/questions/10156430/, чтобы получить TScreen. Указатель AddForm и исправьте его с помощью stackoverflow.com/questions/6905287/ - теперь я могу переопределить TScreen.AddForm! Спасибо за советы!   -  person oxo    schedule 20.07.2012
comment
Я не особо беспокоюсь о том, чтобы написать ответ и получить здесь баллы. Вы всегда можете проголосовать за ответы, которые помогли вам, если хотите.   -  person David Heffernan    schedule 20.07.2012
comment
Майк Лишке TThemeManager использовал ряд методов для обнаружения новых форм в программе. (Он должен был знать о новых формах, чтобы он мог инструментировать процедуры рисования своих компонентов и применять темы XP.) Возможно, стоит изучить этот код, чтобы увидеть, как это делается.   -  person Rob Kennedy    schedule 20.07.2012


Ответы (3)


Вы можете использовать _1 _ для установки WH_CBT Hook, тогда вы должны реализовать обратного вызова CBTProc и, наконец, перехватить одно из возможных значений кода для этого хука. в этом случае вы можете попробовать с HCBT_ACTIVATE или HCBT_CREATEWND.

Проверьте этот образец на наличие кода HCBT_ACTIVATE.

var
 hhk: HHOOK;

function CBT_FUNC(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
const
  ClassNameBufferSize = 1024;
var
 hWindow: HWND;
 RetVal : Integer;
 ClassNameBuffer: Array[0..ClassNameBufferSize-1] of Char;
begin
   Result := CallNextHookEx(hhk, nCode, wParam, lParam);
   if nCode<0 then exit;
   case nCode of
     HCBT_ACTIVATE:
     begin
       hWindow := HWND(wParam);
       if (hWindow>0) then
       begin
          RetVal := GetClassName(wParam, ClassNameBuffer, SizeOf(ClassNameBuffer));
          if RetVal>0 then
          begin
            //do something  
            OutputDebugString(ClassNameBuffer);                     
          end;
       end;
     end;
   end;

end;

Procedure InitHook();
var
  dwThreadID : DWORD;
begin
  dwThreadID := GetCurrentThreadId;
  hhk := SetWindowsHookEx(WH_CBT, @CBT_FUNC, hInstance, dwThreadID);
  if hhk=0 then RaiseLastOSError;
end;

Procedure KillHook();
begin
  if (hhk <> 0) then
    UnhookWindowsHookEx(hhk);
end;

initialization
  InitHook();

finalization
  KillHook();

end.

Примечание: если вы вместо этого используете код HCBT_CREATEWND, вы перехватите любое окно, созданное системой, а не только «формы».

person RRUZ    schedule 20.07.2012
comment
Спасибо, это ОЧЕНЬ хороший подход. Хотя у него есть некоторые недостатки: использование HCBT_ACTIVATE связывает код // делать что-то после события OnShow. А при использовании HCBT_CREATEWND функция FindControl (hWindow) в delphi ничего не находит. - person oxo; 20.07.2012
comment
Это потому, что HCBT_CREATEWND запускается во время создания HWND, прежде чем VCL получит возможность связать его с объектом TWinControl. - person Remy Lebeau; 20.07.2012
comment
Это абсолютно верно, но все же недостаток ... Тем не менее, на мой взгляд, это гораздо лучшее решение, чем использование OnIdle. Для форм VCL он действует примерно так же, как Screen.ActiveFormChange. - person oxo; 21.07.2012

Дорожка Screen.CustomFormCount в Application.OnIdle:

  private
    FPrevFormCount: Integer;
  end;

procedure TForm1.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
begin
  if Screen.CustomFormCount > FPrevFormCount then
    Caption := Caption + ' +1';
  if Screen.CustomFormCount <> FPrevFormCount then
    FPrevFormCount := Screen.CustomFormCount;
end;

procedure TForm1.TestButton1Click(Sender: TObject);
begin
  TForm2.Create(Self).Show;
end;

procedure TForm1.TestButton2Click(Sender: TObject);
begin
  ShowMessage('Also trackable?');  // Yes!
end;

procedure TForm1.TestButton3Click(Sender: TObject);
begin
  OpenDialog1.Execute; // Doesn't update Screen.CustomFormCount
end;

Собственные диалоги, управляемые и отображаемые Windows (TOpenDialog, TFontDialog и т. Д.), Создаются отдельно от VCL, и для их отслеживания вам также понадобится модуль взлома. Тогда попробуйте этот.

person NGLN    schedule 20.07.2012
comment
OnIdle НЕ работает так, как я описал в вопросе - OnIdle запускается после OnShow (проверено с основной формой). Я думаю, что подключение VCL, как предложил Дэвид в комментариях, - единственный правильный способ заставить это работать. Но в любом случае спасибо за внимание к родным диалогам. - person oxo; 20.07.2012

Благодаря Дэвиду я нашел решение: суть в том, чтобы заменить метод Screen.AddForm вашим собственным. Как это сделать, описано в этих SO-ответах:

Еще раз спасибо!

person oxo    schedule 20.07.2012
comment
Вы пытались установить ловушку WH_CBT с помощью SetWindowsHookEx? - person RRUZ; 20.07.2012
comment
Хорошо, я опубликую ответ по этой теме. - person RRUZ; 20.07.2012