51

とはTApplication.Handle?

  • それはどこから来たのですか?
  • なぜそれが存在するのですか?
  • そして最も重要なのは、なぜすべてのフォームがそれを親ウィンドウ ハンドルとして持っているのでしょうか?

Delphi のヘルプには次のように書かれています。

TApplication.ハンドル

アプリケーションのメイン フォーム (ウィンドウ) のウィンドウ ハンドルへのアクセスを提供します。

property Handle: HWND;

説明

親ウィンドウ ハンドルを必要とする Windows API 関数を呼び出す場合は、Handle を使用します。たとえば、独自のトップレベルのポップアップ ウィンドウを表示する DLL には、アプリケーションでウィンドウを表示するための親ウィンドウが必要です。Handle プロパティを使用すると、そのようなウィンドウがアプリケーションの一部になるため、アプリケーションで最小化、復元、有効化、および無効化できます。

「アプリケーションのメイン フォームのウィンドウ ハンドル」という言葉に注目し、それをアプリケーションのメイン フォームのウィンドウ ハンドルと解釈すると、次のように比較できます。

  • 「アプリケーションのメイン フォームのウィンドウ ハンドル」、
  • MainFormのウィンドウハンドルApplication

しかし、それらは同じではありません:

Application.MainForm.Handle: 11473728
Application.Handle: 11079574

だから何Application.Handleですか?

  • それはどこから来たのですか?
  • どの Windows® ウィンドウ ハンドルですか?
  • の Windows® ウィンドウ ハンドルである場合、一致しないApplicationMainFormはなぜですか?
  • のウィンドウ ハンドルでない場合、それは何ですか?ApplicationMainForm
  • さらに重要なのは、なぜそれがすべてのフォームの最終的な親所有者なのかということです。
  • そして最も重要なのは、フォームを親から外して所有しないようにしようとしたり (タスクバーに表示できるようにするため)、または IProgressDialog のようなものを使用しようとすると、すべてがうまくいかないなぜですか?

本当に私が求めているのは、Application.Handleを存在させる設計上の根拠は何ですか? その理由を理解できれば、その方法が明らかになるはずです。


20 の質問のゲームを通して理解を更新します。

所有者を作成することによってウィンドウをタスクバーに表示する解決策について話す際にnullPeter Below は 2000 年に次のように述べています。

これにより、セカンダリ フォームから表示されるモーダル フォームで問題が発生する可能性があります。

モーダル フォームが起動しているときにユーザーがアプリから切り替えて、それを表示していたフォームに戻ると、モーダル フォームがフォームの下に隠れる場合があります。モーダルフォームが親であることを確認することで、これに対処することができます [sic; 彼はそれを示したフォームに所有することを意味しました( params.WndParent上記のように使用)

Dialogsしかし、これはunit およびexceptionからの標準ダイアログでは不可能であり、それらを正しく機能させるにはより多くの努力が必要です (基本的に、 を処理し、 Application viaApplication.OnActivateを親とするモーダル フォームを検索し 、それらを Z オーダーの最上位に移動する via ) 。 .GetLastActivePopupSetWindowPos

  • モーダル フォームが他のフォームの後ろに隠れてしまうのはなぜですか?
  • 通常、モーダル フォームを前面に表示するメカニズムは何ですか?ここでは機能しないのはなぜですか?
  • Windows® は、ウィンドウを積み重ねて表示する役割を果たします。Windows® が正しいウィンドウを表示しないのはなぜですか?

彼はまた、ウィンドウを強制的にタスク バーに表示する新しい Windows 拡張スタイルを使用することについても話しました (ウィンドウを非所有にする通常のルールが不十分、非現実的、または望ましくない場合)、WS_EX_APPWINDOW拡張スタイルを追加することによって:

procedure TForm2.CreateParams(var Params: TCreateParams); 
begin 
   inherited CreateParams( params ); 

   Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW; 
end; 

しかし、彼は次のように警告します。

別のアプリがアクティブなときにセカンダリ フォーム タスクバー ボタンをクリックすると、すべてのアプリケーション フォームが前面に表示されます。したくない場合はオプションがあります

フォームの所有者がまだApplication.Handle. アプリケーションはこれを行っていますか? なぜこれを行うのですか?こうするより、こうすべきではないこれを行わないことの欠点は何ですか。これを行うことのマイナス面がわかりました(システム メニューが正しく機能しない、タスクバー ボタンのサムネイルが不正確である、Windows® シェルがウィンドウを最小化できないなど)。


を扱った別の投稿でApplicationMike Edenfield は、親ウィンドウが他のウィンドウの最小化、最大化、および復元メッセージを送信すると述べています

これにより、フォームにタスクバー ボタンが追加されますが、他にも処理すべき細かい点がいくつかあります。最も明白なのは、フォームが親フォーム (アプリケーションのメイン フォーム) に送信される最小化/最大化を引き続き受信することです。これを回避するには、次のような行を追加して WM_SYSCOMMAND のメッセージ ハンドラーをインストールします。

procedure WMSysCommand(var Msg: TMessage); WM_SYSCOMMAND; 

procedure TParentForm.WMSysCommand(var Msg: TMessage); 
begin 
   if Msg.wParam = SC_MINIMIZE then 
   begin 
      // Send child windows message, don't 
      // send to windows with a taskbar button. 
   end; 
end; 

このハンドラは、最小化メッセージを渡さないように、アプリケーションの残りの > 部分とは独立して動作させたいもののPARENT形式になることに注意してください。> SC_MAXIMIZE、SC_RESTORE などに同様のコードを追加できます。

Windows® ウィンドウの最小化/最大化/復元メッセージがウィンドウに表示されないのはなぜですか? これは、ウィンドウ宛てのメッセージが Windows® によってウィンドウの所有者に送信されるためでしょうか? この場合、Delphi アプリケーションのすべてのフォームはApplication?によって「所有」されています。それは所有者をnullにするという意味ではありませんか:

procedure TForm2.CreateParams(var Params: TCreateParams);
begin
   inherited;
   Params.WndParent := 0; //NULL
end;

ウィンドウ ハンドルが削除され、フォームに干渉しなくなりますApplication。Windows はもう一度、最小化/最大化/復元メッセージを送信する必要がありますか?


おそらく、「通常の」Windows アプリケーションが実行することと、Borland が最初に Delphi アプリケーションを実行するように設計した方法を比較対照すると、このApplicationオブジェクトとメイン ループに関してです。

  • Applicationオブジェクトはどのソリューションを解決していましたか?
  • これらの同じ問題が存在しないようにするために、Delphi の新しいバージョンではどのような変更が加えられましたか?
  • それ以降のバージョンの Delphi での変更は、最初のアプリケーション設計が解決しようと懸命に試みた他の問題を引き起こしませんでしたか?
  • これらの新しいアプリケーションは、アプリケーションがそれらに干渉することなく、どのように機能し続けることができるでしょうか?

明らかに、Borland は初期設計の欠陥に気付きました。彼らの最初の設計は何でしたか、どのような問題を解決しましたか、どのような欠陥がありましたか、どのような再設計が行われたか、どのように問題を解決しましたか?

4

3 に答える 3

54

アプリケーション ウィンドウの理由には、ちょっとした歴史があります。Delphi 1 を開発するとき、IDE に「SDI」(デスクトップ全体に散在するウィンドウ)UI モデルを使用したいと考えていました。また、Windows がそのモデルを嫌っていることもわかっていました (今でもそうです)。しかし、当時の Visual Basic がそのモデルを採用していて、うまく機能しているように見えたことにも気付きました。さらに調査した結果、VB は特別な「隠し」パーキング ウィンドウを「所有者」として使用していることがわかりました (Windows では、親と所有者の概念が曖昧になることがありますが、この区別は VCL に似ています)。 .

これは、メイン メニューを含むウィンドウがめったにフォーカスされず、[ファイル] メニューの Alt-F の処理が機能しないという「問題」を解決した方法です。この中央の駐車ウィンドウを仲介として使用することで、メッセージをより簡単に追跡し、適切なウィンドウにルーティングすることができます。

この配置により、通常は複数のトップ レベル ウィンドウが完全に独立していた別の問題も解決されました。アプリケーションがこれらすべてのウィンドウの「所有者」を処理するようにすることで、それらはすべて協調して動作します。たとえば、アプリケーション ウィンドウのいずれかを選択すると、すべてのアプリケーション ウィンドウが前面に移動し、互いの z オーダーが維持されることに気付いたかもしれません。これにより、アプリケーションが最小化され、機能グループとして復元されます。

これは、このモデルを使用した結果です。このすべての作業を手動で行うこともできましたが、設計の哲学は、Windows を再発明するのではなく、できる限り Windows を活用することでした。これが、TButton または TEdit が実際には Windows の「ユーザー」BUTTON および EDIT ウィンドウ クラスおよびスタイルである理由でもあります。

Windows が進化するにつれて、その "SDI" モデルは人気を失い始めました。実際、Windows 自体がそのスタイルのアプリケーションに対して「敵対的」になり始めました。Windows Vista から 7 まで、パーキング ウィンドウを使用するアプリケーションでユーザー シェルがうまく機能しないようです。そこで、VCL をシャッフルして駐車ウィンドウをなくし、その機能をメイン フォームに移動することにしました。これにより、いくつかの「卵が先か鶏が先か」の問題が発生しました。アプリケーションの初期化の早い段階でパーキング ウィンドウを使用可能にして、他のウィンドウがそれに「アタッチ」できるようにする必要がありますが、メイン フォーム自体はすぐには構築されない可能性があります。TApplication は、これを機能させるためにいくつかのフープをジャンプする必要があり、問題を引き起こす微妙なエッジ ケースがいくつかあります。しかし、ほとんどの問題は解決されました。ただし、今後のアプリケーションでは、古いパーキング ウィンドウ モデルが引き続き使用されます。

于 2010-02-05T17:55:37.113 に答える
12

すべての VCL アプリには、アプリケーションと呼ばれる「隠し」トップ レベル ウィンドウがあります。これは、アプリケーションの起動時に自動的に作成されます。とりわけ、これは VCL のメイン ウィンドウ メッセージ ハンドラです。つまり、Application.ProcessMessages です。

アプリの最上位ウィンドウを非表示にすると、タスク バーに表示される不完全なシステム メニューや、Vista の不適切なサム ネイル ウィンドウなど、いくつかの奇妙なことが発生します。Delphi の新しいバージョンではこれが修正されています。

ただし、すべてのウィンドウが親としてそれを持っている必要はありません。親として持っていると、Windows はより適切に動作する傾向があります。ただし、Application.CreateForm で作成されたフォームは親として保持され、Application オブジェクトによって所有されます。それらは所有されているため、アプリケーションが解放されると解放されます。これは、Forms.DoneApplication の舞台裏で発生します。

于 2010-02-05T04:01:57.433 に答える
8

forms.pas (Delphi 2009) のソースを見ると、win32 GUI アプリで「マスター」ウィンドウを作成して呼び出しを許可しているようです。

  • TApplication.Minimize
  • TApplication.Restore

に渡されたメッセージはApplication.Handle、必要に応じて に転送されるようMainFormです (存在する場合)。これにより、メイン ウィンドウが作成されていない場合にアプリが最小化などに応答できるようになります。プロジェクト ソースを変更することで、メイン ウィンドウのないDelphi アプリを作成できます。

この場合、TApplicationメイン ウィンドウを作成していなくても、メソッドは機能します。すべての目的を把握できているかどうかはわかりませんが、すべての TApplication コードを調べる時間はありません。

あなたの質問ごとに:

  • それはどこから来たのですか?で作成されたウィンドウのハンドルです。TApplication.Create

  • どのウィンドウがそれを処理しますか? すべての GUI Delphi アプリが TApplication 抽象化の一部として必要とする偽のウィンドウ

  • アプリケーションのメイン フォームの Windows ハンドルですかいいえ

  • アプリケーションのメインフォームのハンドルではない場合、それは何ですか? 上記を参照

  • さらに重要なのは、なぜそれがすべてのフォームの最終的な親なのですか? あなたがその究極の親であると仮定すると、アプリケーション内のすべてのフォームを簡単に見つけることができるためです (この「マスター」フォームの子を列挙します)。

  • そして最も重要なこと:フォームを親から外そうとすると、すべてがうまくいかないのはなぜですか。非表示の「マスター」フォームが、子やメインフォームに渡す必要があるシステムメッセージを取得しているためだと思いますが、見つかりません親なしのフォーム。

とにかく、それは私の見解です。の TApplication 宣言とコードを調べると、おそらく詳細を知ることができますforms.pas。私が見た結論は、それが便利な抽象化であるということです。

よろしくお願いします、

ドン

于 2010-02-05T04:01:48.497 に答える