Related
SOLVED: I've posted my solution as an answer.
Here's my problem: (gif) (Sort of solved if I reload the bitmaps for painting the background image when unminimizing before any WM_PAINT message.)
It happens whenever I unminimize the application, time when the app first displays OK (for a brief split second unless you are stepping with the debugger), and suddenly turns black (or whatever color has been set as hbrBackground in the app window classes). I can prevent this behaviour by reloading the HBITMAPs used in WM_PAINT, which are global variables and initialised with their corresponding values at app startup.
The gif starts showing the app reopened after a minimize, with the debugger stepping through the parent window of the Rich Edit Control message loop, the moments just before and after the background of all windows turns black, and then stepping into the Rich Edit Control subclass message loop, into WM_PAINT.
This never happens if I'm switching between apps without the app in question never having been minimized before.
This never happens if the Rich Edit Control (RICHEDIT50W) hasn't displayed any text before, ie. the app works OK if no text is ever displayed.
This is the window tree:
Main Window
Some Child Windows
Child Window 1
Rich Edit Control
The stepping goes out of the Child Window 1 WndProc; into the WM_PAINT of the Rich Edit Control inside the WndSubclassProcWhatever callback.
Some of the things I've done before realizing that a call to LoadImage() just after unminimize could fix the background issue:
Intercept the message loop of the Rich Edit Control with a subclass, and handle (as well as in every other window) messages as: WM_COMMAND, WM_IME_NOTIFY, WM_NCPAINT, WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, WM_ERASEBKGND... Mainly returning something different than the DefSubclassProc/DefWindowProcW.
Calling ValidateRect() as soon as the app is reopened...
It has happened before that instead of the whole app turning black, only the text "highlighting" or the Rich Edit Control parent turned black, with the whole app turning black after another minimize unminimize cycle.
I'm using Visual Studio Community 2019 with default settings in an updated Windows 10, and seeing this problem both in release and debug builds.
I'm now looking forward to prevent the bitmaps from "unloading", thus saving many seemingly unnecessary LoadImage() calls. SOLVED
I tried uploading a minimal version of the code, yet the behaviour turned out not to be exactly the same, so thanks for the answer given before!
This has nothing to do with Rich Edit Control , even if you delete all of the controls, this will happen.
All you have to do is add a default color to the window background when you register the window.
Here:
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_DBLCLKS;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL; // Procesás WM_GETICON
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"mainWindowClass";
wcex.hIconSm = NULL; // Procesás WM_GETICON
return RegisterClassExW(&wcex);
}
Click again after minimize the window will cause it to redraw with the default background color, But you set the background color to NULL here. So try to change wcex.hbrBackground = NULL to wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1)
Updated:
It sounds like you have the same problem as I have encountered before.
Here is my previous code:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
hdcMem = CreateCompatibleDC(hdc);
HGDIOBJ previousbit = SelectObject(hdcMem, hBmp);
AlphaBlend(hdc, 0, 0, width_1, height_1, hdcMem, 0, 0, width_1, height_1, bf);
DeleteDC(hdcMem);
EndPaint(hWnd, &ps);
}
break;
case WM_MOUSEWHEEL:
{
if (GET_WHEEL_DELTA_WPARAM(wParam) > 0 && bTrans <= 234)
{
bTrans += 20;
bf.SourceConstantAlpha = bTrans;
InvalidateRect(hWnd, NULL, TRUE);
}
if (GET_WHEEL_DELTA_WPARAM(wParam) < 0 && bTrans >= 20)
{
bTrans -= 20;
bf.SourceConstantAlpha = bTrans;
InvalidateRect(hWnd, NULL, TRUE);
}
return 0;
}
I slide the mouse wheel, it will trigger the InvalidateRect(hWnd, NULL, TRUE);
But if I delete DeleteDC(hdcMem), it will return a main window without a picture.
The debug snapshot is :
Yes, you can find previousbit == NULL.
As #Remy Lebeau said that, you are leaking the HBITMAP that SelectObject() returns, and giving the HDC permission to potentially destroy your bitmapBackgroundMainWindow behind your back.
This is the main cause.
A call to DeleteObject() fixed the issue.
This is the code from one of the bitmap-background window WM_PAINT messages fixed with the corresponding DeleteObject() call:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
HDC temporaryDC = CreateCompatibleDC(hdc);
BITMAP bitmapToBitBlt;
HGDIOBJ hgdiobjToBitBlt = SelectObject(temporaryDC, bitmapBackgroundMainWindow);
GetObjectW(bitmapBackgroundMainWindow, sizeof(BITMAP), &bitmapToBitBlt);
BitBlt(hdc, 0, 0, bitmapToBitBlt.bmWidth, bitmapToBitBlt.bmHeight, temporaryDC, 0, 0, SRCCOPY);
DeleteObject(temporaryDC); // This fixes the app.
EndPaint(hWnd, &ps);
return 0;
As Windows Docs state, after calling CreateCompatibleDC():
When you no longer need the memory DC, call the DeleteDC function. We recommend that you call DeleteDC to delete the DC. However, you can also call DeleteObject with the HDC to delete the DC.
How does this translate to my app unexpected behaviour, I don't know, feel free to clarify in the comments!
I have read a lot of Stack Overflow over the years when struggling with making sense of Microsoft Windows' strange world of CreateWindowEx() .. etc. This question, when originally asked was "What is the best way to create a fluidly resizable OpenGL window in WinAPI?"
I've been struggling with getting WinAPI to make a window that:
Has an OpenGL context
Is properly centered on the main monitor (or any monitor determined by command line signal) in both multi-monitor and single-monitor displays when in "Windowed" mode or in "Fullscreen" mode
Has a fixed internal client screen size (viewport 2d)
Doesn't allow you to click outside causing it to lose focus at the wrong times or in special cases for multi-monitor
Can be resized fluidly, but doesn't change internal "client size" (meaning that it stretches the OpenGL content which is a fixed size to the new screen size) ... the idea here is to add a layer of virtualization, so that all pixels are expressed in the same 1920x1080 (1080p) coordinate system. This part is no problem for me.
Correctly handles mouse event translation from screen_size -> client_size equivalent via the screen->client ratio
In my homegrown App framework, I have to set the display size, and even then, Windows doesn't give me the right sized window. (Sometimes the title bar is subtracted, sometimes the scrollbars are subtracted, but the context draws under the title bar, for example.)
Also, recently when moving from 2010 EE (Win32 / Windows 7) to 2015 (win32 / Windows 10), I had to change the parameters to recenter the view because it was off-centered on the main display. Now, only sometimes are these values correct or incorrect. If I go "fullscreen" for example, the same values will draw above the top of the screen such that there is an area at the bottom of the screen that shows the "gl clear color" (in my case, orange)
I can play with these things by providing the following command line parameters:
-bordered (default, and has no effect really, is the default windowed mode with the title bar and such)
-borderless (seems to go into fullscreen mode, with the app off-center where win 0,0 is actually in screen center)
-windowed (or -window)
If I don't provide -window, it defaults to "full screen" resolution-adjusted (but only if supported I assume, otherwise it might throw an error).
Anyway, all of this is very bad because
a) I have to write a bajillion cases for each resolution I'm working in, rather than write everything for 1080p and have it adjust to display size, which is what i want because it handles most new displays on laptops and desktops (this is Windows remember) (and only slightly squishes things in those corner cases)
b) I cannot resize the window fluidly, also i have to trap the mouse at center and recalculate the mouse position, so I record only the deltas -- this is to avoid the mouse leaving the window and clicking the desktop, or floating off the monitor to some other monitor, even when it is hidden. I also have to make the mouse cursor invisible so the user doesn't see this, then show a simulated mouse cursor.
c) Users who don't support specifically 1920x1080 won't be able to use full screen mode
Someone pointed this article out in another question (window border width and height in Win32 - how do I get it?):
https://web.archive.org/web/20120716062211/http://suite101.com/article/client-area-size-with-movewindow-a17846
And I've read through this, learning that AdjustWindowRectEx() has some issues:
AdjustWindowRectEx() and GetWindowRect() give wrong size with WS_OVERLAPPED
I don't use WS_OVERLAPPED, so this was only moderately helpful:
AdjustWindowRectEx() and GetWindowRect() give wrong size with WS_OVERLAPPED
Here's how I do it now:
display.Resized(display.w,display.h);
// Fill in the window class structure for testing display type.
winclass.cbSize = sizeof(WNDCLASSEX);
winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc = WinProc;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hInstance = hinstance;
winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.lpszMenuName = NULL;
winclass.lpszClassName = WINDOW_CLASS_NAME;
// Save the game instance handle
display.hinstance = game_instance = hinstance;
// Register the window class
if (!RegisterClassEx(&winclass)) return(0);
if (!gl.Init(hinstance, display.bits)) {
return(0);
}
// Detect the display size and create the final display profile
DWORD winStyle=
WS_EX_APPWINDOW |
WS_EX_TOPMOST /*|
WS_EX_ACCEPTFILES*/ ;
// Adjust Window, Account For Window Borders
int xPos = GetSystemMetrics(SM_CXSCREEN) - display.w;
int yPos = GetSystemMetrics(SM_CYSCREEN) - display.h;
RECT windowRect = {0, 0, display.w, display.h}; // Define Our Window Coordinates
AdjustWindowRectEx (&windowRect, WS_POPUP, 0, winStyle );
// Create the window
if (!(hwnd = CreateWindowEx(
winStyle, // extended style
WINDOW_CLASS_NAME, // class
gl.winTitle.c_str(), // title
( gl.borderless || CmdLine.Option("-borderless") ) ? (WS_POPUPWINDOW | WS_VISIBLE)
: (gl.noFullscreen ? ((CmdLine.Option("-bordered") ? WS_BORDER : 0) | WS_VISIBLE)
: (WS_POPUP | WS_VISIBLE)), // use POPUP for full screen
gl.noFullscreen && !CmdLine.Option("-recenter") ? xPos / 2 : 0,
gl.noFullscreen && !CmdLine.Option("-recenter") ? yPos / 2 : 0, // initial game window x,y
display.w, // initial game width
display.h, // initial game height
HWND_DESKTOP, // handle to parent
NULL, // handle to menu
hinstance, // instance of this application
NULL)
) // extra creation parms
) {
OUTPUT("WinAPI ERROR: Could not open window.\n");
return(0);
}
if (gl.borderless || CmdLine.Option("-borderless") ) {
LONG lStyle = GetWindowLong(hwnd, GWL_STYLE);
lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU);
SetWindowLong(hwnd, GWL_STYLE, lStyle);
LONG lExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
lExStyle &= ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
SetWindowLong(hwnd, GWL_EXSTYLE, lExStyle);
SetWindowPos(hwnd, NULL, 0, 0, display.w, display.h, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
}
// Temporary change to full screen mode
ZeroMemory(&game_screen, sizeof(game_screen)); // clear out size of DEVMODE struct
game_screen.dmSize = sizeof(game_screen);
game_screen.dmPelsWidth = display.w;
game_screen.dmPelsHeight = display.h;
game_screen.dmBitsPerPel = display.bits;
game_screen.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
ChangeDisplaySettings(&game_screen, CDS_FULLSCREEN);
// save the game window handle
display.hwnd = game_window = hwnd;
display.hdc = game_dc = GetDC(display.hwnd = game_window); // get the GDI device context
// set up the pixel format desc struct
pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // size of this PFD
1, // version number
PFD_DRAW_TO_WINDOW | // supports window
PFD_SUPPORT_OPENGL | // supports OpenGL
PFD_DOUBLEBUFFER, // support double buff
PFD_TYPE_RGBA, // request RGBA format
(BYTE)display.bits, // select color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buff
0, // shift bit ignored
0, // no accum buff
0, 0, 0, 0, // accum bits ignored
16, // 16-bit Z-buff (depth buff)
0, // no stencil buff
0, // no aux buff
PFD_MAIN_PLANE, // main drawing layer
0, // reserved
0, 0, 0 // layer masks ignored
};
int pf; // pixel format
if (!gl.arbMultisampleSupported) {
if (!(pf = ChoosePixelFormat(game_dc, &pfd))) // match the pixel format
{
MessageBox(game_window, "OpenGL could not be initialized -- ChoosePixelFormat Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
return FALSE; // error returned
}
} else {
pf = gl.arbMultisampleFormat;
}
if (!SetPixelFormat(game_dc, pf, &pfd)) // set the pixel format
{
MessageBox(game_window, "OpenGL could not be initialized -- SetPixelFormat Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
return FALSE; // error returned
}
if (!(game_rc = wglCreateContext(game_dc))) // create the rendering context
{
MessageBox(game_window, "OpenGL could not be initialized -- CreateContext Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
return FALSE; // error returned
}
if (!(upload_rc = wglCreateContext(game_dc))) // create the rendering context
{
MessageBox(game_window, "Multiple OpenGL contexts could not be initialized -- CreateContext Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
return FALSE; // error returned
} else { // Share as much as you can between two contexts
if (!wglShareLists(game_rc, upload_rc)) {
// could use GetLastError here
MessageBox(game_window, "wglShareLists -- Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
return FALSE; // error returned
}
}
if (!wglMakeCurrent(game_dc, display.hglrc = game_rc)) // make it current
{
MessageBox(game_window, "OpenGL could not be initialized -- MakeCurrent Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
return FALSE; // error returned
}
ShowCursor(false);
ShowWindow(game_window, SW_SHOWNORMAL);
SetForegroundWindow(game_window);
In the above code, what I get is a window that has no resize functionality, hides the OS mouse cursor, and can only be exitted with ALT-TAB (or ALT-F4), and when it is exitted appears at the back of the windows Z-order. I always open my window using a parameter that sets display.w to 1920 and display.h to 1080, either in full screen or in Windowed mode. WM_SIZE is then called to adjust it to the client area.
Please note that the following WM_SIZE is called during the WinProc right after the initial time I set display.Resized(w,h):
case WM_SIZE:
{
display.Resized(LOWORD(lparam), HIWORD(lparam));
return (0);
}
break;
This is executed exactly once during app load, and in the first case it looks like the values are: 1918,1078
UPDATE: If I use the result of GetWindowRect() here, or GetClientRect() as shown below, the window mysteriously moves to Center-X,Center-Y of screen! What gives??
// RECT rect;
// if ( GetClientRect(hwnd,&rect) ) {
// display.Resized((int)rect.right,(int)rect.bottom);
// }
//if ( GetWindowRect( hwnd, &rect ) ) {
// display.Resized((int)ADIFF(rect.left,rect.right),(int)ADIFF(rect.top,rect.bottom));
//}
display.Resized(LOWORD(lparam), HIWORD(lparam));
return (0);
What steps do I need to take to make the window stretchable such that the context is resized to the view, and the mouse is properly adjusted based on the screen ratio?
Basically, there are too many edge cases to make sense of all of this. As time has gone on since the 2 years ago that I asked this question, I've had other inconsistencies between full screen and window emerge.
From what I understand there are basically 3 types of windows:
Your normal on-screen moveable/resizable window for windowing GUIs, like this browser window (if you are not on mobile)
One matched to a display's resolution support (including resolutions smaller than its native) -- we call this "Full screen" (or Fullscreen, which isn't even a word)
One that is a normal on-screen window, but lacks a title bar, borders and scroll bars, and appears as large as the screen. Referred to "on the street" as a "Borderless Window"
I want to master all of these but in a way that makes them all accessible and doesn't require special cases. I've basically given up on doing so with WinAPI, but obviously multiple companies do this. Following Microsoft's documentation isn't very helpful, and I've experimented with a lot of different CreateWindow CreateWindowEx -- many of these features are deprecated by the way, or don't work at all.
(Maybe the best question is, WHEN WILL MICROSOFT CLEAN UP THIS CRAP? But I think we all know the answer.) .. any help to get it working would be appreciated.
I'm now working in: C++, Windows API, OpenGL 3.x / 4.x, Windows 10.
Can we change/increase the size of console output to view large size of data in console application at once?
There seem to be different ways to Rome:
This should be the recommended way I would think, cause the name says it all: GetConsoleWindow as is demonstrated here.
A quick hack might be the windows API function SendInput. If you simulate Alt+Enter, you could probably fullscreen the active window.
Here are some propositions using API calls from user32.dll
Check out the SetConsoleScreenBufferInfoEx API. It takes a CONSOLE_SCREEN_BUFFER_INFOEX as input and that has a dwSize member which contains the size of the console screen buffer, in character columns and rows.
MSDN for SetConsoleScreenBufferInfoEx Function: http://msdn.microsoft.com/en-us/library/ms686039(v=VS.85).aspx
I once used a small hack that is first setting the console's output buffer and then trying to find the console window and resize it. It worked well on XP, I never tested it on newer Windows versions.
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
SMALL_RECT sr;
sr.Top = 0;
sr.Left = 0;
sr.Bottom = 10;
sr.Right = 79;
SetConsoleWindowInfo(h, TRUE, &sr);
TCHAR title[512];
GetConsoleTitle(title, 512);
HWND hWnd = FindWindow(NULL, title);
if(hWnd != NULL) {
HWND hWndInsertAfter = NULL;
UINT nFlags = SWP_NOSIZE | SWP_NOZORDER;
#if 0 // Don't move?
nFlags |= SWP_NOMOVE;
#endif
SetWindowPos(hWnd, hWndInsertAfter , 40, 350, 0, 0, nFlags);
SetForegroundWindow(hWnd);
}
If you are using the command prompt window, right click it's label on the task bar and click the Properties option.
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?
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.
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).
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.
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.
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?
Right click on the taskbar
choose Properties
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.
I want to create an array of 256 colored buttons with the owner draw extended style to a dialog box created with the visual studio dialog design tool. I added a loop to the WM_INITDIALOG message handler in the dialog procedure to do this:
for (i=0; i<=255; i++)
{
int xp, yp;
HWND status;
xp = rect_pos.left+16*(i%16);
yp = rect_pos.top+16*(i>>4);
status = CreateWindow (
TEXT("button"),
"\0",
WS_CHILD|WS_VISIBLE|BS_OWNERDRAW|BS_PUSHBUTTON,
xp,
yp,
15,
15,
hDlg,
(HMENU) 5000+i, // id used to report events
hInst,
NULL
);
if (status == NULL)
xp =7;
}
I added a message handler for the WM_CTLCOLORBTN message.
case WM_CTLCOLORBTN:
{
int zz;
zz = GetWindowLong ((HWND) lParam, GWL_ID); // window identifier
zz -= 5000;
if ((zz >= 0) && (zz <= 255))
{
HBRUSH BS;
SetTextColor ((HDC) wParam, Collector.Color);
SetBkColor ((HDC) wParam, Collector.Color);
return ((LRESULT) Collector.Brush);
}
break;
}
It more or less works but only the first 64 buttons are displayed. I intend to use a different brush to color each button but for debug puproses, I substituted a single well defined brush. I've debugged the code and satisfied myself the x/y coordinates are proper for each button and that the ID provided in the hMenu createwindow call is proper. I watched all 256 buttons get colored in the WM_CTLCOLORBTN handler. I included a check to make sure the createwindow call does not return failure (NULL). I can get either 4 rows of 16 buttons or 4 columns of 16 buttons by interchanging the x/y parameters on the createwindow call.
If I remove the BS_OWNERDRAW bit from the createwindow call, all 256 buttons are drawn.
It's as if there a limit of 64 buttons with BS_OWNERDRAW :-(
Any help would be greatly appreciated!
TIA, Mike
Are you handling the WM_DRAWITEM message in conjunction with the BS_OWNERDRAW style?
In your case, it seems surprising to me that any buttons are displayed while using the BS_OWNERDRAW style, while BS_PUSHBUTTON is set.
As mentioned in the following link to the documentation for BS_OWNERDRAW, you need to handle WM_DRAWITEM and avoid specifying any other BS_ button styles.
Button Styles from MSDN
Also curious is that the WM_CTLCOLORBUTTON message may be received and then ignored for buttons containing the BS_PUSHBUTTON style. Check out the following link for the documentation on that window message.
WM_CTLCOLORBUTTON from MSDN
From what I can see in your code snippet, most likely you will want to do the following:
Set BS_OWNERDRAW when creating the child buttons.
Handle WM_DRAWITEM on the dialog and draw the button in its correct state. Note that you don't have to handle WM_CTLCOLORBUTTON, just use the Brushes and Fonts and modify the DC as you wish inside your WM_DRAWITEM handler.
Also, depending on your application, you might benefit from making your own window class to represent a grid of buttons on your own, and just drawing the items to taste. This is preferable if you're just displaying internal state and not really looking for the user to manage or interact with a grid of buttons.
Thanks to all who gave advice and help. I now have this working to my satisfaction. I have successfully colored the buttons and surrounded them with a black outline if selected or if they have the input focus. I'll add some code snippets below to show the final state of things but here is synopsis. First, I believe there is some legacy code in my system which makes owner drawn buttons respond to the WM_CTLCOLORBTN for the first child 64 buttons created. Second, I believe the only thing one needs to do is create the buttons, respond properly to the WM_DRAWITEM and WM_COMMAND/BN_CLICKED messages.
Here are the code snippets from my dialog box handler.
In the WM_INITDIALOG code -- create the buttons
for (i=0; i<=255; i++)
{
int xp, yp;
HWND status;
xp = rect_pos.left+16*(i&0x0F);
yp = rect_pos.top+16*(i>>4);
status = CreateWindow
(
TEXT("button"),
"\0",
WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_OWNERDRAW,
xp,
yp,
15,
15,
hDlg,
(HMENU) (5000+i), // id used to report events
hInst,
NULL
);
if (status == NULL)
xp =7;
SetFocus (status);
}
Respond to the WM_DRAWITEM message
case WM_DRAWITEM: // Owner drawn botton
{
LPDRAWITEMSTRUCT lpDrawItem;
HBRUSH BS, BS_Old;
HPEN PN_Old;
int sz=15;
int cntl;
cntl = LOWORD (wParam) - 5000;
lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
if (lpDrawItem->CtlType != ODT_BUTTON)
return FALSE;
BS = CreateSolidBrush (ColorRef[cntl]);
if (lpDrawItem->itemState & (ODS_SELECTED | ODS_FOCUS))
{
sz = 14;
PN_Old = (HPEN) SelectObject(lpDrawItem->hDC, GetStockObject(BLACK_PEN));
}
else
PN_Old = (HPEN) SelectObject(lpDrawItem->hDC, GetStockObject(NULL_PEN));
BS_Old = (HBRUSH) SelectObject(lpDrawItem->hDC, BS);
Rectangle (lpDrawItem->hDC, 0, 0, sz, sz);
SelectObject(lpDrawItem->hDC, PN_Old);
SelectObject(lpDrawItem->hDC, BS_Old);
DeleteObject (BS);
return true;
}
and finally in the WM_COMMAND code
if (HIWORD(wParam) == BN_CLICKED)
{
if ((LOWORD(wParam) >= 5000) && (LOWORD(wParam) <=5255))
{
Color[0] = ColorRef[LOWORD(wParam)-5000] & 0xFF;
Color[1] = (ColorRef[LOWORD(wParam)-5000] >> 16) & 0xFF;
Color[2] = (ColorRef[LOWORD(wParam)-5000] >> 8 ) & 0xFF;
InvalidateRect (hDlg, NULL, TRUE);
goto Set_Color;
}
}