I wrote a program who paints widget on desktop wallpaper, under desktop icons, and all was good.
But I reveal a strange bug - my program was not works on some other computers - they displays
the same desktop without widget after start.
So, have anybody any expirience, some knowledge in this area? Please, help me! :)
(I can't find any computers near, where widget not works, in my computer all works well.
I wrote a lot of test programs, whose tries draw anything on desktop with rather methods,
but test displays nothing too in my friends computers.)
Drawing method:
I wrote a dll, who finds desktop window (progman->child->child ))
and creates a hook and install own window procedure for desktop.
In window procedure i hook WM_ERASEBKGND:
case WM_ERASEBKGND:
HDC hdc = (HDC)wparam;
PaintDesktop(hdc);
WidgetPaint(hdc);
return 1;
// I skip in this code getting and setting clipping areas for fast drawing.
(I tried all combinations with clipping and without clipping)
And in tests I also trying to paint widget in WM_PAINT message with same results -- all already works at me, but works not at all my friends.
upd: I solved it! :)
I solved it!
It's simple was a 64-bit machines)
So I compile my code for 64-bit and widget start work pretty good!
And, also -
PaintDesktop(hdc);
will produce very bright wallpaper in Windows7 (and Vista) with Aero.
(User's wallpaper comes brighten).
Must be
CallWindowProc(OldWndProc, WM_ERASEBKGND, wparam, lparam);
Cause of this - Aero treats PaintDesktop() as usual paint (over virtual screen wallpaper, that is the same as on desktop) with color summation.
Regards! :)
Related
I want to create a window with the following properties:
Thick border ("double border" as described in MSDN)
Not resizable
Icon in the title
Close button in the title
It sounds so simple and we can find this kind of windows everywhere in Windows. But as soon as I disable ability to resize my window I also lose the thick border. Here is the illustration of my problem:
The height of my test window's caption bar is slightly less than that of a Shell property dialog, but I would like it to be the same height.
I have tried to use WinSpy and copy all styles from Explorer's window to mine, but this did not help.
I don't know what's going on under the hood, but I found two solutions:
Switch to the newer Platform Toolset (which means drop support of Windows XP)
Disable resizing by writing custom WM_NCHITTEST handler
The second option is quite simple and I do not want drop Windows XP support for a silly reason. Job is done:
LRESULT CListEditorDlg::OnNcHitTest(CPoint point)
{
LRESULT retVal = CDialog::OnNcHitTest(point);
switch (retVal)
{
case HTBOTTOM:
case HTBOTTOMLEFT:
case HTBOTTOMRIGHT:
case HTLEFT:
case HTRIGHT:
case HTTOP:
case HTTOPLEFT:
case HTTOPRIGHT:
retVal = HTBORDER;
}
return retVal;
}
I've tried to draw any thing on Desktop window using GetDC(GetDesktopWindow), like the following simple program:
#include <windows.h>
int main()
{
TextOut(GetDC(GetDesktopWindow()), 10, 10, TEXT("Test TextOut Tester!!"), 21);
return 0;
}
It seems that my current user privileges affect the drawing behavior, I am not admin on my PC, is this the reason for that? is there any documentation for this issue ?
Thanks in advance
The simple reason you can't draw on the desktop window like this is you cannot actually see the desktop window. Since Windows 95 the desktop window has been completely obscured by a cluster of windows owned by explorer.
The DC you get when you call GetDC(GetDesktopWindow()) will thus be completely clipped.
If you want to draw directly on the display GetDC(NULL) will give you a DC that you can use to draw all over the desktop and visible windows. But that, as has been mentioned, will be operating entirely outside Windows' repainting logic and the results will be, well, ugly and unsuited to any real purpose (other than, say, getting some kind of debug feedback from a windowless app you are in the process of developing).
Most applications that want do "display something on the desktop" do so by creating a window and drawing on that. Why is that not appropriate here?
This is what you should do:
HDC hdc = ::GetDC(NULL);
//draw on the desktop using the hdc
::ReleaseDC(NULL, hdc);
I have developed an application to display jpeg images. It can display 4 images, one in each quadrant of the screen. It uses 4 windows for that. The windows have no border (frame) nor titlebar.
When a new image is loaded, the window size is adjusted for the new image and then the image is displayed.
Especially when the window is made larger, there is often a flicker. With my eyes to slits, it seems that the old contents is moved when resizing before the new contents is displayed.
I consulted many resources and used all tricks:
the window has only style CS_DBLCLKS (no CS_HREDRAW or CS_VREDRAW);
the background brush is NULL;
WM_ERASEBKGND returns 1;
WM_NCPAINT returns 0;
WM_NCCALCSIZE tells to align to the side not moved (can you tell it to discard the client area?);
WM_WINDOWPOSCHANGING returns 0;
SetWindowPos has flags SWP_NOCOPYBITS | SWP_DEFERERASE | SWP_NOREDRAW | SWP_NOSENDCHANGING.
Still, the flicker (or the contents move) occurrs when resizing the window. What I want is to:
SetWindowPos to new size and position;
InvalidateRect (hWnd, NULL, FALSE);
UpdateWindow(hWnd);
without any painting, background erasing or content moving until WM_PAINT.
WM_PAINT does:
hDC= BeginPaint (hWnd, &ps);
hMemDC= CreateCompatibleDC (hDC);
hOldBitmap = SelectObject (hMemDC, hNewBitmap);
BitBlt (hDC,...,hMemDC,0,0,SRCCOPY);
SelectObject (hMemDC, hOldBitmap);
DeleteDC (hMemDC);
EndPaint (hWnd, &ps);
Can anyone tell me if/where I make a mistake that causes the old content of the window to be moved?
Hardware etc: Windows 7 on HP Elitebook Core7 64 bits with NVIDIA Quadro K1000m driver 9.18.13.3265 (updated to 341.44).
UPDATE (Jul '17)
I have seen the behavior of the pogram also on another Windows computer (Windows 8/10). It does not seem to be the NVIDIA display driver.
The behavior is the most visible when resizing a window tiled to the centre of the screen (right bottom = w/2, h/2) to the left or left upper corner (0, 0).
I may have problems with the calculations for the WM_NCCALCSIZE message to tell Windows not to do anything. Could anyone give an example calculation for my purpose? See also How do I force windows NOT to redraw anything in my dialog when the user is resizing my dialog?
You have an impressive list of anti-flickering tricks :-)
I don't know if this is of importance (since it depends on how your tool windows are created and espacially if they are child windows to a common parent):
Try setting window style WS_CLIPCHILDREN in the parent window of the tool windows (if there is one).
If not set the parent window will erase it's (entire) background and then forward the paint messages to the child windows which will cause flickering. If WS_CLIPCHILDREN is set the parent window does nothing to the client area occupied by child windows. As a result the area of child windows isn't drawn twice and there is no chance for flickering.
This is a theory more than an answer:
By default, in modern Windows, your window is just a texture on the video card, and the desktop window manager is mapping that to a rectangle on the screen. You've seem to have done everything necessary to make sure that texture gets updated in one fell swoop.
But when you resize the window, perhaps the desktop compositor immediately updates its geometry, causing the (still unchanged) texture to be appear in the new position on the screen. It's only later, when you do the paint, that the texture is updated.
You can test this theory by temporarily turning off desktop compositing. On Windows 7, you navigate to System Properties, choose the Advanced tab, under Performance choose Settings..., on the Visual Effects tab, deselect the "Enable desktop composition" setting. Then try to reproduce the problem. If it goes away, then that supports (but doesn't absolutely prove) my theory.
(Remember to re-enable compositing, since that's how most of your users will be running most of the time.)
If the theory is true, it seems the goal is to get to the paint as soon possible after the window resize. If the window of time is small enough, then both could happen within a monitor refresh cycle and there would be no flicker.
Ironically, your efforts to eliminate flicker may be working against you here, since you've intentionally suppressed the invalidation and redraw that would normally result from SetWindowPos until you do it manually at a later step.
A debugging tip: Try introducing delays (e.g., Sleep(1000);) at key points in the process so you can see whether the resize and redraw are actually rendering on screen as two distinct steps.
You have indeed got a nice set of tricks.
First I can suggest a few variants on the existing tricks that might help especially on XP/Vista/7, and second I want to mention where the persistent flicker you see on Win8/10 is likely coming from and some tricks to reduce that.
First, despite advice to the contrary in other OP posts, you may find that if you set the CS_HREDRAW|CS_VREDRAW window styles that you avoided before, it actually eliminates the BitBlt that is done inside the internal SetWindowPos Windows does during window resizing (but---and this is the confusing part---on Windows 8/10 you will still see flicker from another source...more on that below).
If you don't want to include CS_HREDRAW|CS_VREDRAW, you can also intercept WM_WINDOWPOSCHANGING (first passing it onto DefWindowProc) and set WINDOWPOS.flags |= SWP_NOCOPYBITS, which disables the BitBlt inside the internal call to SetWindowPos() that Windows makes during window resizing.
Or, you could add to your WM_NCCALCSIZE trick by having it return a set of values that will tell Windows to just BitBlt 1 pixel on top of itself so that even if the BitBlt does happen, it doesn't do anything visibly:
case WM_NCCALCSIZE:
{
RECT ocr; // old client rect
if (wParam)
{
NCCALCSIZE_PARAMS *np = (NCCALCSIZE_PARAMS *)lParam;
// np->rgrc[0] is new window rect
// np->rgrc[1] is old window rect
// np->rgrc[2] is old client rect
ocr = np->rgrc[2];
}
else
{
RECT *r = (RECT *)lParam;
// *r is window rect
}
// first give Windoze a crack at it
lRet = DefWindowProc(hWnd, uMsg, wParam, lParam);
if (wParam)
{
NCCALCSIZE_PARAMS *np = (NCCALCSIZE_PARAMS *)lParam;
// np->rgrc[0] is new client rect computed
// np->rgrc[1] is going to be dst blit rect
// np->rgrc[2] is going to be src blit rect
//
ncr = np->rgrc[0];
RECT &dst = np->rgrc[1];
RECT &src = np->rgrc[2];
// FYI DefWindowProc gives us new client rect that
// - in y
// - shares bottom edge if user dragging top border
// - shares top edge if user dragging bottom border
// - in x
// - shares left edge if user dragging right border
// - shares right edge if user dragging left border
//
src = ocr;
dst = ncr;
// - so src and dst may have different size
// - ms dox are totally unclear about what this means
// https://learn.microsoft.com/en-us/windows/desktop/
// winmsg/wm-nccalcsize
// https://learn.microsoft.com/en-us/windows/desktop/
// api/winuser/ns-winuser-tagnccalcsize_params
// - they just say "src is clipped by dst"
// - they don't say how src and dst align for blt
// - resolve ambiguity
// essentially disable BitBlt by making src/dst same
// make it 1 px to avoid waste in case Windoze still does it
dst.right = dst.left + 1;
dst.bottom = dst.top + 1;
src.right = dst.left + 1;
src.bottom = dst.top + 1;
lRet = WVR_VALIDRECTS;
}
else // wParam == 0: Windoze wants us to map a single rect w->c
{
RECT *r = (RECT *)lParam;
// *r is client rect
}
return lRet;
So that's all very nice, but why does Windows 8/10 look so bad?
Apps under Windows 8/10 Aero don't draw directly to the screen, but rather draw to offscreen buffers that are then composited by the evil DWM.exe window manager. DWM actually adds another layer of BitBlt-type behavior on top of the existing legacy XP/Vista/7 BitBlt behavior.
And the DWM blit behavior is even more crazy because they don't just copy the client area, but they actually replicate pixels at the edges of your old client area to make the new one. Unfortunately, making DWM not do its blit is much harder than just passing some extra flags.
I don't have a 100% solution, but I hope the above info will help, and please see this Q&A for a timing trick you can use to reduce the chances of the DWM-layer blit happening to your app:
How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?
Just in addition to your list of tricks. I have the same problem with flicker on my Dell XPS notebook while resizing window using left/top edge. I've tried all the tricks you mentioned. As far as I understand window border is drawn in GPU and the window content is prepared in GDI subsystem and transferred to video memory for window composition (DWM introduced in Windows 8.1). I tried to remove GDI rendering completely (setting WS_EX_NOREDIRECTIONBITMAP style) which makes window without any content, and then create content surface directly using DXGI subsystem (using CreateSwapChainForComposition, there are few examples on how to do this). But the problem remains. There is still a lag between rendering a window frame and resizing/displaying content surface.
However it may solve your problem, as you don't have the border and you will not notice this lag. But you will have full control over window repaint and it will be made on GPU side.
Some years ago we developed an ActiveX component called CaptionX (CaptionX Home Page). It allows the developers to place custom clickable graphical buttons into the window title bar.
All works fine in any Windows except Vista and Windows 7 when Aero Glass is turned on - the title bar with our custom icons is drawn as if we used the Windows Basic theme without the transparency effect for the window borders.
People would like to use CaptionX in the latest versions of the OS, but we cannot make it working.
All searches in the Internet tell us we need to enable Aero Glass when we use custom drawing in the window title using the DwmSetWindowAttribute API call with the DWMWA_NCRENDERING_POLICY attribute, but we have not managed to make it working.
Our code that draws on the window's non-client surface looks like this (sorry - it's the old VB6 :):
Friend Function WindowProc(ByVal lPrevWndProc As Long, ByVal hwnd As Long, ByVal iMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Select Case iMsg
Case WM_NCPAINT
DoNCPaint lPrevWndProc, wParam
WindowProc = 0
Exit Function
Case WM_...
' Other message handlers
End Select
WindowProc = CallWindowProc(lPrevWndProc, hwnd, iMsg, wParam, lParam)
End Function
We added the following call
DwmSetWindowAttribute m_hwnd, DWMWA_NCRENDERING_POLICY, DWMNCRP_ENABLED, 4
to many places in our code, but it does not have any effect. We can disable the Aero Glass effect if it is used by default in a window using DwmSetWindowAttribute, but cannot enable it.
What are we doing wrong? Do we need to add more API calls to our code, and if so where?
There may be a mistake on your calling, you should not pass a plain DWMNCRP_ENABLED value into the API, instead, you should pass a ref of a DWMNCRENDERINGPOLICY structure.
This old approach does not work in latest versions of Windows starting from Vista because of the new Desktop Window Manager (DWM) responsible for drawing the window frame. Here is also one interesting blog found in the web.archive.org that explains the nature of the problem:
Frequently asked questions about the Aero Basic window frame
The pertinent excerpt is the following:
The DWM doesn't have any legacy worries because applications cannot
draw inside the glass frame, since it's rendered and managed by a
totally different process. If an application tries to do it, Windows
will detect it and remove the glass frame entirely (and therefore
revert to the Basic frame), so that the application can draw what it
wants to draw.
To modify the window title bar in Windows Vista, 7 and so on, we need to use the new DWM API.
My purpose is to size a window to a width/height greater than the size of my physical screen programmatically under Win32. How can I do this?
On my systems it seems the maximum size of a given window is bound by the size of my screen whether programmatically or whether sizing manually by dragging the sizing cursor.
I have tried programmatically with SetWindowPos() and MoveWindow() and both cap the size of the target window. Oddly I know some people do not have this 'cap' so I wonder whether this is perhaps due to some OS setting (registry). Does anyone know something about this? Or perhaps some way to workaround it?
// Edit: new developments
I am testing on Windows XP and Windows 7. The graphics cards I'm using are a NVIDIA Quadro NVS 290 (256MB) and a Geforce 9800GT (1GB). After further investigation it looks like Windows is intercepting the message and fiddling with the parameters. For example, if you call SetWindowPos to make a target 2000x2000 it will only receive a WM_SIZE for the capped x/y.
Implement a message handler for WM_GETMINMAXINFO to stop Windows from applying the sane default behavior:
case WM_GETMINMAXINFO: {
DefWindowProc(hWnd, message, wParam, lParam);
MINMAXINFO* pmmi = (MINMAXINFO*)lParam;
pmmi->ptMaxTrackSize.x = 2000;
pmmi->ptMaxTrackSize.y = 2000;
return 0;
}
Windows with a thick frame (to allow user resize) are restricted from growing larger than the desktop.
Try SetWindowLong() clearing the THICKFRAME (0x40000) flag.
The following should allow programatic sizing, but the user will lose the ability to resize. If you add the Thickframe back after sizing, the user can resize, but when he does so the window will immediately shrink back to the desktop limited size.
The following is from some csharp code that also removes all borders, caption, etc:
WS style = (WS)GetWindowLong(ptr, GWL_STYLE);
style = style & ~WS.BORDER & ~WS.ThickFrame & ~WS.SYSMENU & ~WS.CAPTION | WS.POPUP;
SetWindowLong(ptr, GWL_STYLE, (int)style);
A good tool to play with window settings is uuSpy.
It's like Microsoft Spy++, but allows you to modify settings like THICKFRAME.
Yes, windows can be larger than the screen (or even the sum of all your monitors). Windows can also be positioned off-screen (which some applications do as a hack to hide while remaining active).
Perhaps the Windows 7 desktop manager is kicking in and trying to "dock" those windows to the edges of your screen for you.
You might try using the slightly lower-level API SetWindowPos, which gives you control over notifications, z-order, and other stuff.
You can get a window to be larger in resolution (and even way way larger) than your screen, using the 'Infinte Screen" software:
http://ynea.futureware.at/cgi-bin/infinite_screen.pl
Here's how to use it:
Download it, run it.
In the Oversize tab, choose the Windows you want to enlarge.
Give it the Width and Height you want. Done!
Just in case you need a large screenshot (that's how I ended up here):
If you want to get a screenshot of the window, you've got a screenshot option in the same Oversize tab. (Because screenshots are normally no bigger than the screen size, even if the window is larger). Another (and better) way to screenshot the window is using Greenshot, as you can save them in .tiff and directly watching the window.