Delphi:检测何时创建了新表单

Delphi:检测何时创建了新表单,delphi,vcl,Delphi,Vcl,我想检测何时创建了新表单 现在我使用屏幕。ActiveFormChange事件并在屏幕中检查新表单。CustomForms但是ActiveFormChange在表单的OnShow事件之后被触发 我甚至想在OnShow被激发之前检测表单。有没有办法在不修改Vcl.Forms单元的情况下执行此操作 我想检测所有表单(还有Delphi模式消息等),因此不可能从自定义类继承所有表单(如果我错了,请纠正我) 或者,是否可以检测到某个t组件.fc组件列表中添加了新组件?在应用程序.OnIdle中跟踪屏幕.C

我想检测何时创建了新表单

现在我使用
屏幕。ActiveFormChange
事件并在
屏幕中检查新表单。CustomForms
但是
ActiveFormChange
在表单的
OnShow
事件之后被触发

我甚至想在
OnShow
被激发之前检测表单。有没有办法在不修改
Vcl.Forms
单元的情况下执行此操作

我想检测所有表单(还有Delphi模式消息等),因此不可能从自定义类继承所有表单(如果我错了,请纠正我)


或者,是否可以检测到某个
t组件.fc组件
列表中添加了新组件?

应用程序.OnIdle中跟踪
屏幕.CustomFormCount

  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;
private
FPrevFormCount:整数;
结束;
过程TForm1.ApplicationEvents1Idle(发送方:TObject;变量Done:Boolean);
开始
如果Screen.CustomFormCount>FPrevFormCount,则
标题:=标题+'+1';
如果Screen.CustomFormCount FPrevFormCount,则
FPrevFormCount:=Screen.CustomFormCount;
结束;
程序TForm1.TestButton1Click(发送方:ToObject);
开始
TForm2.Create(Self.Show);
结束;
程序TForm1.TestButton2单击(发送方:ToObject);
开始
ShowMessage('也可跟踪?');//对
结束;
程序TForm1.TestButton3Click(发送方:ToObject);
开始
OpenDialog1.Execute;//不更新Screen.CustomFormCount
结束;

由Windows管理和显示的本机对话框(
TOpenDialog
TFontDialog
等)是在VCL之外创建的,要跟踪它们,还需要一个黑客单元。试试看。

多亏了David,我找到了一个解决方案:线索是用自己的方法替换
Screen.AddForm
方法。这些SO答案中描述了如何执行此操作:

再次感谢

您可以使用该函数安装
WH\u CBT
钩子,然后必须实现一个函数,并最终截取该钩子的一个可能代码值。在这种情况下,您可以尝试使用
HCBT\u ACTIVATE
HCBT\u CREATEWND

检查此样本是否存在
HCBT\u激活
代码

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.
var
hhk:HHOOK;
函数CBT_FUNC(nCode:Integer;wParam:wParam;lParam:lParam):LRESULT;stdcall;
常数
ClassNameBufferSize=1024;
变量
hWindow:HWND;
RetVal:整数;
ClassNameBuffer:字符的数组[0..ClassNameBufferSize-1];
开始
结果:=CallNextHookEx(hhk、nCode、wParam、lParam);
如果nCode0)那么
开始
RetVal:=GetClassName(wParam、ClassNameBuffer、SizeOf(ClassNameBuffer));
如果RetVal>0,则
开始
//做点什么
OutputDebugString(ClassNameBuffer);
结束;
结束;
结束;
结束;
结束;
程序初始化钩子();
变量
dwid:DWORD;
开始
dwThreadID:=GetCurrentThreadId;
hhk:=SetWindowsHookEx(WH_CBT、@CBT_FUNC、hInstance、dwThreadID);
如果hhk=0,则为r;
结束;
程序KillHook();
开始
如果(hhk 0)那么
UnhookWindowsHookEx(hhk);
结束;
初始化
InitHook();
定稿
基尔胡克();
结束。
注意:如果您使用
HCBT_CREATEWND
代码,您将 拦截系统创建的任何窗口,而不仅仅是“窗体”


通常情况下,您可以控制表单的创建,为什么“需要”进行检测?其实不是。如果调用ShowMessage,对话框窗口将在对话框单元内的某个位置创建。您的代码正在调用ShowMessage,对吗?所以你有控制权。。。您可以为此创建一个包装器函数。(DetectShowMessage或类似的东西)我认为您需要使用类助手来破解该私有方法Mike Lischke的
t管理员使用一系列技术来检测程序中的新表单。(它必须了解新的表单,以便能够为其组件的绘制例程添加工具并应用XP主题。)检查代码以了解它是如何完成的可能是值得的。OnIdle的工作方式与我在问题中描述的不同-OnShow后启动OnIdle(使用主表单测试)。我认为,如David在评论中所建议的那样,挂接VCL是唯一正确的方法。不过还是要感谢您注意本机对话框。您是否尝试过使用该函数安装WH_CBT挂钩?好的,我将发布有关此主题的答案。谢谢,这是一个非常好的方法。尽管它有一些缺点:使用HCBT_ACTIVATE会在OnShow事件后绑定“//do something”代码。当使用HCBT_CREATEWND时,delphi的FindControl(hWindow)找不到任何东西。这是因为
HCBT_CREATEWND
是在创建
HWND
时触发的,VCL还没有机会将其与
TWinControl
对象关联。这绝对正确,但仍然是一个缺点。。。尽管如此,在我看来,这是一个比使用OnIdle更好的解决方案。对于VCL表单,其效果与Screen.ActiveFormChange大致相同。