迈克·利施克的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;
The 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);
And my当单元完成时,单例也会被类似地删除:
initialization
...
finalization
InternalDwmServices.Free;
end.
现在我们来解决问题了。我无法保证某人可能选择访问的顺序ThemeServices
or DWM
,每个都应用它们的子类。我也不知道德尔福最终确定单位的顺序。
子类被以错误的顺序删除,并且应用程序关闭时发生崩溃。
怎么修?我怎么能够确保我将子类化方法保留足够长的时间,直到other家伙完成了 http://blogs.msdn.com/b/oldnewthing/archive/2003/11/11/55653.aspx我完成后? (毕竟我不想泄漏内存)
See also
- Raymond Chen:更安全的子类化 http://blogs.msdn.com/b/oldnewthing/archive/2003/11/11/55653.aspx
- MSDN:使用窗口过程 http://msdn.microsoft.com/en-us/library/ms633570%28v=vs.85%29.aspx
- Raymond Chen:当正常的窗口销毁消息被循环抛出时 http://blogs.msdn.com/b/oldnewthing/archive/2005/07/27/443824.aspx
Update:我看到Delphi 7通过重写解决了这个bugTApplication
。 >
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
以相反的顺序。有人以答案的形式提出了这一点,我会接受它。