Delphi: Как удалить подклассы в обратном порядке? - PullRequest
4 голосов
/ 06 января 2011

Подклассы 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);

И мой синглтон удаляется аналогичным образом, когда устройство завершает работу:

initialization
   ...
finalization
    InternalDwmServices.Free;
end.

Теперь мы подошли к проблеме. Я не могу гарантировать порядок, в котором кто-то может выбрать доступ к ThemeServices или DWM, каждый из которых применяет свой подкласс. Также я не могу знать порядок, в котором Delphi будет завершать юниты.

Подклассы удаляются в неправильном порядке, и при закрытии приложения происходит сбой.

как исправить? Как я могу гарантировать, что я продолжу свой метод подклассов достаточно долго, пока другой парень не выполнит после того, как я закончу? (Я не хочу, чтобы утечка памяти, в конце концов)

Смотри также


Обновление: Я вижу, Delphi 7 исправляет ошибку, переписав TApplication. > <</p>

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 исправил, когда они приняли TThemeManager Майка.

Это очень хорошо может означать, что нет способа удалить подклассы на TApplication в обратном порядке. Кто-то выразил это в форме ответа, и я приму его.

Ответы [ 4 ]

4 голосов
/ 06 января 2011

Измените свой код на SetWindowSubclass, как советует статья, на которую вы ссылались.Но это работает, только если все используют один и тот же API, поэтому исправьте Theme Manager, чтобы использовать ту же технику.API был введен в Windows XP, поэтому нет опасности, что он не будет доступен в системах, где он будет необходим.

Не должно быть проблем с исправлением Theme Manager.Он предназначен для поддержки Windows XP, которую Microsoft больше не поддерживает, и для поддержки Delphi с 4 по 6, который Borland больше не поддерживает.Поскольку разработка всех соответствующих продуктов прекращена, вы можете безопасно развернуть проект Theme Manager без риска отставания из-за будущих обновлений.

На самом деле вы не вводите зависимость.Скорее, вы исправляете ошибку, которая присутствует только тогда, когда обе библиотеки оконных окон используются одновременно.Пользователям вашей библиотеки не нужно иметь Theme Manager для вашей работы, но если они хотят использовать оба, они должны использовать Theme Manager с вашими исправлениями.Не должно быть никаких возражений против этого, поскольку у них уже есть базовая версия, так что они не будут устанавливать совершенно новую библиотеку.Они просто применили патч и перекомпилировали.

2 голосов
/ 07 января 2011

Вместо того, чтобы создавать подклассы окна TApplication, возможно, вы можете вместо этого использовать AllocateHWnd (), чтобы получать те же широковещательные сообщения отдельно, поскольку это собственное окно верхнего уровня.

1 голос
/ 06 января 2011

Думаю, я бы сделал следующее:

  • Поместите ссылку на ThemeServices в разделе инициализации ThemeSrv.pas.
  • Поместите ссылку на DwmServices в разделе инициализации DwmSrv.pas (я думаю, название вашего устройства).

Поскольку юниты завершаются в порядке, обратном порядку инициализации, ваша проблема будет решена.

0 голосов
/ 06 января 2011

Почему бы вам просто не использовать ApplicationEvents и покончить с этим. Нет необходимости возиться с подклассами. Другой способ - создать только один подкласс, создать несколько уведомлений и подписаться на любое количество подписчиков.

Приветствия

...