【问题标题】:Win32: full-screen and hiding taskbarWin32:全屏和隐藏任务栏
【发布时间】:2011-01-23 20:36:14
【问题描述】:

我有一个窗口,我SetWindowPos(window, HWND_TOP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_FRAMECHANGED);

它覆盖了整个屏幕,好的,但也需要一段时间(0.5 秒)才能覆盖任务栏。

有没有办法立即越过任务栏?我发现设置HWND_TOPMOST 会立即执行此操作,但即使我切换应用程序,它也会保持在所有其他窗口之上——这是我不想要的。此外,如果我先隐藏窗口然后显示它,它会以某种方式强制窗口重绘并立即覆盖任务栏,但它会闪烁(因为隐藏)。还有其他方法吗?

【问题讨论】:

    标签: winapi fullscreen taskbar


    【解决方案1】:

    编辑 2。甚至还有一种更好的全屏方式,铬方式,来源取自这里:

    http://src.chromium.org/viewvc/chrome/trunk/src/ui/views/win/fullscreen_handler.cc?revision=HEAD&view=markup

    void FullscreenHandler::SetFullscreenImpl(bool fullscreen, bool for_metro) {
      ScopedFullscreenVisibility visibility(hwnd_);
    
      // Save current window state if not already fullscreen.
      if (!fullscreen_) {
        // Save current window information.  We force the window into restored mode
        // before going fullscreen because Windows doesn't seem to hide the
        // taskbar if the window is in the maximized state.
        saved_window_info_.maximized = !!::IsZoomed(hwnd_);
        if (saved_window_info_.maximized)
          ::SendMessage(hwnd_, WM_SYSCOMMAND, SC_RESTORE, 0);
        saved_window_info_.style = GetWindowLong(hwnd_, GWL_STYLE);
        saved_window_info_.ex_style = GetWindowLong(hwnd_, GWL_EXSTYLE);
        GetWindowRect(hwnd_, &saved_window_info_.window_rect);
      }
    
      fullscreen_ = fullscreen;
    
      if (fullscreen_) {
        // Set new window style and size.
        SetWindowLong(hwnd_, GWL_STYLE,
                      saved_window_info_.style & ~(WS_CAPTION | WS_THICKFRAME));
        SetWindowLong(hwnd_, GWL_EXSTYLE,
                      saved_window_info_.ex_style & ~(WS_EX_DLGMODALFRAME |
                      WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
    
        // On expand, if we're given a window_rect, grow to it, otherwise do
        // not resize.
        if (!for_metro) {
          MONITORINFO monitor_info;
          monitor_info.cbSize = sizeof(monitor_info);
          GetMonitorInfo(MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONEAREST),
                         &monitor_info);
          gfx::Rect window_rect(monitor_info.rcMonitor);
          SetWindowPos(hwnd_, NULL, window_rect.x(), window_rect.y(),
                       window_rect.width(), window_rect.height(),
                       SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
        }
      } else {
        // Reset original window style and size.  The multiple window size/moves
        // here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be
        // repainted.  Better-looking methods welcome.
        SetWindowLong(hwnd_, GWL_STYLE, saved_window_info_.style);
        SetWindowLong(hwnd_, GWL_EXSTYLE, saved_window_info_.ex_style);
    
        if (!for_metro) {
          // On restore, resize to the previous saved rect size.
          gfx::Rect new_rect(saved_window_info_.window_rect);
          SetWindowPos(hwnd_, NULL, new_rect.x(), new_rect.y(),
                       new_rect.width(), new_rect.height(),
                       SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
        }
        if (saved_window_info_.maximized)
          ::SendMessage(hwnd_, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
      }
    }
    

    编辑。 正如 BrendanMcK 在对此答案的评论中指出的那样,创建全屏窗口可能会更好,请参阅此链接:http://blogs.msdn.com/b/oldnewthing/archive/2005/05/05/414910.aspx(“如何用全屏窗口覆盖任务栏?”)

    使用上面链接的新代码是:

    HWND CreateFullscreenWindow(HWND hwnd)
    {
     HMONITOR hmon = MonitorFromWindow(hwnd,
                                       MONITOR_DEFAULTTONEAREST);
     MONITORINFO mi = { sizeof(mi) };
     if (!GetMonitorInfo(hmon, &mi)) return NULL;
     return CreateWindow(TEXT("static"),
           TEXT("something interesting might go here"),
           WS_POPUP | WS_VISIBLE,
           mi.rcMonitor.left,
           mi.rcMonitor.top,
           mi.rcMonitor.right - mi.rcMonitor.left,
           mi.rcMonitor.bottom - mi.rcMonitor.top,
           hwnd, NULL, g_hinst, 0);
    }
    

    下面的旧答案 - 不要使用它,仅用于记录如何不这样做。

    您必须隐藏任务栏和菜单栏才能立即全屏显示。

    这里是代码(使用 WTL),调用 SetFullScreen(true) 进入全屏模式:

    template <class T, bool t_bHasSip = true>
    class CFullScreenFrame
    {
    public:
        bool m_fullscreen;
        LONG m_windowstyles;
        WINDOWPLACEMENT m_windowplacement;
    
        CFullScreenFrame() 
            :
            m_fullscreen(false),
            m_windowstyles(0)
        { }
    
        void SetFullScreen(bool fullscreen)
        {
            ShowTaskBar(!fullscreen);
    
            T* pT = static_cast<T*>(this);
    
            if (fullscreen) {
                if (!m_fullscreen) {
                    m_windowstyles = pT->GetWindowLongW(GWL_STYLE);
                    pT->GetWindowPlacement(&m_windowplacement);
                }
    
            }
    
            // SM_CXSCREEN gives primary monitor, for multiple monitors use SM_CXVIRTUALSCREEN.
            RECT fullrect = { 0 };              
            SetRect(&fullrect, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
    
            WINDOWPLACEMENT newplacement = m_windowplacement;
            newplacement.showCmd = SW_SHOWNORMAL;
            newplacement.rcNormalPosition = fullrect;
    
            if (fullscreen) {
                pT->SetWindowPlacement(&newplacement);
                pT->SetWindowLongW(GWL_STYLE,  WS_VISIBLE);
                pT->UpdateWindow();
            } else {
                if (m_fullscreen) {
                    pT->SetWindowPlacement(&m_windowplacement);
                    pT->SetWindowLongW(GWL_STYLE, m_windowstyles);
                    pT->UpdateWindow();
                }
            }
    
            m_fullscreen = fullscreen;
        }
    
        void ShowTaskBar(bool show)
        {
            HWND taskbar = FindWindow(_T("Shell_TrayWnd"), NULL);
            HWND start = FindWindow(_T("Button"), NULL);
    
            if (taskbar != NULL) {
                ShowWindow(taskbar, show ? SW_SHOW : SW_HIDE);
                UpdateWindow(taskbar);
            }
            if (start != NULL) { 
                // Vista
                ShowWindow(start, show ? SW_SHOW : SW_HIDE);
                UpdateWindow(start);
            }       
        }
    };
    

    您还必须在 WM_CLOSE 消息中添加一些代码:

    case WM_CLOSE:
        ShowTaskBar(true);
    

    此解决方案有一个警告,如果您的应用程序崩溃或被任务管理器杀死,那么用户将永久丢失其系统上的任务栏! (除非他再次运行您的应用程序,进入全屏并退出,否则他将再次看到任务栏)。

    在我之前的回答中,我指出了“atlwince.h”,但该功能仅适用于 Windows CE,我在上面粘贴的那个适用于 XP、Vista 和 7。

    【讨论】:

    • -1 因为你不应该弄乱任务栏;一开始就不需要它;请在此处查看 Raymond Chen 的博客:How do I cover the taskbar with a fullscreen window?
    • 太棒了,-1 转换为 +1。 (实际上,一开始我对那个 -1 有点太快了;我应该先留下评论。我认为这些年来我对看到 windows hack 变得过于敏感了 - !)
    • @BrendanMcK:不不,你是对的,隐藏任务栏是一个非常糟糕的主意,应该受到惩罚。最近我正在测试我的一个使用全屏功能 (5 Minute Break) 的应用程序,它在 Win7 上崩溃,因此用户丢失了任务栏。我在一个 Google Chrome 文件(wtl 库)中看到了这种方法,这就是这个想法的来源,但似乎他们没有使用它,因为我刚才测试过杀死 chrome.exe 进程,但它没有t 隐藏任务栏。
    • @BrendanMcK 您的链接在 2016 年不再有效。这是新链接:How do I cover the taskbar with a fullscreen window?
    • 新版本对创建窗口有效,但同样适用于与现有窗口进行全屏/窗口化?
    【解决方案2】:

    是的,HWND_TOPMOST 为我做。 这里有一段代码让我可以很好地(并且快速地)全屏显示:

    
    bool enterFullscreen(HWND hwnd, int fullscreenWidth, int fullscreenHeight, int colourBits, int refreshRate) {
        DEVMODE fullscreenSettings;
        bool isChangeSuccessful;
        RECT windowBoundary;
    
        EnumDisplaySettings(NULL, 0, &fullscreenSettings);
        fullscreenSettings.dmPelsWidth        = fullscreenWidth;
        fullscreenSettings.dmPelsHeight       = fullscreenHeight;
        fullscreenSettings.dmBitsPerPel       = colourBits;
        fullscreenSettings.dmDisplayFrequency = refreshRate;
        fullscreenSettings.dmFields           = DM_PELSWIDTH |
                                                DM_PELSHEIGHT |
                                                DM_BITSPERPEL |
                                                DM_DISPLAYFREQUENCY;
    
        SetWindowLongPtr(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_TOPMOST);
        SetWindowLongPtr(hwnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
        SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, fullscreenWidth, fullscreenHeight, SWP_SHOWWINDOW);
        isChangeSuccessful = ChangeDisplaySettings(&fullscreenSettings, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL;
        ShowWindow(hwnd, SW_MAXIMIZE);
    
        return isChangeSuccessful;
    }
    

    请注意,如果您告诉它错误的设置,这将改变分辨率。这是我通常想要的,但如果你不喜欢这样,你可以通过使用来找出你的分辨率(其中mainWindow 是从CreateWindow()CreateWindowEx() 之类的东西返回的):

    
    windowHDC = GetDC(mainWindow);
    fullscreenWidth  = GetDeviceCaps(windowHDC, DESKTOPHORZRES);
    fullscreenHeight = GetDeviceCaps(windowHDC, DESKTOPVERTRES);
    colourBits       = GetDeviceCaps(windowHDC, BITSPIXEL);
    refreshRate      = GetDeviceCaps(windowHDC, VREFRESH);
    

    当你想退出全屏时,你可以这样做:

    
    bool exitFullscreen(HWND hwnd, int windowX, int windowY, int windowedWidth, int windowedHeight, int windowedPaddingX, int windowedPaddingY) {
        bool isChangeSuccessful;
    
        SetWindowLongPtr(hwnd, GWL_EXSTYLE, WS_EX_LEFT);
        SetWindowLongPtr(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
        isChangeSuccessful = ChangeDisplaySettings(NULL, CDS_RESET) == DISP_CHANGE_SUCCESSFUL;
        SetWindowPos(hwnd, HWND_NOTOPMOST, windowX, windowY, windowedWidth + windowedPaddingX, windowedHeight + windowedPaddingY, SWP_SHOWWINDOW);
        ShowWindow(hwnd, SW_RESTORE);
    
        return isChangeSuccessful;
    }
    

    我将代码设置为使用热键在全屏模式和窗口模式之间切换,并将窗口模式变量保持为全局变量,以便在切换到窗口模式时保持不变。

    这段代码还具有在相当于“独占模式”下运行的优势(我使用的是 XP,还没有在较新版本的 windows 上尝试过),这意味着它会快得多。如果我在压缩代码(来自我更大的代码)中犯了任何错误,请告诉我。

    【讨论】:

    • 感谢您的回复。我认为它起作用的原因是您至少将窗口大小调整了两次“SetWindowPos() 和 ShowWindow()”(我不知道“ChangeDisplaySettings()”)。问题是,如果我已经在“SW_MAXIMIZE”中会发生什么以及要全屏显示什么?
    • 我在某处读到有一种窗口管理器的“缓存”。所以我希望有一种方法可以强制它刷新或以其他方式告诉它应该重新绘制下面的任务栏。
    • 换句话说,在我看来,“SW_MAXIMIZE”除了调整大小还有其他作用,我想知道什么......
    • 抱歉我的回复晚了。据我所知,“SW_MAXIMIZE”只是告诉窗口你想要尽可能多的屏幕(就像关闭按钮旁边的窗口左上角的按钮一样)。我提供的代码依赖于我使用的函数调用的组合,而不仅仅是 ShowWindow() 中的设置。如果您已经在“SW_MAXIMIZE”中,此代码仍然有效。
    • 请注意,如果您不需要更改分辨率,则不需要调用 ChangeDisplaySettings。 CDS_FULLSCREEN 标志实际上并没有使应用程序全屏,它只是说“这些设置是临时的,就好像被全屏窗口使用一样”。有关此标志的更多详细信息,请参见 Raymond Chen 的博客:What does it mean when a display change is temporary?
    【解决方案3】:

    Raymond Chen 在他的博客中描述了执行此操作的“正确”方法:

    https://devblogs.microsoft.com/oldnewthing/20100412-00/?p=14353

    明确地摆弄任务栏窗口是推荐的行为。

    【讨论】:

      【解决方案4】:

      这是latest unbroken linkRaymond Chen 的答案。

      由于 MSDN/Microsoft 不断断开链接,我将在下面粘贴以供后代使用:


      出于某种原因,people think too hard。如果你想创建一个覆盖任务栏的全屏窗口,只需创建一个全屏窗口,任务栏就会自动消失。不要四处寻找任务栏并戳它;让它做它的事。

      一如既往,以the scratch program 开头并添加以下内容:

      HWND CreateFullscreenWindow(HWND hwnd)
      {
       HMONITOR hmon = MonitorFromWindow(hwnd,
                                         MONITOR_DEFAULTTONEAREST);
       MONITORINFO mi = { sizeof(mi) };
       if (!GetMonitorInfo(hmon, &mi)) return NULL;
       return CreateWindow(TEXT("static"),
             TEXT("something interesting might go here"),
             WS_POPUP | WS_VISIBLE,
             mi.rcMonitor.left,
             mi.rcMonitor.top,
             mi.rcMonitor.right - mi.rcMonitor.left,
             mi.rcMonitor.bottom - mi.rcMonitor.top,
             hwnd, NULL, g_hinst, 0);
      }
      
      void OnChar(HWND hwnd, TCHAR ch, int cRepeat)
      {
       if (ch == TEXT(' ')) {
        CreateFullscreenWindow(hwnd);
       }
      }
      
      HANDLE_MSG(hwnd, WM_CHAR, OnChar);
      

      请注意,此示例程序不担心破坏全屏窗口或阻止用户创建多个窗口。这只是一个样本。重点是看 CreateFullScreenWindow 函数是怎么写的。

      我们使用the MonitorFromWindow function 来确定我们应该全屏显示哪个显示器。请注意,在多显示器系统中,这可能与任务栏所在的显示器不同。幸运的是,我们不必担心这一点。任务栏算出来了。

      我见过人们寻找任务栏窗口,然后在其上执行 ShowWindow(hwndTaskbar, SW_HIDE)。这很疯狂,原因有很多。

      首先是一个心理练习,您在评估类似这样的技巧时应该始终使用:“如果两个程序尝试了这个技巧怎么办?”现在你有两个程序,它们都认为它们负责隐藏和显示任务栏,它们都没有相互协调。结果是一团糟。一个程序隐藏任务栏,然后另一个程序隐藏任务栏,然后第一个程序决定它已完成,因此它取消隐藏任务栏,但第二个程序尚未完成并在它认为应该隐藏时获得一个可见的任务栏。事情只会从那里走下坡路。

      其次,如果您的程序在有机会取消隐藏任务栏之前就崩溃了怎么办?任务栏现在永久隐藏,用户必须注销并重新登录才能恢复他们的任务栏。这不是很好。

      第三,如果根本没有任务栏怎么办?在终端服务器方案中,run programs by themselves without Explorer (archived) 很常见。在此配置中,没有资源管理器,也没有任务栏。或者,您可能正在运行没有任务栏的未来版本的 Windows,它已被其他一些机制取代。你的程序现在要做什么?

      不要对任务栏做任何这样的事情。只需创建您的全屏窗口并让任务栏自动完成它的工作。

      【讨论】:

        【解决方案5】:

        我相信当它的外壳钩子告诉它一个“粗鲁的应用程序”时,任务栏会离开,这可能需要一点时间。

        如果您从窗口 HWND_TOPMOST 开始并使其在 1 秒后不是最顶部怎么办?

        【讨论】:

          【解决方案6】:
          1. 右键单击任务栏
          2. 选择属性
          3. 取消选中“将任务栏保持在其他窗口之上”的复选框。

          任务栏属于用户,当你的应用程序全屏显示时,需要 1/2 秒的时间自动隐藏,这取决于他们。如果他们想改变这种行为,那么他们可以改变它。

          如果您在嵌入式系统中工作,那么您可能有正当理由隐藏任务栏。但在这种情况下,没有理由不简单地将任务栏配置为不总是在顶部。如果您想更改代码中的某些设置,也可以查看SystemParametersInfo

          【讨论】:

          • 好吧,每次开始游戏,你要先做这三个步骤,然后再恢复旧状态?真的吗?
          • user2328447,你在拖钓吗?当游戏进入全屏状态时,任务栏的状态会被忽略——因此游戏与任务栏混淆既愚蠢又糟糕——但你肯定已经注意到这一点......
          • 在窗口全屏中,任务栏不会被忽略。这正是原始海报所要求的。有几个游戏使用了这个。
          猜你喜欢
          • 1970-01-01
          • 2012-11-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-03-21
          相关资源
          最近更新 更多