【问题标题】:How to hide firemonkey application button from Taskbar (XE4)?如何从任务栏(XE4)隐藏firemonkey应用程序按钮?
【发布时间】:2013-05-27 08:17:25
【问题描述】:

根据question,可以通过将窗口样式更改为 WS_EX_TOOLWINDOW 来隐藏 fmx 任务栏图标。 在 XE2 和 XE3 中,此代码有效:

uses FMX.Platform.Win, Winapi.Windows;

procedure TForm1.Button1Click(Sender: TObject);
var h:THandle;
begin
  h := FmxHandleToHWND(Handle);
  ShowWindow(h, SW_HIDE);
  SetWindowLong(h, GWL_EXSTYLE, GetWindowLong(h, GWL_EXSTYLE) or WS_EX_TOOLWINDOW);
  ShowWindow(h, SW_SHOW);
end;

在 XE4 中,此解决方案不起作用(应用程序按钮应该被隐藏但没有任何反应)。有人知道吗?

谢谢。

【问题讨论】:

  • 这对我来说在 XE3 上工作正常(没有 MainformOnTaskbar):使用 FMX.Platform.Win ;过程 TForm2.Button1Click(Sender: TObject); var h:THandle;开始 h := FmxHandleToHWND(Handle); ShowWindow(h, SW_HIDE); SetWindowLong(h, GWL_EXSTYLE, GetWindowLong(h, GWL_EXSTYLE) 或 WS_EX_TOOLWINDOW);显示窗口(h,SW_SHOW);结束;
  • MainformOnTaskbar 确定主窗体是否为无主窗体。在 FMX 中可能没有任何意义,因为主窗体可能总是无主的。所以使用工具窗口样式就足够了。
  • 建议的代码不适用于 XE4。
  • 您的目标是防止您的应用一起出现在任务栏上(图标 + 标题)还是只是删除图标(标题可见)?
  • 我的目标只是图标,标题不重要。

标签: windows delphi delphi-xe3 firemonkey-fm2 delphi-xe4


【解决方案1】:

刚刚在 XE7 中尝试过,当然它没有用。然而,稍微看一下 FMX.PlatformWin 会发现应用程序句柄现在通过 ApplicationHWND 函数公开,所以在 XE7 上工作的代码(不要忘记包含单元 FMX.Platform.WinWinapi.Windows)是......

procedure HideAppOnTaskbar (AMainForm : TForm);
var
  AppHandle : HWND;
begin
  AppHandle := ApplicationHWND; 
  ShowWindow(AppHandle, SW_HIDE);
  SetWindowLong(AppHandle, GWL_EXSTYLE, GetWindowLong(AppHandle, GWL_EXSTYLE) and (not     WS_EX_APPWINDOW) or WS_EX_TOOLWINDOW);
  //ShowWindow(AppHandle, SW_SHOW);
end;

最后的 ShowWindow 是可选的 - 似乎没有什么区别。您可以删除扩展样式并恢复 WS_EX_APPWINDOW 样式以再次显示工具栏图标。

【讨论】:

    【解决方案2】:

    似乎在 XE4 FM 应用程序中没有更多的应用程序对象句柄。 所以我们需要获取主窗体的父窗体。 下面两个小方法可以在任务栏上隐藏/显示您的应用。

    procedure HideAppOnTaskbar (AMainForm : TForm);
    var
      AppHandle : HWND;
    begin
      AppHandle := GetParent(FmxHandleToHWND(AMainForm.Handle));
      ShowWindow(AppHandle, SW_HIDE);
      SetWindowLong(AppHandle, GWL_EXSTYLE, GetWindowLong(AppHandle, GWL_EXSTYLE) or WS_EX_TOOLWINDOW);
    end;
    
    procedure ShowAppOnTaskbar (AMainForm : TForm);
    var
      AppHandle : HWND;
    begin
      AppHandle := GetParent(FmxHandleToHWND(AMainForm.Handle));
      ShowWindow(AppHandle, SW_HIDE);
      SetWindowLong(AppHandle, GWL_EXSTYLE, GetWindowLong(AppHandle, GWL_EXSTYLE) and (not WS_EX_TOOLWINDOW));
      ShowWindow(AppHandle, SW_SHOW);
    end;
    

    我们也可以使用“Application.MainForm”而不是传递主窗体,但是在主窗体的“OnCreate”事件期间未分配此变量。

    因此,在主窗体的“OnCreate”事件中,您可以简单地编写:

    procedure TMyMainForm.FormCreate(Sender: TObject);
    begin
      HideAppOnTaskbar (self);
    end;
    

    【讨论】:

    • 我测试了建议的代码。它不起作用(没有效果)。我认为此代码仅适用于 XE3;另请注意,函数 FmxHandleToHWND 不再存在于 XE4 上。
    【解决方案3】:
    procedure HideAppOnTaskbar;
    var
      appHandle: HWND;
      pid, current_pid: DWORD;
      name: String;
    
    begin
      //ShowWindow(FindWindowA('TFMAppClass', nil), SW_HIDE);
    
      name := ChangeFileExt(ExtractFileName(ParamStr(0)), '');
    
      appHandle := 0;
      pid := 0;
      current_pid := GetCurrentProcessId();
      repeat
      begin
        //appHandle := FindWindowExA(0, appHandle, 'TFMAppClass', nil);
        appHandle := FindWindowExA(0, appHandle, 'TFMAppClass', PAnsiChar(AnsiString(name)));
        if (appHandle>0) then
        begin
          GetWindowThreadProcessId(appHandle, pid);
          if (current_pid = pid) then break;
        end;
      end
      until (appHandle>0);
    
      //SetParent(FmxHandleToHWND(Handle), nil);
      ShowWindow(appHandle, SW_HIDE);
    
    end;
    

    【讨论】:

      【解决方案4】:
      HWND hWnd = NULL;
      DWORD pid, current_pid = GetCurrentProcessId();
      do 
      {
          hWnd = FindWindowExA(NULL, hWnd, "TFMAppClass", NULL);
          if(hWnd)
          {
              GetWindowThreadProcessId(hWnd, &pid);
              if(current_pid == pid)
                  break;
          }
      } while(hWnd);
      
      ::SetParent(FmxHandleToHWND(Handle), NULL);
      ::ShowWindow(hWnd, SW_HIDE);
      

      【讨论】:

      • 谢谢。 “TFMAppClass”为我指明了正确的方向。我用 Spy++ 寻找类似的东西,但显然错过了。
      • 这段代码有一个问题。 ShowWindow(FindWindowA("TFMAppClass", NULL), SW_HIDE) 将隐藏它找到的具有此类的第一个窗口,这可能不是您的应用程序的窗口。如果您有两个 Firemonkey 应用程序正在运行,或者一个应用程序的两个实例...最好使用 FindWindow('TFMAppClass', PChar(ChangeFileExt(ExtractFileName(ParamStr(0)), ''))) 之类的东西,或者更好地将其与 GetWindowThreadProcessIdFindWindowEx 结合使用,以检查 ti 是否属于您的进程。
      • 您应该使用EnumWindows(),而不是在循环中调用FindWindow()
      【解决方案5】:

      这适用于 Delphi 10.3,它可能也适用于其他版本。

      我不知道你是否注意到,但是当你的代码到达 FormCreate 过程时,你的 FMX 应用程序将已经创建了一个任务栏按钮,至少在 Delphi 10.3.3 Rio 上是这样的我们可能不喜欢。因此,如果您使用建议的方法,您的应用程序将在任务栏中快速显示其图标以隐藏它。

      因此,如果您可以访问 Delphi 的 VCL/FMX 源代码文件,并且您不喜欢显示您的应用程序任务栏图标甚至一毫秒,您只需修改位于 c:\Program Files (x86)\Embarcadero\Studio\20.0\source\fmx\ 目录的 FMX.Platform.Win.pas 文件,然后将其复制到您的项目目录中,因此将选择它而不是原始目录,并按照以下建议修改CreateAppHandle函数:

      function TPlatformWin.CreateAppHandle: HWND;
      var
       ...
      begin
        ...
        Result := CreateWindowEx(WS_EX_WINDOWEDGE or WS_EX_APPWINDOW, FMAppClass.lpszClassName, PChar(FTitle),
                                 WS_POPUP or WS_GROUP, 0, 0, 0, 0, GetDesktopWindow, 0, hInstance, nil);
        if FApplicationHWND = 0 then // modified/added line
          Winapi.Windows.ShowWindow(Result, SW_HIDE) // modified/added line
        else // modified/added line
          Winapi.Windows.ShowWindow(Result, SW_SHOWNORMAL);
      end;   
      

      就是这样,你不需要其他方法,注意它也有 CreateWindowEx 传递你窗口的样式,如果你也想从 Alt-Tab 列表中隐藏它,请将 WS_EX_APPWINDOW 替换为 WS_EX_TOOLWINDOW而是。

      【讨论】:

        猜你喜欢
        • 2012-01-04
        • 1970-01-01
        • 1970-01-01
        • 2011-08-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-01
        • 1970-01-01
        相关资源
        最近更新 更多