Delphiでの通常のWindowsの実装
概要DelphiのVCLライブラリでは、使用と実装の便利さのために、アプリケーションオブジェクトアプリケーションはメッセージ応答を処理するための非表示のウィンドウを作成します。 VCLを使用して開発されたプログラムを、他のウィンドウで通常配置してタイルできないなど、多少変形しているように見えるのはこのウィンドウです。 VCLの詳細な分析を通じて、この記事は、元のプログラミング方法を変更することなく、アプリケーションプロジェクトファイルに3行のコードを変更することで問題を解決できるソリューションを提供します。
キーワードVCL、通常のウィンドウ、正規化
1はじめに
Delphiが提供するVCLクラスライブラリに書かれたWindowsアプリケーションには、標準のWindowsウィンドウと明らかに異なる明確な機能があります。メインウィンドウのシステムメニューは、タスクバーのシステムメニューとは異なります。一般的に言えば、メインウィンドウのシステムメニューには6つのメニュー項目があり、タスクバーシステムメニューには3つのメニュー項目しかありません。実際に使用して、VCLで開発されたプログラムには次の恥ずかしさがあることがわかりました。
1)十分に美しくない。これは、標準と一致しない場合、自然に少し変形しているように見えることは確かです。
2)メインウィンドウが最小化された場合、アニメーション効果はありません。
3)ウィンドウを配置することはできず、通常は他のウィンドウでタイルします。
4)タスクバーシステムメニューの優先度が最も高くなっています。モーダルウィンドウが存在する場合、モーダルウィンドウの設計に反して、プログラム全体を最小限に抑えることができます。
メインウィンドウのアニメーション効果を最小限に抑える問題は、forms.pas in delphi 5.0の後のforms.pasによって解決されましたが、残りの問題は常に存在しています。ほとんどの場合、これはアプリケーションに影響を与えませんが、専門的な効果が追求される状況では、実際に受け入れられません。 C ++ビルダーとDelphiは同じクラスライブラリを使用しているため、上記の問題はC ++ビルダーを使用して書かれたWindowsアプリケーションにも存在します。
この問題については、以前の記事(フォレストガンプの家で見つけることができます)で議論しましたが、当時の物語は基本的にトリックに思え、偶然その方法を見つけました。この記事のタスクは、VCLクラスライブラリを分析してこれを行うことの原則を説明し、3行のコードのみを使用してこの「異常なウィンドウ」の問題を完全に解決する方法を提供することです。
2つの原則
2.1アプリケーション作成プロセス
典型的なアプリケーションDelphiプロジェクトファイルは、最初からアプリケーションオブジェクトの初期化方法への参照があることに気付きました。
プログラムProject1;
用途
フォーム、
'unit1.pas' {form1}のunit1;
{$ r *.res}
始める
Application.Initialize;
application.createform(tform1、form1);
application.run;
終わり。
隠されたウィンドウはアプリケーションオブジェクトによって作成されているため、アプリケーションオブジェクトはどこから来たのでしょうか? Delphiのコード編集ウィンドウでCTRLを押し続け、アプリケーションをクリックすると、アプリケーションオブジェクトがforms.pasユニットで定義されているいくつかのグローバルオブジェクトの1つであることがわかります。これで十分ではありません。知りたいのは、アプリケーションオブジェクトが作成された場所です。これは、Tapplicationクラスのインスタンスを参照する前に正常に作成する必要があるためです。
考えてみてください。アプリケーションの前に実行されるコードはありますか?ちなみに、それは初期化コードセグメントのコードです。 VCLソースコードを慎重にデバッグした後、VCLの多くのユニットに初期化コードセグメントがあることがわかります。すべての初期化アクション。初期化方法は、アプリケーションを初期化するため、アプリケーションオブジェクトが特定のユニットの初期化スニペットで作成されることは明らかです。
キーワード「tapplication.create」を使用してVCLソースコードディレクトリで検索すると、controls.pasユニットにアプリケーションオブジェクトを作成するコードが見つかりました。 controls.pasユニットの初期化コードセグメントでは、initcontrols手順への呼び出しがあり、initcontrolsの実装は次のとおりです。
ユニットコントロール;
…
初期化
...
initcontrols;
手順initcontrols;
始める
...
マウス:= tmouse.create;
画面:= tscreen.create( nil );
アプリケーション:= tapplication.create( nil );
...
終わり;
OK、この時点で分析は最初のステップを完了しました。なぜなら、異常なウィンドウの問題を解決するには、アプリケーションオブジェクトを初期化する前に1つのことをしなければならないため、アプリケーションの初期化プロセスを理解することが非常に重要です。
2.2島の変数
Islibrary変数は、System.pasユニットで定義されているグローバルフラグ変数の1つです。 Islibraryの値が真である場合、プログラムモジュールが動的リンクライブラリであることを意味します。そうでなければ、実行可能なプログラムです。 VCLクラスライブラリの一部のプロセスは、このフラグ変数の異なる値に基づいて異なるアクションを完了します。つまり、この変数は、Delphiの異常な窓の問題を解決する上で重要な役割を果たします。
前述のように、便利なため、アプリケーションオブジェクトが初期化されたときに目に見えないウィンドウが作成されました(つまり、Spy ++のようなツールで見られるクラス名として「tapplication」のウィンドウ)が作成されましたが、これはこの目に見えないため、ウィンドウが作成できます。 Delphiで開発されたプログラムは、多くの異常を示しています。わかりました、この目に見えないウィンドウを削除して(そして同時にタスクバーシステムメニューを削除して)、アプリケーションメインウィンドウに置き換えることができれば、すべての問題が解決されるわけではありませんか?
言うのは簡単ですが、VCLソースコードを実装するには大規模な手術が必要ですか?それは馬の前にカートを置くのではないでしょうか?もちろん、答えはノーです。そうしないと、この記事は利用できなかったでしょう。ここで言いたいのは、次の分析では、いわゆる「プログラミングの方法が一つの心にある」ことがわかります。インターフェイス。ソースコード分析を行わない場合は、円を描く必要がある場合がありますが、実際には、天才のデザインが私たちにそれ以上のままになっていることがわかります。
Tapplicationクラスのコンストラクター作成を開くと、そのようなコードの行があります。
Constructor tapplication.create(aowner:tcomponent);
始める
...
ISLIBRARYでなければ、 Handleを作成します。
...
終わり;
ここで言われているのは、プログラムモジュールが動的リンクライブラリでない場合、CreateHandleを実行し、CreateHandleによって行われた作業は次のとおりです。ウィンドウ "は上記の目に見えないウィンドウであり、これは犯人です。タップリケーションクラスでは、ファンドル変数がウィンドウハンドルを保存するために使用されます。これは、Islibraryの価値に従って異なるアクションを完了するためです。動的リンクライブラリでは、メッセージループは一般的に必要ではありませんが、アプリケーションオブジェクトを使用してVCLを使用して動的リンクライブラリを開発するため、デザインは次のとおりです。 OK、アプリケーションオブジェクトをだまし、作成する前にiSLibraryをtrueに割り当て、CreateHandleの実行を除外して、この迷惑なウィンドウを削除する必要があります。
ISLIBRARYに割り当てられたコードは、ISLIBRARYが以前にTRUEに割り当てられるように、初期化コードセグメントのコードが含まれている順に実行されるため、明らかに特定のユニットの初期化コードセグメントに配置する必要があります。アプリケーションオブジェクトは、プロジェクトファイルに作成されます。次のように(ユニットの名前がunitdllexe.pasという名前であると仮定して、フォームユニットの前に割り当てコードを含むユニットを配置する必要があります。
プログラムテンプレート。
用途
unitdllexe in 'unitdllexe.pas'、
フォーム、
formmain in 'formmain.pas' {mainform}、
...
unitdllexe.pasコードリストは次のとおりです。
ユニットunitdllexe;
インタフェース
実装
初期化
ISLIBRARY:= true;
// Applciationオブジェクトに、これが動的なリンクライブラリであり、非表示のウィンドウを作成する必要がないことを伝えます。
終わり。
さて、非表示のウィンドウが作成されていないため、メインウィンドウのシステムメニューが並べられ、通常は他のウィンドウに置き換えられたことがわかります。 Windows。しかし、問題は、ウィンドウを最小化できないことです。どうしたの?それはまだ古い方法です、それに従ってください。
2.3メインウィンドウの最小化
最小化はシステムコマンドに属し、最終的にはウィンドウを最小限に抑えるためにAPI関数defwindowProcと呼ばれる必要があるため、wm_syscommandメッセージに困難なく応答する関数wmsyscommandが見つかりました。 .wndproc to Handle:
手順tcustomform.wmsyscommand( var message:twmsyscommand);
始める
メッセージで
始める
if (cmdtypeおよび$ fff0 = sc_minimize)および(application.mainform = self) then
application.wndproc(tmessage(メッセージ))
...
終わり;
終わり;
application.wndprocでは、最小化されたメッセージに応じて最小化方法が呼び出されるため、問題の核心は最小化プロセスでなければなりません。
手順tapplication.wndproc( var message:tmessage);
...
始める
...
メッセージで
のケースMSG
wm_syscommand:
ケースwparamおよび$ fff0
sc_minimize:最小化;
sc_restore:復元;
それ以外
デフォルト;
...
終わり;
最後に、tapplication.minimizeを見つければ、すべてを理解できます。ここでのdefwindowproc関数への呼び出しは、何の効果も生じません、なぜですか?以前にアプリケーションオブジェクトを欺き、CreateHandleの呼び出しをフィルタリングし、アプリケーションオブジェクトのメッセージに応答するために必要なウィンドウを作成しなかったため、ハンドルファンドルは0であり、コールはもちろん失敗します。ファンドルをメインアプリケーションウィンドウに向けることができれば、問題は解決します。
手順tapplication.minimize;
始める
...
defwindowproc(fhandle、wm_syscommand、sc_minimize、0);
//ここのファンドル値は0です
...
終わり;
3実装
ボーランドの天才の意図しない設計により、再び問題を解決することができました。以前の分析から、VCLで開発された動的リンクライブラリには、Windowsメッセージを受信するための非表示のウィンドウ(CreateHandleは実行されません)がありますが、ダイナミックリンクライブラリでは、ウィンドウを表示する場合は、親ウィンドウ。この問題を解決する方法は? VCLデザイナーは、目に見えないウィンドウハンドルを書き込み可能なものとして保持するFHANDLE変数を設計するため、実際にファンドルに値を割り当てて、表示する必要がある子ウィンドウに親ウィンドウを提供できます。たとえば、フォームを表示するダイナミックリンクライブラリプラグインでは、通常、メインモジュール実行可能ファイルのダイナミックリンクライブラリの関数を介してアプリケーションオブジェクトのハンドルを渡し、それをアプリケーションに割り当てます。ダイナミックのハンドルメインモジュール実行可能ファイルのリンクを使用して、アプリケーションのダイナミックリンクライブラリのハンドルに割り当てます。
手順SetApplicationHandle(mainAppwnd:hwnd)
始める
Application.Handle:= mainAppwnd;
終わり;
OK、aplication.handleは実際にはメッセージに応答するために内部的に使用されるウィンドウハンドルであり、作成されるべき目に見えないウィンドウが削除されているため、それを置き換えるためのウィンドウハンドルを与えるだけでよいです元々不要なウィンドウのハンドルを隠すだけで十分ですか?そのような窓はどこにありますか?アプリケーションのメインウィンドウが最良の選択であるため、次のコードが利用可能です。
プログラムテンプレート。
用途
unitdllexe in 'unitdllexe.pas'、
フォーム、
formmain in 'formmain.pas' {mainform};
{$ r *.res}
始める
Application.Initialize;
application.createform(tformmain、formmain);
Application.Handle:= formmain.handle;
application.run;
終わり。
そのため、すべての問題が解決されました。 VCLソースコードを変更する必要はありません。また、元のプログラムに2行を追加する必要はありません。アプリケーションウィンドウを作成するための3行のコードのうち、標準のウィンドウウィンドウとまったく同じです。
1)タスクバーとウィンドウタイトルバーには、一貫したシステムメニューがあります。
2)メインウィンドウが最小化されると、アニメーション効果があります。
3)ウィンドウを配置して、他のウィンドウで通常並べることができます。
4)モーダルウィンドウがある場合、親ウィンドウで動作することはできません。
上記の実装コードは、Delphiのすべてのバージョンで使用されます。