質問してからしばらく経ちましたが、その間に解決策を見つけたので、他の人に役立つ場合に備えて投稿します.
間違ったキャプションと欠落したメニューの問題は、Delphi XE4 が Firemonkey アプリのウィンドウ構造を変更したことに起因しています。それ以前は、メイン フォームのウィンドウはタスク バーに配置され、適切なキャプションとコンテキスト メニューがありました。XE4 では、アプリは「TFMAppClass」というクラス名で新しいウィンドウを作成し、それをタスクバーに配置されるメイン アプリケーション ウィンドウとして使用します。メイン フォーム ウィンドウは、その兄弟です。
これにより、タスクバー ボタンのキャプションを設定できず、コンテキスト メニューがなく、ボタンのクリックに適切に応答せず、メイン フォームが非表示のときにボタンを非表示にできません。
そのため、タスクバーからアプリ ウィンドウを非表示にして、代わりにフォーム ウィンドウを表示する必要がありました。ただし、アプリ ウィンドウのスタイルは最小化/復元のたびにリセットされ、タスク バーに再表示されるため、1 回実行するだけでは十分ではありません。
アプリ ウィンドウを非表示にするには、単に を呼び出しますShowWindow(AppWindowHandle, SW_HIDE)
。
タスクバーにメイン フォーム ウィンドウを表示するには、WS_EX_APPWINDOW
拡張ウィンドウ スタイルを設定し、アプリが表示されたり、復元されたりするたびSetWindowLong()
に呼び出しShowWindow
て、フォアグラウンドに移動する必要があります。
これは、フックを配置して WM_CREATE、WM_SHOWWINDOW、および WM_ACTIVATE メッセージをインターセプトし、これらのメッセージが呼び出されたときにスタイルを適用することによって行われます。使い方を簡単にするために、すべてのコードを単一のユニットに配置し、フックをinitialization
パーツに設定します。
呼び出す関数はありません。uses
単位を使用するには、句のどこかに置くだけです。
unit FM3TaskbarFix;
interface
implementation
{$IFDEF MSWINDOWS}
uses
Winapi.Messages, Winapi.Windows, System.Sysutils, Fmx.Forms, Fmx.Platform.Win;
var
GHookHandle: HHOOK; // Handle for the hook we set
GAppWnd : HWND = 0; // Handle of the main application window
function CallWndProc(nCode: Integer; iWParam: WPARAM; iLParam: LPARAM): LRESULT; stdcall;
var
ActiveThreadID, WindowThreadID: DWORD;
ProcMsg: TCWPStruct;
begin
Result := CallNextHookEx(GHookHandle, nCode, iWParam, iLParam);
if (nCode < 0) then
Exit;
ProcMsg := PCWPStruct(iLParam)^;
case ProcMsg.message of
WM_CREATE:
// Save the "main" app window handle for later usage. There is only one window with the TFMAppClass class per app
if (GAppWnd = 0) and (PCREATESTRUCT(ProcMsg.lParam)^.lpszClass = 'TFMAppClass') then
GAppWnd := ProcMsg.hwnd;
WM_ACTIVATE, WM_SHOWWINDOW:
begin
// Hide the app window. This has to be called on each minimize, restore, etc.
if IsWindowVisible(GAppWnd) then
ShowWindow(GAppWnd, SW_HIDE);
// Only handle Show/Activate. wParam of 1 means the app is shown or activated, NOT hidden or deactivated
// Also apply the style settings only to the Application.MainForm
// We don't want to show other forms on the taskbar
if (ProcMsg.wParam = 1) and
(GetWindow(ProcMsg.hwnd, GW_OWNER) = GAppWnd) and Assigned(Application.MainForm) and
(WindowHandleToPlatform(Application.MainForm.Handle).Wnd = ProcMsg.hwnd) then
begin
// Show the main form on the taskbar
SetWindowLong(ProcMsg.hwnd, GWL_EXSTYLE, GetWindowLong(ProcMsg.hwnd, GWL_EXSTYLE) or WS_EX_APPWINDOW);
ShowWindow(ProcMsg.hwnd, SW_SHOW);
ActiveThreadID := GetWindowThreadProcessId(GetForegroundWindow, nil);
WindowThreadID := GetWindowThreadProcessId(ProcMsg.hwnd, nil);
AttachThreadInput(WindowThreadID, ActiveThreadID, True);
try
SetForegroundWindow(ProcMsg.hwnd);
SetActiveWindow(ProcMsg.hwnd);
finally
AttachThreadInput(WindowThreadID, ActiveThreadID, False);
end;
end;
end; { WM_ACTIVATE, WM_SHOWWINDOW }
end; { case }
end;
initialization
GHookHandle := SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, 0, GetCurrentThreadID);
finalization
UnhookWIndowsHookEx(GHookHandle);
{$ENDIF}
end.
ところで、このコードはネットからの 2 つのサンプルに基づいています。著者が誰であるかはわかりませんが、アイデアの功績を称えます。
残っている問題が 1 つあります。アプリを最初に最小化すると、フォーム ボタンではなく、アプリ ウィンドウのボタンが一時的に再表示されます。アプリが復元または最小化された後、これはもう起こりません。