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大致相同。