Win32:全屏和隐藏任务栏

40

我有一个窗口,我使用 SetWindowPos(window, HWND_TOP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_FRAMECHANGED); 命令设置它。

它覆盖了整个屏幕,但是要花费一定时间(0.5秒)来覆盖任务栏。

有没有一种方法可以立即遮住任务栏?我发现将窗口设置为 HWND_TOPMOST 可以立即实现,但它会一直处于所有其他窗口之上,即使我切换应用程序-这不是我想要的结果。此外,如果我首先隐藏窗口然后再显示它,它会强制窗口重新绘制并立即覆盖任务栏,但它会闪烁(因为它被隐藏了)。还有其他方法吗?

6个回答

53
编辑2. 还有一个更好的全屏方式,即使用Chromium的方式。源码来自这里:

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上正常工作。


4
-1 是因为你不应该去调整任务栏;在大多数情况下,这并不是必要的。请参考雷蒙德·陈在这篇博客中所说的内容:如何用全屏窗口遮盖任务栏? - BrendanMcK
太棒了,-1 转换为 +1。(实际上我在一开始就太急于使用 -1 了;我应该先留下评论。这些年来,我想我对看到 Windows 黑客攻击变得过于敏感了 -!) - BrendanMcK
@BrendanMcK:不不,你说得对,隐藏任务栏是一个非常糟糕的想法,它应该受到惩罚。最近我在测试我的一个应用程序时使用了全屏功能(5 Minute Break),但在Win7上崩溃了,用户因此失去了任务栏。我在Google Chrome文件(wtl库)中看到过这种方法,这就是这个想法的来源,但似乎他们没有使用它,因为我刚才测试杀死chrome.exe进程,它并没有隐藏任务栏。 - Czarek Tomczak
这很棒,运行良好。唯一的问题是,在全屏之前,必须确保已释放鼠标(捕获)如果你一直按住它。如果不这样做,可能会发生不好的事情。 - Robinson
3
@BrendanMcK,你的链接在2016年已经失效。以下是新链接:如何在全屏窗口下覆盖任务栏? - frogpelt
1
新版本适用于创建窗口,但对于现有窗口的全屏/窗口模式是否同样适用呢? - Robinson

21

没错,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上尝试过),这意味着它将更加快速。如果我在缩小代码(从我更大的代码中)时出现任何错误,请告诉我。


我在某个地方读到过窗口管理器有一种“缓存”。因此,我希望有一种方法可以强制刷新它,或者告诉它应该重新绘制任务栏。 - Lars Kanto
换言之,我觉得“SW_MAXIMIZE”除了调整大小之外还执行了其他操作,我想知道是什么... - Lars Kanto
关于重新绘制任务栏,那被认为是一个单独的程序,你不应该(不能?)处理它的重新绘制。然而,如果你正在为Windows Mobile编程,你可以找到“任务栏”并隐藏/移动(我两者都做)。使用“FindWindow(TEXT("HHTaskBar"), NULL);”来查找它(它返回一个窗口句柄),然后调用ShowWindow()或MoveWindow()来修改它(并记得在退出时恢复它)。我不确定普通窗口是否有相应的功能。 - Warpspace
2
请注意如果您不需要更改分辨率,则无需调用 ChangeDisplaySettings。 CDS_FULLSCREEN 标志实际上并没有让应用程序全屏,它只是表示“这些设置是临时的,就像由正在全屏的窗口使用”。关于此标志的更多详细信息,请参阅 Raymond Chen 的博客:[当显示更改是临时的时,这意味着什么?](http://blogs.msdn.com/b/oldnewthing/archive/2008/01/04/6973747.aspx) - BrendanMcK
@Warpspace,你能详细解释一下“独占模式的等效物”吗?我正在调查如何将应用程序从窗口模式切换到独占模式,并且已经查找了你上面使用的每个API,但仍不清楚哪个部分实现了独占模式。谢谢! - yoyo
显示剩余2条评论

10

2
这是一个有用的助手,可以将文本翻译成中文。

这里是最新未损坏的链接Raymond Chen的回答。

由于MSDN/Microsoft经常会破坏链接,为了保留记录,我将在下面粘贴:


由于某种原因,人们想太多了。如果您想创建一个全屏窗口以覆盖任务栏,只需创建一个全屏窗口,任务栏将自动躲开。不要四处寻找任务栏并戳它;让它发挥作用。
像往常一样,从Scratch程序开始,然后添加以下内容:
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函数。
我们使用MonitorFromWindow函数来确定应该在哪个显示器上全屏。请注意,在多显示器系统中,这可能不是任务栏所在的显示器。幸运的是,我们不必担心这个问题;任务栏会自动解决。
我见过人们寻找任务栏窗口,然后对其执行ShowWindow(hwndTaskbar, SW_HIDE)。这种做法有很多问题。
首先,你应该始终使用一种思维方式来评估这种技巧:“如果两个程序都尝试使用这个技巧会怎样?”现在你有两个程序都认为它们负责隐藏和显示任务栏,而且两者之间没有协调。结果就是一团糟。一个程序隐藏了任务栏,然后另一个程序也这样做了,然后第一个程序决定完成了,所以它取消隐藏任务栏,但第二个程序还没有完成,当它认为任务栏应该被隐藏时却看到了一个可见的任务栏。事情只会变得更糟。
第二,如果您的程序在取消隐藏任务栏之前崩溃了怎么办?任务栏现在被永久隐藏,用户必须注销并重新登录才能恢复他们的任务栏。那不太好。
第三,如果根本没有任务栏呢?在终端服务器方案中常见的是让程序单独运行而没有资源管理器存档)。在这种配置下,没有资源管理器,也没有任务栏。或者,也许您正在运行一个未来版本的 Windows,它已经被某种其他机制所取代。现在您的程序会怎么做?
不要去处理任务栏。只需创建全屏窗口并让任务栏自动执行其功能。

1

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

如果您从窗口HWND_TOPMOST开始,并在1秒后将其取消置顶呢?


-6
  1. 右键单击任务栏
  2. 选择 属性
  3. 取消勾选“将任务栏置于其他窗口之上”的复选框。

任务栏属于用户,他们应该关心当您的应用程序全屏时它需要1/2秒钟自动隐藏。如果他们想要改变这种行为,那么他们可以改变它。

如果您在嵌入式系统中工作,则可能有合理的理由隐藏任务栏。但在这种情况下,没有理由不将任务栏配置为不始终置于顶部。如果您想在代码中更改其中一些设置,也可以查看SystemParametersInfo


2
好的,每次开始游戏之前,您都想在执行这三个步骤后恢复旧状态吗?真的吗? - user2328447
user2328447,你在逗我吗?当游戏全屏时,任务栏的状态会被忽略 - 因此,游戏干扰任务栏既愚蠢又有问题 - 但是你肯定已经注意到了这一点... - John Knoeller
在窗口全屏模式下,任务栏不会被忽略。这正是原帖作者所要求的。有几款游戏使用了这种模式。 - user2328447

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接