4

Mike LischkeのTThemeServicesサブクラス。これにより、テーマが変更されたときにApplication.HandleWindows(つまり)からブロードキャスト通知を受信できるよう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がユニットをファイナライズする順序もわかりません。

サブクラスが間違った順序で削除されており、アプリケーションを閉じるとクラッシュします。

直し方?私が終わった後に他の人が完了するまで、サブクラス化メソッドを十分長く保つにはどうすればよいですか?(結局、メモリをリークしたくない)

も参照してください


更新: Delphi7はを書き直すことでバグを解決しているようです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これは、サブクラスを逆の順序で削除する方法がないことを意味している可能性があります。誰かがそれを答えの形で入れて、私はそれを受け入れます。

4

4 に答える 4

4

SetWindowSubclassリンク先の記事がアドバイスしているように、コードを call に変更してください。しかし、これは全員が同じ API を使用している場合にのみ機能するため、同じ手法を使用するようにテーマ マネージャーにパッチを適用します。API は Windows XP で導入されたので、それが必要なシステムで使用できないという危険はありません。

Theme Manager にパッチを適用しても問題はありません。Microsoft がサポートを終了した Windows XP と、Borland がサポートを終了した Delphi 4 から 6 をサポートするように設計されています。関連するすべての製品の開発が終了したため、将来の更新のために遅れるリスクなしにテーマ マネージャー プロジェクトをフォークしても問題ありません。

あなたは本当に依存関係を導入していません。むしろ、両方のウィンドウ表示ライブラリが同時に使用されている場合にのみ存在するバグを修正しています。あなたのライブラリーのユーザーは、あなたのライブラリーが機能するためにテーマ マネージャーを持っている必要はありませんが、両方を使用したい場合は、パッチが適用されたテーマ マネージャーを使用する必要があります。彼らはすでに基本バージョンを持っているので、それに対する異論はほとんどないはずです。したがって、まったく新しいライブラリをインストールするわけではありません. パッチを適用して再コンパイルするだけです。

于 2011-01-06T16:51:18.650 に答える
2

TApplication ウィンドウをサブクラス化する代わりに、おそらく AllocateHWnd() を使用して、同じブロードキャストを個別に受信できます。これは、それ自体が最上位のウィンドウであるためです。

于 2011-01-06T21:04:07.867 に答える
1

私は次のことをすると思います:

  • ThemeSrv.pasの初期化セクションにThemeServicesへの参照を配置します。
  • DwmSrv.pasの初期化セクションにDwmServicesへの参照を配置します(ユニットの名前だと思います)。

ユニットは初期化の順序とは逆の順序でファイナライズされるため、問題は解決されます。

于 2011-01-06T15:36:50.297 に答える
0

ApplicationEvents を使用して、それで完了してはいかがでしょうか。サブクラス化をいじる必要はありません。もう 1 つの方法は、サブクラスを 1 つだけ作成し、複数の通知イベントを作成して、必要な数だけサブスクライブすることです。

乾杯

于 2011-01-06T16:44:28.793 に答える