Related
Is there a way to get an handle to Windows's wallpaper (behind icons) in C++ in order to draw on it?
That would allow to make an active desktop (discontinued after Windows XP) equivalent, a Wallpaper Engine equivalent, or any other similar tool. (Temperature and resources usage monitoring on the wallpaper in my case).
Note: the handle returned by GetDesktopWindow() returns the window at desktop icons level, not behind it.
Solutions from similar questions aren't working for me.
Specifically i tried VLC media player's wallpaper mode code.
Key code is:
hwnd = FindWindow( _T("Progman"), NULL );
if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SHELLDLL_DefView"), NULL );
if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SysListView32"), NULL );
if( !hwnd )
{
msg_Warn( p_vout, "couldn't find \"SysListView32\" window, "
"wallpaper mode not supported" );
return;
}
But it will not draw on the wallpaper.
Credits to this draw behind desktop icons C# page as reference. The article explains the theory behind the solution, which applies regardless of the programming language being used.
Long story short, the smooth fading animation you see on Windows 10 when changing wallpaper is achieved by creating a new window that does exactly what you're asking for, drawing under the icons. That window achieves the fade-in effect for the new wallpaper, and is created by the Program Manager.
In the mentioned article you can see together with C# implementation an explanation of every step. Here i'll write a C++ equivalent keeping comments from the source.
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
HWND p = FindWindowEx(hwnd, NULL, L"SHELLDLL_DefView", NULL);
HWND* ret = (HWND*)lParam;
if (p)
{
// Gets the WorkerW Window after the current one.
*ret = FindWindowEx(NULL, hwnd, L"WorkerW", NULL);
}
return true;
}
HWND get_wallpaper_window()
{
// Fetch the Progman window
HWND progman = FindWindow(L"ProgMan", NULL);
// Send 0x052C to Progman. This message directs Progman to spawn a
// WorkerW behind the desktop icons. If it is already there, nothing
// happens.
SendMessageTimeout(progman, 0x052C, 0, 0, SMTO_NORMAL, 1000, nullptr);
// We enumerate all Windows, until we find one, that has the SHELLDLL_DefView
// as a child.
// If we found that window, we take its next sibling and assign it to workerw.
HWND wallpaper_hwnd = nullptr;
EnumWindows(EnumWindowsProc, (LPARAM)&wallpaper_hwnd);
// Return the handle you're looking for.
return wallpaper_hwnd;
}
The C-like casts can be replaced with reinterpret_casts, according to your coding preferences.
One note that isn't mentioned in the article:
Since when changing wallpaper a new WorkerW window is generated to achieve the fading effect, if the user tries to change wallpaper while your program is actively drawing and refreshing your instance of WorkerW, the user set background will be placed on top of your drawing, start fading in until it reaches 100% opacity, and lastly be destroyed, leaving your WorkerW still running.
My program - among other things - changes the console window appearance (mainly the window size and border).
Now on my computer, everything is working perfectly at the moment, but when I run the application in VirtualBox, or on a different computer, I get the following:
The window on the top-left corner of the image is not actually a window. It's an image of a window, that you can't click. (mouse clicks go through it)
You can get rid of it by stretching the selection rectangle on the desktop over it, or if you highlight (for example) a button that is under it. Also, you can move a window over it, which makes it disappear completely.
The black rectangle on the bottom-right corner of the image is my console window, which is displayed correctly.
My question is, how to get rid of the 'ghost' window with C++?
I tried Googling a bit, but all I could find was ChangeDisplaySettings(0, 0);, which on my computer doesn't do anything (probably because I don't even have this problem on my computer), and in VirtualBox, it first appears to momentarily make the console window fullscreen and then back to the way it was. (the screen flickers the first time you run the application)
Although it does remove the ghost window, I don't want the screen to flicker like that, so this is not what I'm looking for.
EDIT:
As I can't really figure out what would be relevant code for this problem, I'll just dump pretty much all the code that has anything to do with changing the window itself in my program.
CSBIEx.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX);
GetConsoleScreenBufferInfoEx(hCon, &CSBIEx);
CSBIEx.dwSize.X = 49;
CSBIEx.dwSize.Y = 21;
SetConsoleScreenBufferInfoEx(hCon, &CSBIEx);
srWnd.Bottom = 20;
srWnd.Left = 0;
srWnd.Right = 48;
srWnd.Top = 0;
SetConsoleWindowInfo(hCon, TRUE, &srWnd);
GetClientRect(hWnd, &rClnt);
rClnt.top += 1;
rClnt.bottom -= 2;
rClnt.right -= 1;
SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP);
exStyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE);
exStyle &= ~WS_EX_CLIENTEDGE;
SetWindowLongPtr(hWnd, GWL_EXSTYLE, exStyle);
BringWindowToTop(hWnd);
SetWindowPos(hWnd, HWND_TOPMOST, ((rScr.right / 2) - rClnt.right / 2) - 1, (rScr.bottom / 2) - rClnt.bottom / 2, 0, 0, SWP_FRAMECHANGED | SWP_DRAWFRAME | SWP_NOSIZE);
SetWindowRgn(hWnd, CreateRectRgnIndirect(&rClnt), 1);
ShowWindow(hWnd, SW_SHOWNORMAL);
//ChangeDisplaySettings(0, 0);
2ND EDIT:
I don't know if it's of any help, but I noticed that if I use ChangeDisplaySettings(NULL, 0); instead of ChangeDisplaySettings(0, 0); it doesn't do anything. That's pretty weird considering that NULL is #defined 0..
If no one can figure anything out, I'll probably just end up using ChangeDisplaySettings(0, 0);.
InvalidateRect(NULL, NULL, TRUE);
Was the thing that I was looking for.
Are you running an "Aero" theme on your computer? If so, switch to the classic theme. Betcha you will see the problem manifest itself. I think your app is not handling the WM_PAINT message properly. The Aero themes send far fewer WM_PAINT messages. The OS does the painting with bitmaps that it saves.
EDIT: Try calling these with the new dimensions:
BOOL WINAPI SetConsoleDisplayMode(
_In_ HANDLE hConsoleOutput,
_In_ DWORD dwFlags,
_Out_opt_ PCOORD lpNewScreenBufferDimensions
);
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686033%28v=vs.85%29.aspx
BOOL WINAPI SetConsoleWindowInfo(
_In_ HANDLE hConsoleOutput,
_In_ BOOL bAbsolute,
_In_ const SMALL_RECT *lpConsoleWindow
);
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686125%28v=vs.85%29.aspx
You can also try sending yourself a WM_PAINT message with DispatchMessage.
I have problems with creating a simple Group-Box-Control via CreateWindowEx. The font-size/-style of its caption just doesn’t look right.
I have created a simple Windows Dialog (containing group-boxes, buttons…) with the Visual Studio - Resource Manager. When I load that dialog with DialogBox(…) everything looks normal but when I create another group-box-control on that same dialog via CreateWindowEx(…) the caption of the new control has a different font-size/-style.
With Microsoft Spy++ I was able to see the dwExStyle and dwStyle values of the other groub-boxes, but even when I use the same values in CreateWindowEx I still get a different look.
Here is the code I use to create the new group-box:
HWND hGroup1 = GetDlgItem(_hWnd, IDC_GROUPBOX1);
HWND hGroup2 = CreateWindowEx(
WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_NOPARENTNOTIFY,
L"Button",
L"Hallo",
WS_CHILDWINDOW | WS_VISIBLE | BS_GROUPBOX,
20, 20, 250, 250,
hGroup1,
nullptr,
_hInstance,
nullptr);
Here is a screen capture of the dialog:
http://imageshack.us/photo/my-images/856/groupboxfontissue.png/
Please let me know where I went wrong and what I can do to fix it.
[EDIT-1]
In regards to Jonathan Potter and Superman, as you suggested I set the font-handle of the new group-box to the same as for the other controls.
HFONT hFont1 = (HFONT)SendMessage(hGroup1, WM_GETFONT, 0, 0);
HFONT hFont2 = (HFONT)SendMessage(hGroup2, WM_GETFONT, 0, 0);
HFONT hFont3 = (HFONT)SendMessage(_hWnd, WM_GETFONT, 0, 0);
SendMessage(hGroup2, WM_SETFONT, (WPARAM)hFont1, TRUE);
hFont2 = (HFONT)SendMessage(hGroup2, WM_GETFONT, 0, 0);
At the end of this code, I can see that all controls and the dialog window have the same font-handle but only the controls which were created with the Resource Manager have the correct font (which is the system font).
Is there anything else I can do???
[EDIT-2]
I cannot believe it… it works now! Thank you all very much for your help!
I just had to set the hWndParent value in CreateWindowEx(…) to the dialog handle and then use WM_GETFONT and WM_SETFONT to copy the right font.
I wish you all a nice weekend.
Controls you create manually (via CreateWindowEx) do not get their font set automatically, and will default to the "system font" (which is what you see in your screenshot). Instead, you need to set the control's font once it has been created. For example,
SendMessage(hGroup2, WM_SETFONT, (WPARAM)SendMessage(hGroup1, WM_GETFONT, 0, 0), TRUE);
When you place a control in a dialog using the resource editor, the font set to the dialog, which is the parent of the control will be used for it by default.
If you're creating a control dynamically, the system font will be used instead of the font of the dialog.
To get the same font of the dialog for a control that you create dynamically, set the font of the dialog to the control in the WM_INITDIALOG handler.
In the code snippet below, replace m_hWnd with the handle of the parent dialog.
HFONT font = (HFONT)::SendMessage(m_hWnd, WM_GETFONT, 0, 0);
::SendMessage(hGroup2, WM_SETFONT, (WPARAM)font, TRUE);
Im using mfc to draw a custom menu except it has a nasty looking border around it. How do i get rid of the border or draw over it?
For example:
(the white border around the edge)
Edit:
i know its only three hours left but none of the things below work. I have tried them using the following code:
HWND hwnd = m_pParent->getBrowserHWND();
uint32 style = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLong(hwnd, GWL_STYLE, style&~WS_BORDER);
SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED);
HookHwnd hook(hwnd);
int res = TrackPopupMenu((HMENU)menu.GetHMenu(), TPM_LEFTALIGN|TPM_RIGHTBUTTON|TPM_RETURNCMD|TPM_RECURSE, xPos, yPos, 0, hwnd, NULL);
SetWindowLong(hwnd, GWL_STYLE, style);
Actually further to freefallr's advice it may well just be a simple WS_BORDER.
Try removing it using:
ModifyStyle( WS_BORDER, 0, SWP_FRAMECHANGED );
I only use WTL for UI coding, it's been years since I've looked at MFC, but it's also very close to the Windows API. You might check the creation flags for the menu.
Call GetWindowLong and specifically, check GWL_EXSTYLE for WS_EX_CLIENTEDGE; this may be the cause of your problem. You can always OR it out and call SetWindowLong and redraw the menu to test.
Hope this is of some help!
Update:
I wonder if the frame isn't being updated. Try:
ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED);
I have a Windows program which has two 2 windows in it:
hwnd (main interface)
hwnd2 (toplevel window, no parent, created by hwnd)
When I double click on hwnd, I need hwnd2 to pop up and show some data, so I use this function to bring hwnd2 to top:
BringWindowToTop(hwnd2);
hwnd2 is brought to top, but there is one thing odd. When I click on hwnd2 again, hwnd (main interface) pops itself up again automatically.
I tried to use the following function to solve this problem, but non of them works.
SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
//doesn't work
BringWindowToTop(hwnd2); //This is the function brings hwnd2 to top
SetForegroundWindow(hwnd2); //doesn't work
SetWindowPos(hwnd2, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
//doesn't work
SetWindowPos(hwnd2, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
// hwnd2 "always" on top, not what I want
SetActiveWindow(hwnd2); // doesn't work too (for replying to Magnus Skog, thanks)
SwitchToThisWindow(hwnd2, TRUE);// got the same problem with BringWindowToTop function
SwitchToThisWindow(hwnd2, FALSE);
How could I solve this problem?
Thanks in advance.
(for replying to aJ, hwnd2 doesn't have parent because it needs to be a toplevel window so it can be in front/back of other windows)
(hwnd2 is a media player which is composed of several windows, one of the windows is for video dispaly, two other trackbar controls for progress bar and volume bar, one Toolbar control for control panel.)
(There is one this might help, no matter which window I click on hwnd2, hwnd pops up automatically as loong as "the mouse is on top of hwnd in Z-order", including menu bar and non-client area, etc.)
(This media player is writen in Direct Show. I use IVideoWindow::put_Owner to put video window as the video owner, Direct Show internally creates a sub-video window as a child of the video window. Except for this sub-video window which I can't see the source code, I don't see any thing suspicious in hwnd2.)
I found the reason, which is because of Direct Show. I use multithread to execute it, and then the problem's solved. But...why??
This problem can be resolved by using PostMessage (rather than SendMessage).
try this,it is said coming from M$
HWND hCurWnd = ::GetForegroundWindow();
DWORD dwMyID = ::GetCurrentThreadId();
DWORD dwCurID = ::GetWindowThreadProcessId(hCurWnd, NULL);
::AttachThreadInput(dwCurID, dwMyID, TRUE);
::SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
::SetWindowPos(m_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);
::SetForegroundWindow(m_hWnd);
::SetFocus(m_hWnd);
::SetActiveWindow(m_hWnd);
::AttachThreadInput(dwCurID, dwMyID, FALSE);
In order to bring a window to top, you should get your window handle,thread handle, the windows thread handle who is in foreground
then we attach our thread to foreground window thread and get input by AttachThreadInput, then we set our window z order
to topmost and then restore its z order to normal, call SetForegroundWindow,SetFocus,SetActiveWindow to make sure our window is brought to top and is active and have focus
then deattach the input queue from the old foreground window thread, make our thread the only one who capture the input events
So why should We call AttachThreadInput, it is because
SetFocus sets the keyboard focus to the specified window. The window must be
attached to the calling thread's message queue.
What does AttachThreadInput do?
The AttachThreadInput function can be used to allow a set of threads
to share the same input state. By sharing input state, the threads
share their concept of the active window. By doing this, one thread
can always activate another thread's window. This function is also
useful for sharing focus state, mouse capture state, keyboard state,
and window Z-order state among windows created by different threads
whose input state is shared.
We use SetWindowPos to bring the windows to topmost and show the window if the window is hidding by using SWP_HIDEWINDOW
SetWindowPos function changes the size, position, and Z order of a
child, pop-up, or top-level window. These windows are ordered
according to their appearance on the screen. The topmost window
receives the highest rank and is the first window in the Z order
If your problem is your window is also minimized , you should add one line code to the end
ShowWindow(m_hWnd, SW_RESTORE);
Both work great:
::SetForegroundWindow(wnd)
or
::SetWindowPos(m_hWnd, // handle to window
HWND_TOPMOST, // placement-order handle
0, // horizontal position
0, // vertical position
0, // width
0, // height
SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE// window-positioning options
);
But remember that the last one sets the window always on top.
After many tries and errors.I found following solution to this problem:
SendMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); // restore the minimize window
SetForegroundWindow(hwnd);
SetActiveWindow(hwnd);
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
//redraw to prevent the window blank.
RedrawWindow(hwnd, NULL, 0, RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN );
The hwnd is your windows HWND . Please do not just copy and paste. You also need use GetLastError to check api error after every api call.
I have confirm following result on my win7:
Can restore minimize window and no error return.
If the window already top, the window title will blink and no error return.
If the window has closed, it will return the error "0x578 Invalid window handle."
It can bring the window to the top on all not top-most window and no error return.(For example it will behind the top-most taskmanager)
It do not make the window top-most. The user can make other window on top of it.
SwitchToThisWindow works best for me.
Have you tried SetActiveWindow()?
This will restore an app if minimized and bring it to the front:
ShowWindow(hWnd, SW_SHOW);
SetForegroundWindow(hWnd);
//work great!
Var
WndHandle:HWND;
begin
WndHandle :=FindWindowEx(0,0,nil,'Calculator');
PostMessage(WndHandle,WM_SHOWWINDOW,SW_RESTORE,0);
SetForegroundWindow(WndHandle);
end;