Win32: full-screen and hiding taskbar
Asked Answered
T

6

41

I have a window, which I SetWindowPos(window, HWND_TOP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_FRAMECHANGED);

It covers the whole screen, ok, but it takes a while (0.5 sec) to cover the taskbar as well.

Is there a way to come over the taskbar immediately? I found that setting HWND_TOPMOST does it immediately, but it stays above all the other windows, even if I switch the app - this is something I don't want. Also, if I first hide the window and then show it, it somehow forces the window to redraw and covers the taskbar immediately, but it flickers (because of the hiding). Is there another way?

Tad answered 4/3, 2010 at 20:23 Comment(0)
D
54

Edit 2. There is even a better way for doing fullscreen, the chromium way, source taken from here:

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);
  }
}

Edit. It is probably better to create a fullscreen window as BrendanMcK pointed it out in a comment to this answer, see this link: http://blogs.msdn.com/b/oldnewthing/archive/2005/05/05/414910.aspx ("How do I cover the taskbar with a fullscreen window?")

The new code using the link above would be:

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);
}

Old answer below - do not use it, stays only for the record on how NOT to do this.

You have to hide taskbar and menubar to see fullscreen immediately.

Here is the code (uses WTL), call SetFullScreen(true) to go into full screen mode:

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);
        }       
    }
};

You also have to add some code to WM_CLOSE message:

case WM_CLOSE:
    ShowTaskBar(true);

There is one caveat with this solution, if your application crashes or is killed through task manager, then user losses taskbar on his system permanently! (unless he runs your application again, goes into fullscreen and exits, then he will see the taskbar again).

Earlier in my answer I pointed to "atlwince.h" but that function worked only on Windows CE, the one I pasted above works fine with XP, Vista and 7.

Draught answered 14/3, 2011 at 14:10 Comment(6)
-1 because you shouldn't be messing with the taskbar; it's not needed in the first place; see Raymond Chen's blog about this here: How do I cover the taskbar with a fullscreen window?Volumetric
Awesome, -1 converted to +1. (Actually I was a bit too quick with that -1 in the first place; I should have just left the comment first. I think I've become over-sensitive to seeing windows hacks over the years - !)Volumetric
@BrendanMcK: no no, you're right, it's a very bad idea with that taskbar hidden and it deserves punishment. Recently I was beta-testing one of my apps that was using the fullscreen feature (5 Minute Break) and it was crashing on Win7, users lost their taskbar because of that. I've seen this method in one of Google Chrome files (wtl library), that's where this idea came from, but seems like they do not use it, as I've tested killing chrome.exe processes a moment ago and it didn't hide the taskbar.Draught
This is great, works well. The only issue with it is you must make sure you've released the mouse (capture) if you hold it, before going full-screen. If you don't do this nasty things may happen.Rhyton
@Volumetric Your link is no longer valid in 2016. Here's the new link: How do I cover the taskbar with a fullscreen window?Fossil
The new version is valid for creating a window but does the same apply for going fullscreen/windowed with an existing one?Rhyton
S
21

Yup, HWND_TOPMOST does it for me. Here is a section of code that makes full-screen work well (and quick) for me:


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;
}

Note that this will change the resolution if you tell it the wrong settings. This is what I usually want, but if you don't like that, you can find out your resolution by using (where mainWindow is returned from something like CreateWindow() or CreateWindowEx()):


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

When you want to get out of full-screen you do something like this:


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;
}

I set my code to change between full-screen and windowed mode using a hotkey, and I keep the windowed mode variables as global, so that when changing to windowed mode, it stays put.

This code also has the advantage of running in the equivalent of "exclusive mode" (I'm using XP, and haven't tried it on the newer versions of windows), which means it'll be much, much faster. Let me know if I've made any mistakes from condensing the code (from my much bigger code).

Scrivens answered 10/3, 2010 at 11:56 Comment(7)
Thanks for the reply. I think the reason why it works is that you resize the window at least twice "SetWindowPos() and ShowWindow()" (I don't know that "ChangeDisplaySettings()"). The problem is, what happens if I'm already in "SW_MAXIMIZE" and what to go full-screen?Tad
I read somewhere that there is kind of "cache" of the window manager. So I was hoping there is a way to force it to refresh or otherwise tell it that it should repaint the taskbar beneath.Tad
In other words, it seems to me that "SW_MAXIMIZE" does something besides the resizing and I would like to know what...Tad
Sorry for my late reply. As far as I know, "SW_MAXIMIZE" just tells windows that you want as much screen as possible (just like the button on the top-left of a window next to the close button). The code I provided relies on the combination of function calls I used, rather than just the setting in ShowWindow(). If you're already in "SW_MAXIMIZE", this code still works.Scrivens
As for repainting the taskbar, that's considered a separate program, and you're not supposed to (can't?) handle it's repainting. If you're programming for Windows Mobile, however, you can find the "taskbar" and hide/move (I do both) it. Use "FindWindow(TEXT("HHTaskBar"), NULL);" to find it (it returns a window handle), and then call ShowWindow() or MoveWindow() to modify it (and remember to restore it on exit). I'm not sure if there's an equivalent for normal windows, though.Scrivens
Note that if you don't need to change the resolution, then the call to ChangeDisplaySettings is not needed. The CDS_FULLSCREEN flag doesn't actually make an app go fulscreen, it just says "these settings are temporary, as though used by a window that's going fullscreen". More details on this flag at Raymond Chen's blog here: What does it mean when a display change is temporary?Volumetric
@Warpspace, can you elaborate on "the equivalent of exclusive mode"? I'm investigating how to switch an app from windowed mode to exclusive mode, and having looked up every API you've used above I'm still unclear which part achieves exclusive mode. Thanks!Mccabe
R
10

Raymond Chen describes the "correct" way to do this at his blog:

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

Fiddling with the task bar window explicitly is not recommended behaviour.

Rebel answered 13/3, 2012 at 11:53 Comment(0)
M
2

Here's the latest unbroken link to Raymond Chen answer.

Since MSDN/Microsoft keeps breaking links I'll paste below for posterity:


For some reason, people think too hard. If you want to create a fullscreen window that covers the taskbar, just create a fullscreen window and the taskbar will automatically get out of the way. Don't go around hunting for the taskbar and poking it; let it do its thing.

As always, start with the scratch program and add the following:

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);

Note that this sample program doesn't worry about destroying that fullscreen window or preventing the user from creating more than one. It's just a sample. The point is seeing how the CreateFullScreenWindow function is written.

We use the MonitorFromWindow function to figure out which monitor we should go fullscreen to. Note that in a multiple monitor system, this might not be the same monitor that the taskbar is on. Fortunately, we don't have to worry about that; the taskbar figures it out.

I've seen people hunt for the taskbar window and then do a ShowWindow(hwndTaskbar, SW_HIDE) on it. This is nuts for many reasons.

First is a mental exercise you should always use when evaluating tricks like this: "What if two programs tried this trick?" Now you have two programs both of which think they are in charge of hiding and showing the taskbar, neither of which is coordinating with the other. The result is a mess. One program hides the taskbar, then the other does, then the first decides it's finished so it unhides the taskbar, but the second program wasn't finished yet and gets a visible taskbar when it thought it should be hidden. Things only go downhill from there.

Second, what if your program crashes before it gets a chance to unhide the taskbar? The taskbar is now permanently hidden and the user has to log off and back on to get their taskbar back. That's not very nice.

Third, what if there is no taskbar at all? It is common in Terminal Server scenarios to run programs by themselves without Explorer (archived). In this configuration, there is no Explorer, no taskbar. Or maybe you're running on a future version of Windows that doesn't have a taskbar, it having been replaced by some other mechanism. What will your program do now?

Don't do any of this messing with the taskbar. Just create your fullscreen window and let the taskbar do its thing automatically.

Maladjusted answered 15/4, 2020 at 20:2 Comment(0)
B
1

I believe the taskbar will get out of the way when its shell hook tells it about a "rude app", this might take a little while.

What if you start out with the window HWND_TOPMOST and make it not top most after 1 second?

Bravery answered 6/3, 2010 at 16:56 Comment(0)
A
-6
  1. Right click on the taskbar
  2. choose Properties
  3. uncheck the checkbox that says "Keep the taskbar on top of other windows".

The taskbar belongs to the user, It's up to them to care about having it take 1/2 second to auto-hide when you app goes full screen. If they want to change that behavior then they can change it.

If you are working in an embedded system, then you may have a legitimate reason to hide the taskbar. But in that case, there's no reason not to simply configure the taskbar to not always be on top. You could also have a look at SystemParametersInfo if you want to change some of these settings in your code.

Adlei answered 6/3, 2010 at 1:6 Comment(3)
OK, and each time you start a game, you want to do these three steps before, and afterwards restore the old state? Really?Oscillator
user2328447, are you trolling? The state of the taskbar is ignored when a game goes full screen - so it would be both stupid and broken for games to mess with the taskbar - but surely you've noticed this already....Adlei
In windowed fullscreen, the taskbar is NOT ignored. Which is exactly what the original poster asked for. Several games use this.Oscillator

© 2022 - 2024 — McMap. All rights reserved.