【发布时间】:2011-01-06 15:27:30
【问题描述】:
Mike Lischke 的 TThemeServices 子类 Application.Handle,以便在主题更改时可以接收来自 Windows(即 WM_THEMECHANGED)的广播通知。
它继承了Application 对象的窗口:
FWindowHandle := Application.Handle;
if FWindowHandle <> 0 then
begin
// If a window handle is given then subclass the window to get notified about theme changes.
{$ifdef COMPILER_6_UP}
FObjectInstance := Classes.MakeObjectInstance(WindowProc);
{$else}
FObjectInstance := MakeObjectInstance(WindowProc);
{$endif COMPILER_6_UP}
FDefWindowProc := Pointer(GetWindowLong(FWindowHandle, GWL_WNDPROC));
SetWindowLong(FWindowHandle, GWL_WNDPROC, Integer(FObjectInstance));
end;
然后,子类化的窗口过程会按照应有的方式处理WM_DESTROY 消息,删除它的子类,然后传递WM_DESTROY 消息:
procedure TThemeServices.WindowProc(var Message: TMessage);
begin
case Message.Msg of
WM_THEMECHANGED:
begin
[...snip...]
end;
WM_DESTROY:
begin
// If we are connected to a window then we have to listen to its destruction.
SetWindowLong(FWindowHandle, GWL_WNDPROC, Integer(FDefWindowProc));
{$ifdef COMPILER_6_UP}
Classes.FreeObjectInstance(FObjectInstance);
{$else}
FreeObjectInstance(FObjectInstance);
{$endif COMPILER_6_UP}
FObjectInstance := nil;
end;
end;
with Message do
Result := CallWindowProc(FDefWindowProc, FWindowHandle, Msg, WParam, LParam);
end;
TThemeServices 对象是一个单例,在单元完成过程中被销毁:
initialization
finalization
InternalThemeServices.Free;
end.
这一切都很好——只要 TThemeServices 是唯一一个将应用程序的句柄子类化的人。
我有一个类似的单例库,它也想挂钩Application.Handle,以便我可以接收广播:
procedure TDesktopWindowManager.WindowProc(var Message: TMessage);
begin
case Message.Msg of
WM_DWMCOLORIZATIONCOLORCHANGED: ...
WM_DWMCOMPOSITIONCHANGED: ...
WM_DWMNCRENDERINGCHANGED: ...
WM_DESTROY:
begin
// If we are connected to a window then we have to listen to its destruction.
SetWindowLong(FWindowHandle, GWL_WNDPROC, Integer(FDefWindowProc));
{$ifdef COMPILER_6_UP}
Classes.FreeObjectInstance(FObjectInstance);
{$else}
FreeObjectInstance(FObjectInstance);
{$endif COMPILER_6_UP}
FObjectInstance := nil;
end;
end;
with Message do
Result := CallWindowProc(FDefWindowProc, FWindowHandle, Msg, WParam, LParam);
当单元完成时,my 单例也同样被移除:
initialization
...
finalization
InternalDwmServices.Free;
end.
现在我们来解决问题。我不能保证某人可能选择访问ThemeServices 或DWM 的顺序,每个都应用它们的子类。我也不知道 Delphi 最终确定单元的顺序。
子类被删除顺序错误,应用程序关闭时崩溃。
如何解决?完成后我如何ensure that i keep my subclassing method around long enough until the other guy is done? (毕竟我不想泄露内存)
另见
- Raymond Chen: Safer Subclassing
- MSDN: Using Window Procedures
- Raymond Chen: When the normal window destruction messages are thrown for a loop
更新:我看到 Delphi 7 通过重写 TApplication 解决了这个错误。 >
procedure TApplication.WndProc(var Message: TMessage);
...
begin
...
with Message do
case Msg of
...
WM_THEMECHANGED:
if ThemeServices.ThemesEnabled then
ThemeServices.ApplyThemeChange;
...
end;
...
end;
Grrrr
换句话说:尝试继承 TApplication 是一个错误,Borland 在采用 Mike 的 TThemeManager 时修复了该错误。
这很可能意味着无法以相反的顺序删除TApplication 上的子类。有人以答案的形式提出,我会接受。
【问题讨论】:
-
Delphi 7 的主题代码是基于 Mike Lischke 的代码。但是当然,既然他们有源代码,那么他们就没有必要进行子类化。您是否有充分的理由不能使用现代 Delphi?
-
我完全不同意将 TApplication 子类化,就像 Mike 在他的 XP 主题管理器中所做的那样,是一个错误。他还能做什么?更重要的是,我认为该代码是我遇到过的最伟大的编码作品之一。事实上,这仍然是主题绘画的主要参考资料之一。尽管有一些小错误,但考虑到他正在尝试的复杂性,它们很少而且相差甚远,这不足为奇。所以,我支持迈克,回应你的“Grrrr”!!
-
我并不是要暗示子类化
TApplication是一个错误——只是他的做法是错误的(假设在他之前没有其他人继承了 TApplication,并且他之后的其他人也不会) -
为什么是delphi-t标签?什么是delphi-t?
-
@Jørn。哎呀。 “T”是“5”的不足一键。修复了标签。谢谢。
标签: delphi themes subclassing delphi-5 dwm