当前位置: 首页 > 知识库问答 >
问题:

如何停止我的应用程序显示在任务栏上?

焦光霁
2023-03-14

我的应用程序有一个选项,用户只能在系统托盘中运行,而不能在任务栏中运行。当我的应用程序由Delphi 6构建时,它运行良好。切换到Delphi XE2后,它不再工作。

我把它弄得一团糟,我让它适用于Windows 7,但是在Windows XP上运行时,我仍然有一个问题。应用程序正确地从任务栏中隐藏,并显示在系统托盘中。但是当我创建并显示任何附加表单时,图标会显示在Windows XP中。

procedure TfrmAppointment.HideWindowFromTaskbar;
var
   TaskbarList: ITaskbarList;
begin
Application.MainFormOnTaskBar := False;

// Windows 7 seems to behave differently.  This seems to fix it.
if (CheckWin32Version(6, 1)) then
    begin
    // We are in Win7, and we requested the tray.
    TaskbarList := CreateComObject(CLSID_TaskbarList) as ITaskbarList;
    TaskbarList.HrInit;
    TaskbarList.DeleteTab(Application.Handle);
    end
else
   begin
   // Previous code from D6 days
   ShowWindow(Application.Handle, SW_HIDE);
   SetWindowLong(Application.Handle, GWL_EXSTYLE, GetWindowLong(Application.Handle, GWL_EXSTYLE) or WS_EX_TOOLWINDOW);
   ShowWindow(Application.Handle, SW_SHOWNOACTIVATE);
   end;
end;

如果用户选择在系统托盘中显示应用程序的选项,则会运行该代码。它在我测试过的所有版本的Windows上都运行良好。但是,在Windows XP上,当我显示任何子表单时,应用程序会立即显示在任务栏中。在Windows 7中一切正常。

知道我错过了什么吗?

我应该补充一点,我知道这可能与在Delphi 2009应用程序中隐藏主窗体是同一个问题,但是我已经设置了MainFormOnTaskBar,所以答案似乎不适用。

[编辑:]更具体地说,我在这里添加了更多信息。此应用程序有两种模式:在任务栏中显示和在系统托盘中显示。

第一种模式与任何正常应用程序相同。应用程序仅存在于任务栏中。它最小化到任务栏。它从任务栏中恢复。

第二种模式的行为完全相同,但该任务栏图标只存在于系统托盘中。因此,当用户最小化应用程序时,我会截取该消息,抓取“Shell_TrayWnd”/“TrayNotifyWnd”的树,并调用DrawAnimatedRects()模拟最小化到托盘的过程。然后我隐藏主窗体。在系统托盘的消息中,我反向绘制相同的动画矩形,并使其再次可见。虽然窗体可见,但它不会显示在任务栏中。

这一切在所有Windows版本中都非常好。

我遇到的具体问题是,当显示任何其他窗体时,Windows XP会在任务栏中创建应用程序图标。Windows 7不能做到这一点。因此,如果Windows XP用户只使用应用程序主窗体,则不会出现问题,两种查看模式都可以正常工作。如果他们打开另一个窗口,应用程序图标就会出现,即使在该窗口关闭后也会保持不变。Windows 7没有这样做,图标仍然不见了。

共有2个答案

澹台奇略
2023-03-14

大卫的回答是正确的。虽然有一些小问题,但我还是坚持了下来,一切正常。他在我搞清楚这件事的时候发布了他的最新消息。我在这里发布了一些额外的代码示例,并接受了他的回答。首先,我分配:

Application.OnMessage := AppMessage;

那么程序如下:

procedure TfrmAppointment.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
// This first check decides if we are minimizing via the upper right button OR
// The context menu in the upper left hand corner of the window. 
// Minimizing twice restores, so this can be a restore as well.
if ((((Msg.message = WM_NCLBUTTONDOWN) and (Msg.wParam = HTMINBUTTON)) or
     ((Msg.message = WM_SYSCOMMAND) and (Msg.wParam = SC_MINIMIZE))) and
    (Screen.ActiveForm = Self)) then
   begin
   // This function is defined as (bool, bool) where the variables are:
   // Param1: Mimimizing (true), Restoring (false)
   // Param2: Draw animation rectangles for doing this or not
   Handled := MinimizeOrRestore(Self.WindowState <> wsMinimized, True);
   end
else if ((Msg.message = WM_SYSCOMMAND) and 
         (Msg.wParam = SC_RESTORE) and 
         (Screen.ActiveForm = Self)) then
   begin
   // Specifically, restore has been asked for
   Handled := MinimizeOrRestore(False, True); // Minimize with animation
   end
else if ((Msg.message = WM_SYSCOMMAND) and (Msg.wParam = SC_CLOSE)) then
   begin
   // The user just used the system menu to close the application
   ApplicationIsClosing := True; // see below for this
   end
end;

然后在我的FormCloseQuery中,我检查“Application ationIsCarly”是否为真。如果是FALSE,那么我知道用户点击了X,我只需最小化调用此处引用的其他函数的应用程序。如果是真的,我允许关闭。

最后,MinimizeOnRestore获取表单本身的树以及系统托盘,然后执行DrawAnimatedRects。这并不总是适用于Vista或更高版本,但也不会出错。接下来,它会隐藏主应用程序窗口或使其可见。除非遇到错误,否则它总是返回true。然后返回false。

姬振
2023-03-14

你应该设定

Application.MainFormOnTaskBar := True;

在你的房间里。dpr文件,然后永远不要修改该设置。

然后,当您想从任务栏中删除主窗体时,您只需编写

MainForm.Hide;

当你需要把主窗体从隐藏中拿出来时,再写一次

MainForm.Show;

就这样。

当然,你会想显示和隐藏你的通知区图标,同时隐藏和显示主窗体。

HideWindowFromTaskbar中的代码不是必需的,您应该将其删除。当应用程序处于MainFormOnTaskBarequalsTrue模式时,主窗体是一个非自有的顶级窗口。因此,只要它是可见的,它就会出现在任务栏上。所以你可以从任务栏中删除主窗体,只需我隐藏它。

应用程序中的其他表单将由顶级窗口拥有。通常,它们将由主窗体拥有。由于是所有者,它们不会出现在任务栏上。

总的来说,你应该尽量避免摆弄窗户样式。您通常可以让应用程序按照您需要的方式运行,而无需这样做。此外,如果需要调整窗口样式,则必须在CreateParams中进行调整。这样,重新创建窗口时,窗口样式将保持不变。但我重复了一遍,尽量避免修改窗口样式。

MSDN的主要参考资料包括:

  • 窗口功能
  • 任务栏

以下是我能制作的证明这一点的最小程序:

program MainFormHiding;

uses
  Forms, StdCtrls;

var
  MainForm, OtherForm: TForm;
  Button: TButton;

type
  TEventHandlerClass = class
    class procedure ToggleMainFormVisible(Sender: TObject);
  end;

class procedure TEventHandlerClass.ToggleMainFormVisible(Sender: TObject);
begin
  MainForm.Visible := not MainForm.Visible;
end;

begin
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm, MainForm);
  OtherForm := TForm.Create(Application);
  MainForm.Caption := 'Main Form';

  OtherForm.Visible := True;
  OtherForm.Caption := 'Other Form';
  Button := TButton.Create(OtherForm);
  Button.Caption := 'Toggle';
  Button.Parent := OtherForm;
  Button.OnClick := TEventHandlerClass.ToggleMainFormVisible;

  Application.Run;
end.

在评论中,您明确表示希望能够在不隐藏主窗体的情况下隐藏任务栏窗口。在这种情况下,我建议您将MainFormOnTaskbar设置为False。这意味着应用程序。Handle将是与任务栏按钮关联的窗口。然后可以隐藏该窗口,将其从任务栏中删除。

现在,您需要为任何辅助表单显式设置poupparent。如果希望这些窗口归主窗体所有,则可以对其进行设置。

这是我针对此场景调整的示例:

program MainFormHiding;

uses
  Forms, StdCtrls, Windows;

var
  MainForm, OtherForm: TForm;
  Button: TButton;

type
  TEventHandlerClass = class
    class procedure ToggleTaskbarButton(Sender: TObject);
  end;

class procedure TEventHandlerClass.ToggleTaskbarButton(Sender: TObject);
begin
  if IsWindowVisible(Application.Handle) then
    ShowWindow(Application.Handle, SW_HIDE)
  else
    ShowWindow(Application.Handle, SW_SHOW);
end;

begin
  Application.MainFormOnTaskbar := False;
  Application.CreateForm(TForm, MainForm);
  OtherForm := TForm.Create(Application);
  OtherForm.PopupParent := MainForm;
  MainForm.Caption := 'Main Form';
  Application.Title := MainForm.Caption;

  OtherForm.Visible := True;
  OtherForm.Caption := 'Other Form';
  Button := TButton.Create(OtherForm);
  Button.Caption := 'Toggle';
  Button.Parent := OtherForm;
  Button.OnClick := TEventHandlerClass.ToggleTaskbarButton;

  Application.Run;
end.

运行此程序并单击切换按钮。现在你将看到主窗体和其他窗体显示。任务栏上什么也没有。我包括了切换按钮,以显示程序运行时,您可以在两种操作模式之间切换。不需要重新启动它。

这里的关键是将一个非可见窗体的窗口设置为与任务栏关联的窗口。一旦你这样做了,你可以再次通过显示和隐藏该窗口来控制任务栏的存在。在本例中,该窗口是应用程序窗口,application。句柄。因为这是任务栏上的窗口,所以需要设置其Title属性来控制其文本。

最后,我再次强调,与任务栏的交互最好通过窗口所有者和可见性来控制。始终使用这些方法而不是ITaskbarList、扩展窗口样式等搜索解决方案。

使现代化

希望是关于这个主题的最后一句话。正如您所注意到的,当主窗体最小化时,正上方的代码行为不佳。发生这种情况时,应用程序窗口再次可见,因此再次出现在任务栏中。

说到抑制这种行为,我对自己不是很有把握。这种行为是由TApplication中的代码引起的。最小化,当主窗体最小化时显示应用程序句柄。我的最佳解决方案是将主窗体最小化转换为隐藏。

procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;

....

procedure TMainForm.WMSysCommand(var Msg: TWMSysCommand);
begin
  if (Msg.CmdType and $FFF0)=SC_MINIMIZE then 
  begin
    Hide;
    exit;
  end;
  inherited;
end;

或者另一种方法是通过OnMinimize事件处理程序来抑制应用程序窗口的显示,该事件处理程序用于TApplication

class procedure TEventHandlerClass.ApplicationMinimize(Sender: TObject);
begin
  ShowWindow(Application.Handle, SW_HIDE);
end;
 类似资料:
  • 我有一个MFC应用程序。在我的应用程序中,如果我在Windows XP上运行,它工作正常。但如果我在Windows Vista中运行,MFC对话框会隐藏在任务栏后面。 我使用了这个代码,但它隐藏了任务栏。但我想在任务栏上方显示应用程序。

  • 问题内容: 首先,我是一位经验丰富的C程序员,但是刚接触python。我想使用pyqt在python中创建一个简单的应用程序。让我们想象一下这个应用程序,就像它在运行时一样简单,它必须在系统托盘中放置一个图标,并且在菜单中提供了退出该应用程序的选项。 这段代码有效,它显示了菜单(为了简化起见,我没有连接退出动作,依此类推) 但这不是: 我可能想念一些东西。没有错误,但是在第二种情况下,当我单击鼠标

  • 我有一个用LabVIEW创建的应用程序,我需要在运行时在Windows任务栏上显示/隐藏应用程序图标。我认为WINAPI可以用于此目的,我尝试使用ShowWindow函数(user32.dll) 显示窗口(hWnd,SW_HIDE)- 显示窗口(hWnd,SW_SHOWMINIMIZED)-

  • 问题内容: 我正在尝试在Windows中显示。如何在Windows任务栏上显示(如)? 问题答案: 对话框本身不能具有任务栏条目,但是您可以构造一个没有任何可见效果的框架,并将其用作对话框的父级。然后,该对话框看起来像具有任务栏条目。以下代码显示了如何执行此操作:

  • 我有一个简单的控制台应用程序,有时需要执行图形操作,对于那些我使用JavaFx框架(有一些功能,我需要像css样式的文本)我只是生成一些形状和文本到一个隐藏的场景,然后保存在文件中仅此而已, 我知道要使用JavaFx,我必须将图形操作传递给JavaFx线程,但是当一切都完成后,我必须关闭应用程序(几个小时后),这个JavaFx线程仍然保持打开...我真的不想强行退出System.exit(),因为

  • 所以我有以下代码: 运行良好,闪屏显示得与我想要的完全一样,但它不在任务栏中显示图标,因此可以单击另一个窗口,再也看不到闪屏栏(它隐藏在其他窗口后面)。br> 我已经尝试使用窗口标志 ,但是如果我使用它,任务栏中仍然没有图标,而且现在它总是位于所有其他窗口的顶部(这是我不想要的)。br> 到目前为止,我已经浏览了一些窗口标志,并在谷歌上搜索了很长一段时间,我试图显示它。br> 另外,我知道我可以给