Delphi:如何以相反的顺序删除子类? [英] Delphi: How to remove subclasses in reverse order?

查看:236
本文介绍了Delphi:如何以相反的顺序删除子类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Mike Lischke的 TThemeServices 子类 Application.Handle ,以便它可以从Windows接收广播通知(即 WM_THEMECHANGED )当主题更改。



它分类应用程序对象的窗口:

  FWindowHandle:= Application.Handle; 
如果FWindowHandle<> 0然后
begin
//如果给出了一个窗口句柄,则子类化窗口以获得关于主题更改的通知。
{$ ifdef COMPILER_6_UP}
FObjectInstance:= Classes.MakeObjectInstance(WindowProc);
{$ else}
FObjectInstance:= MakeObjectInstance(WindowProc);
{$ endif COMPILER_6_UP}
FDefWindowProc:=指针(GetWindowLong(FWindowHandle,GWL_WNDPROC));
SetWindowLong(FWindowHandle,GWL_WNDPROC,Integer(FObjectInstance));
结束

然后,子类窗口procdure会按照它应该是 WM_DESTROY 消息,删除它的子类,然后通过 WM_DESTROY 消息:

 程序TThemeServices.WindowProc(var Message:TMessage); 
begin
case Message.Msg
WM_THEMECHANGED:
begin
[... snip ...]
end;
WM_DESTROY:
begin
//如果我们连接到一个窗口,那么我们必须听其破坏。
SetWindowLong(FWindowHandle,GWL_WNDPROC,Integer(FDefWindowProc));
{$ ifdef COMPILER_6_UP}
Classes.FreeObjectInstance(FObjectInstance);
{$ else}
FreeObjectInstance(FObjectInstance);
{$ endif COMPILER_6_UP}
FObjectInstance:= nil;
结束
结束

with Message do
结果:= CallWindowProc(FDefWindowProc,FWindowHandle,Msg,WParam,LParam);
结束

TThemeServices 对象是单例,销毁在单元定稿期间:

 初始化
finalization
InternalThemeServices.Free;
结束。

这一切都很好 - 只要TThemeServices是唯一一个子类化应用程序句柄的人。



我有一个类似的单身图书馆,也想钩住 Application.Handle 所以我可以收到广播: / p>

 程序TDesktopWindowManager.WindowProc(var Message:TMessage); 
开始
案例Message.Msg
WM_DWMCOLORIZATIONCOLORCHANGED:...
WM_DWMCOMPOSITIONCHANGED:...
WM_DWMNCRENDERINGCHANGED:...
WM_DESTROY:
begin
//如果我们连接到一个窗口,那么我们必须听其破坏。
SetWindowLong(FWindowHandle,GWL_WNDPROC,Integer(FDefWindowProc));
{$ ifdef COMPILER_6_UP}
Classes.FreeObjectInstance(FObjectInstance);
{$ else}
FreeObjectInstance(FObjectInstance);
{$ endif COMPILER_6_UP}
FObjectInstance:= nil;
结束
结束

with Message do
结果:= CallWindowProc(FDefWindowProc,FWindowHandle,Msg,WParam,LParam);

我的单例在单元完成时也被删除:

 初始化
...
finalization
InternalDwmServices.Free;
结束。






现在我们来问题。我不能保证有人可能选择访问 ThemeServices DWM 的顺序,每个都应用其子类。我也不知道Delphi将最终确定单位的顺序。



子类以错误的顺序被删除,应用程序关闭时出现崩溃。 >

如何解决?我如何确保我保持我的子类化方法足够长在我完成之后,直到其他的人完成 (我毕竟不想泄漏内存)



另请参见








更新:我看到Delphi 7通过重写 TApplication 来解决错误。 ><

 程序TApplication.WndProc(var Message:TMessage); 
...
开始
...
与消息do
case Msg
...
WM_THEMECHANGED:
如果ThemeServices.ThemesEnabled然后
ThemeServices.ApplyThemeChange;
...
end;
...
end;

Grrrr



<换句话说:尝试子类化TApplication是一个错误,Borland在采用Mike的 TThemeManager 时修复。



很好可能意味着没有办法以相反的顺序删除 TApplication 上的子类。有人以答案的形式提出,我会接受的。

解决方案

更改代码以调用 SetWindowSubclass ,作为您链接的文章建议。但是,只有每个人都使用相同的API,所以补丁主题管理器使用相同的技术。该API是在Windows XP中引入的,所以没有任何危险,它不需要在需要的系统上。



补丁主题管理器应该没有问题。它旨在支持Microsoft不再支持的Windows XP,并支持Borland不再支持的Delphi 4到6。由于所有相关产品的开发已经停止,因此您可以安全地拨出主题管理器项目,而不会因未来更新而落后。



您不是真的引入依赖。相反,您正在修复仅在同时使用两个窗口外观库时才出现的错误。您的库的用户不需要为您的主题管理器工作,但如果他们希望使用这两者,他们需要使用主题管理器应用您的修补程序。应该没有什么反对,因为他们已经有基础版本,所以不像他们将要安装一个全新的图书馆。他们只是应用补丁并重新编译。


Mike Lischke's TThemeServices subclasses Application.Handle, so that it can receive broadcast notifications from Windows (i.e. WM_THEMECHANGED) when theming changes.

It subclasses the Application object's window:

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;

The subclassed window procdure then does, as it's supposed to, WM_DESTROY message, remove it's subclass, and then pass the WM_DESTROY message on:

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 object is a singleton, destroyed during unit finalization:

initialization
finalization
  InternalThemeServices.Free;
end.

And that all works well - as long as TThemeServices is the only guy who ever subclasses the Application's handle.

i have a similar singleton library, that also wants to hook Application.Handle so i can receive broadcasts:

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 singleton is similarly removed when the unit finalizes:

initialization
   ...
finalization
    InternalDwmServices.Free;
end.


Now we come to the problem. i can't guarantee the order in which someone might choose to access ThemeServices or DWM, each of which apply their subclass. Nor can i know the order in which Delphi will finalize units.

The subclasses are being removed in the wrong order, and there is a crash on application close.

How to fix? How can i ensure that i keep my subclassing method around long enough until the other guy is done after me is done? (i don't want to leak memory, after all)

See also


Update: i see Delphi 7 solves the bug by rewriting 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

In other words: trying to subclass TApplication was a bug, that Borland fixed when they adopted Mike's TThemeManager.

That very well may mean that there is no way to remove subclasses on TApplication in reverse order. Someone put that in the form of an answer, and i'll accept it.

解决方案

Change your code to call SetWindowSubclass, as the article you linked to advises. But that only works if everyone uses the same API, so patch Theme Manager to use the same technique. The API was introduced in Windows XP, so there's no danger that it's not available on the systems where it would be needed.

There should be no problem with patching Theme Manager. It's designed to support Windows XP, which Microsoft doesn't support anymore, and to support Delphi 4 through 6, which Borland doesn't support anymore. Since development has ceased on all relevant products, it is safe for you to fork the Theme Manager project without risk of falling behind due to future updates.

You're not really introducing a dependency. Rather, you're fixing a bug that's only present when both window-appearance libraries are in use at the same time. Users of your library don't need to have Theme Manager for yours to work, but if they wish to use both, they need to use Theme Manager with your patches applied. There should be little objection to that since they already have the base version, so it's not like they'd be installing an entirely new library. They'd just be applying a patch and recompiling.

这篇关于Delphi:如何以相反的顺序删除子类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆