Delphi 使用FM3的Windows XP中缺少任务栏按钮上下文菜单

Delphi 使用FM3的Windows XP中缺少任务栏按钮上下文菜单,delphi,taskbar,delphi-xe4,firemonkey-fm3,Delphi,Taskbar,Delphi Xe4,Firemonkey Fm3,我最近升级到了XE4,现在正在查找来自XE3的更改和所谓的修复 让我非常惊讶的是,任务栏上按钮的上下文菜单不再出现 复制非常容易:只需在XE4中创建一个新的Firemonkey项目并在Windows上运行它。右键单击任务栏应用程序按钮,查看上下文菜单是否出现。我指的是带有“关闭”、“恢复”、“最小化”等功能的菜单。 这仅适用于Windows XP和Server 2003。在Win7上,它工作并显示“关闭”菜单项 按钮的标题现在也不同了。主窗体的标题应该是“Form1”,但可执行文件名应该是Pro

我最近升级到了XE4,现在正在查找来自XE3的更改和所谓的修复

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

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

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

有人能帮我做这个吗?人们仍然使用XP,这种行为对于用户来说是非常意外的


谢谢

我问这个问题已经有一段时间了,但我同时找到了一个解决方案,如果对其他人有用,我会把它贴出来

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

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

要隐藏应用程序窗口,只需调用
ShowWindow(AppWindowHandle,SW\u hide)

要在任务栏上显示主窗体窗口,我们必须使用
SetWindowLong()
设置
WS\u EX\u APPWINDOW
扩展窗口样式,并在每次显示、还原应用程序时调用
ShowWindow
,然后将其置于前台

这是通过放置一个钩子来截获WM_CREATE、WM_SHOWWINDOW和WM_ACTIVATE消息,并在调用这些消息时应用样式来完成的。为了便于使用,所有代码都放在一个单元中,挂钩设置在
初始化部分。
没有可调用的函数。要使用该单元,只需将其放在
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.
单元FM3TaskbarFix;
接口
实施
{$IFDEF MSWINDOWS}
使用
Messages、Winapi.Windows、System.Sysutils、Fmx.Forms、Fmx.Platform.Win;
变量
GHookHandle:HHOOK;//我们设置的钩子的把手
GAppWnd:HWND=0;//主应用程序窗口的句柄
函数CallWndProc(nCode:Integer;iWParam:WPARAM;iLParam:LPARAM):LRESULT;stdcall;
变量
ActiveThreadID,WindowThreadID:DWORD;
ProcMsg:TCWPStruct;
开始
结果:=CallNextHookEx(GHookHandle、nCode、iWParam、iLParam);
如果(nCode<0),则
出口
ProcMsg:=PCWPStruct(iLParam)^;
case procmg.message of
WM_创建:
//保存“主”应用程序窗口句柄以供以后使用。每个应用程序只有一个包含TFMAppClass类的窗口
如果(GAppWnd=0)和(PCREATESTRUCT(procmg.lParam)^.lpszClass='TFMAppClass'),那么
GAppWnd:=ProcMsg.hwnd;
WM_激活,WM_显示窗口:
开始
//隐藏应用程序窗口。必须在每次最小化、还原等操作时调用此命令。
如果IsWindowVisible(GAppWnd),则
显示窗口(大开窗口、隐藏窗口);
//仅处理显示/激活。wParam为1表示应用程序已显示或激活,而不是隐藏或停用
//还将样式设置仅应用于Application.main表单
//我们不想在任务栏上显示其他窗体
如果(procmg.wParam=1)和
(GetWindow(procmg.hwnd,GW_OWNER)=GAppWnd)和分配(Application.MainForm)以及
(WindowHandleToPlatform(Application.MainForm.Handle).Wnd=procmg.hwnd)然后
开始
//在任务栏上显示主窗体
SetWindowLong(procmg.hwnd、GWL_EXSTYLE、GetWindowLong(procmg.hwnd、GWL_EXSTYLE)或WS_EX_APPWINDOW);
显示窗口(ProcMsg.hwnd,SW_SHOW);
ActiveThreadID:=GetWindowThreadProcessId(GetForeGroundIndow,无);
WindowThreadID:=GetWindowThreadProcessId(procmg.hwnd,nil);
AttachThreadInput(WindowThreadID、ActiveThreadID、True);
尝试
SetForegroundWindow(procmg.hwnd);
SetActiveWindow(procmg.hwnd);
最后
AttachThreadInput(WindowThreadID、ActiveThreadID、False);
结束;
结束;
结束;{WM_激活,WM_显示窗口}
结束;{case}
结束;
初始化
GHookHandle:=SetWindowsHookEx(WH_CALLWNDPROC,CALLWNDPROC,0,GetCurrentThreadID);
定稿
Unhookwindowshookx(GHookHandle);
{$ENDIF}
结束。
顺便说一句,这段代码基于网络上的两个示例。我不知道作者是谁,但我相信他们的想法。
还有一个问题。当应用程序首次最小化时,应用程序窗口的按钮会临时重新出现,而不是表单按钮。在应用程序恢复或再次最小化后,这种情况不再发生