2

我最近升级到 XE4,现在正在寻找 XE3 的更改和所谓的修复程序。

令我惊讶的是,任务栏上按钮的上下文菜单不再出现。

很容易复制:只需在 XE4 中创建一个新的 Firemonkey 项目并在 Windows 上运行它。右键单击任务栏应用程序按钮,查看上下文菜单是否出现。我的意思是带有“关闭”、“恢复”、“最小化”等的菜单。这仅适用于 Windows XP 和 Server 2003。在 Win7 上它可以工作并显示“关闭”菜单项。

现在按钮的标题也不同了。它应该是“Form 1”作为主窗体的标题,但它是 Project1 作为可执行文件的名称。这适用于所有 Windows 版本。

有人可以帮我解决这个问题吗?人们仍然使用 XP,这种行为对用户来说是非常出乎意料的。

谢谢

4

1 回答 1

0

自从我提出这个问题以来已经过去了一段时间,但我在此期间找到了一个解决方案,并将发布它以防它对其他人有用。

错误标题和缺少菜单的问题来自 Delphi XE4 更改了 Firemonkey 应用程序的窗口结构这一事实。在此之前,主窗体的窗口被放置在任务栏上,并具有适当的标题和上下文菜单。在 XE4 中,应用程序创建一个类名为“TFMAppClass”的新窗口,并将其用作放置在任务栏中的主应用程序窗口。主窗体窗口是该窗体的兄弟。
这导致无法设置任务栏按钮标题、没有上下文菜单、无法正确响应单击按钮以及在隐藏主窗体时无法隐藏按钮。

因此,需要从任务栏中隐藏应用程序窗口并显示表单窗口。只做一次是不够的,因为应用程序窗口的样式在每次最小化/恢复时都会重置,并且会重新出现在任务栏上。

要隐藏应用程序窗口,只需调用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.

顺便说一句,此代码基于来自网络的两个示例。我不知道作者是谁,但我相信他们的想法。
还有一个问题。当应用程序第一次最小化时,应用程序窗口的按钮会临时重新出现,而不是表单按钮。在应用程序再次恢复或最小化后,这种情况就不会再发生了。

于 2013-12-12T08:22:56.110 回答