Redraw Desktop WinAPI

Pages: 12
hi all.
i'm trying to write a program that draws a ball on my desktop without affecting anything else, means: even though the ball is moving all around, you will still be able to ignore it and keep doing whatever you're doing.
everything works just fine except for one little problem.

the desktop isn't refreshing itself before the ball is drawn again, this makes the desktop crowded with balls.
i tried WindowRedraw() but with no luck, also UpdateWindow() but neither worked.
here's a piece of my code:

1
2
3
4
5
6
7
8
9
10
11
12
13
// g is a namespace that contains all the global variables.
// g::hdcFrontBuffer is an HDC connected to the desktop window through a call to GetDC(0).
void ProcessBackBuffer()
{
       // the problem is here, i want the desktop to refresh at this point.
	RedrawWindow(GetDesktopWindow() , &g::rcDesktop , 0 , RDW_INTERNALPAINT | RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_INVALIDATE);
       /// unfortunately, it's not.
	
	BitBlt(g::hdcBackBuffer , 0 , 0 , g::bm.bmWidth , g::bm.bmHeight , g::hdcFrontBuffer , g::ball.Pos.X , g::ball.Pos.Y , SRCCOPY);
       // hdcMask is connected to a mask image to provide transparency effect.
	BitBlt(g::hdcBackBuffer , 0 , 0 , g::bm.bmWidth, g::bm.bmHeight , g::hdcMask , 0 , 0 , SRCAND);
	BitBlt(g::hdcBackBuffer , 0 , 0 , g::bm.bmWidth , g::bm.bmHeight , g::hdcImage , 0 , 0 ,SRCPAINT);
}


i have other methods that updates the status, initialize the GDI resources and to release them when the program terminates.
g::ball is an instant of a class that contains all information about the floating object:
its image
the mask of its image
its position
its Delta values (dx , dy)

i searched everywhere i know for this problem, but found no convenient answers.
thank you for even reading this.
I believe you need to call InvalidateRect(NULL) before UpdateWindow() to redraw the entire window, but I think this is going to cause tons of flickering, since you don't control the drawing of other windows, the taskbar, etc.
Drawing on the desktop is something that became a lot more difficult then the Desktop Window Manager was introduced in Windows Vista. Up until Windows XP, RedrawWindow could be used, but not on the dektop window itself; instead, you found the list view control which is a child of the desktop and draw on that, as shown in Chris' source code here (whoever Chris is?):

Drawing on the desktop
http://www.angelcode.com/dev/desktopdraw/

I tried this code on my old Windows XP machine and it worked fine. But on Windows 7 it's flickery at best, and sometimes the rotating star just goes missing. It's also a bit of a hack as it makes assuptions about how Explorer handles the desktop.

So I hacked the code to work with a lower most, layered window instead. It's almost the same, except the drawing is now just above the icons rather than below them.

See code below, in the following post due to its length. It uses the same resources, etc. as provided by Chris (so download the rest of the code from the link above.)

A more sophisticated solution would probably require working with the Desktop Window Manager, which is something I've not yet looked into.

Andy

Desktop Window Manager
http://en.wikipedia.org/wiki/Desktop_Window_Manager

How do I get the window handle of the desktop?
http://stackoverflow.com/questions/1669111/how-do-i-get-the-window-handle-of-the-desktop
Last edited on
Modified version of code from:

Drawing on the desktop
http://www.angelcode.com/dev/desktopdraw/

Instead of drawing on the List View child of the desktop window, the app now draws on a bottom most, layered window which is keyed for trsnsparency.

Andy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
// Copyright (c) 2002 Andreas Jönsson, www.AngelCode.com

// Don't forget to link with winhook.lib


/************************************************
Modified by Chris Grams to eliminate the need for
windows message hooks

instead of installing message hooks, what I have done here is
used the RedrawWindow function to update the parts of the desktop
the need updating.

this erases everything inside the rect rc and the RDW_UPDATENOW flag
tells RedrawWindow that the window should be redrawn immediately
RedrawWindow( hDesktopWnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );

this just draws everything thats over top of the desktop background.
RDW_NOERASE tells RedrawWindow not to have the desktop erase the background
which essentially does everything that the windows hooks did, only without
the windows hooks.
RedrawWindow( hDesktopWnd, &rc, NULL, RDW_NOERASE | RDW_INVALIDATE | RDW_UPDATENOW );
*************************************************/


#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>
#include <math.h>
#include "resource.h"

// Global Variables:
HWND hWnd        = NULL;
HWND hDlg        = NULL;
TCHAR szWindowClass[] = _T("Test Class");
COLORREF crefKey = RGB(0, 255, 0);
HBRUSH hbrBkgnd  = NULL;
HINSTANCE hInst  = NULL;

int     posX        = 0;
int     posY        = 0;
float   angle       = 0;

// Foward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow);
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);

void DrawWindow(HWND hWnd);

// Main function
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    hbrBkgnd = CreateSolidBrush(crefKey);

    MyRegisterClass(hInstance);

    // Create our window:
    if (!InitInstance(hInstance, nCmdShow))
        return FALSE;

    // Main message loop:
    MSG msg;
    while( GetMessage(&msg, NULL, 0, 0) ) 
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    DeleteObject(hbrBkgnd);

    return msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex = {0};
    wcex.cbSize         = sizeof(WNDCLASSEX);
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = hbrBkgnd;
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));

    return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    hInst = hInstance;

    RECT rect = {0};
    SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);

    hWnd = CreateWindowEx( WS_EX_LAYERED,
                           szWindowClass,
                           NULL,
                           WS_POPUP,
                           rect.left,
                           rect.top,
                           rect.right  - rect.left,
                           rect.bottom - rect.top,
                           NULL,
                           NULL,
                           hInstance,
                           NULL );

    if(!hWnd)
    {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
    SetLayeredWindowAttributes(hWnd, crefKey, 255, LWA_COLORKEY);
    UpdateWindow(hWnd);

    return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) 
    {
    case WM_USER:
        // Close the main window when the dialog tells us to
        DestroyWindow(hWnd);
        return TRUE;

    case WM_CREATE:
        // Create a dialog so we have a way of closing the app
        hDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_DIALOG), 0, (DLGPROC)DialogProc);
        ShowWindow(hDlg, SW_SHOW);

        // Create a timer so that we can animate our drawing
        SetTimer(hWnd, 1, 50, 0);
        break;

    case WM_DESTROY:
        // Kill our timer
        KillTimer(hWnd, 1);

        // Post the quit message so that our main loop will know that we have quit
        PostQuitMessage(0);
        break;

    case WM_TIMER:
        {
            angle += 0.1f;
            if( angle >= 2*3.141592f )
                angle -= 2*3.141592f;

            // Get the current mouse position
            POINT pt = {0, 0};
            GetCursorPos(&pt);
            ScreenToClient(hWnd, &pt);

            // Update position
            RECT rc = {posX-50, posY-50, posX+50, posY+50};

            // redraw the desktop window right away
            RedrawWindow( hWnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );

            posX = pt.x;
            posY = pt.y;

            // are we re-drawing the window at the new cursor position just to make sure that
            // the part of the window under the animation is validated?!
            RECT rc2 = {posX-50, posY-50, posX+50, posY+50};
            RedrawWindow( hWnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );

            DrawWindow(hWnd);
        }
        break;

        default:
            return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }

    return 0;
}

INT_PTR CALLBACK DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_COMMAND:
        // Close the dialog no matter what command it is
        // AND tell parent window to close, too
        PostMessage(hWnd, WM_USER, 0, 0);
        DestroyWindow(hDlg);
        return TRUE;
    }
   
    return FALSE;
}

// This is where we draw our image on the desktop
void DrawWindow(HWND hWnd)
{
    // Get the DC to the desktop window
    HDC dc = GetDC(hWnd);

    // Store DC settings
    int storedDC = SaveDC(dc);

    // Draw the rotating star
    POINT pts[5];
    pts[0].x = posX + int(50*cos(angle + 0.000)); pts[0].y = posY + int(50*sin(angle + 0.000));
    pts[1].x = posX + int(50*cos(angle + 2.513)); pts[1].y = posY + int(50*sin(angle + 2.513));
    pts[2].x = posX + int(50*cos(angle + 5.027)); pts[2].y = posY + int(50*sin(angle + 5.027));
    pts[3].x = posX + int(50*cos(angle + 1.257)); pts[3].y = posY + int(50*sin(angle + 1.257));
    pts[4].x = posX + int(50*cos(angle + 3.770)); pts[4].y = posY + int(50*sin(angle + 3.770));
    Polygon(dc, pts, 5);

    // redraw the portion of the window that was just painted over with the rotating star,
    // but specify RDW_NOERASE to keep the desktop from drawing the background as well
    RECT rc = {posX-50, posY-50, posX+50, posY+50};
    RedrawWindow( hWnd, &rc, NULL, RDW_NOERASE | RDW_INVALIDATE | RDW_UPDATENOW );

    // Restore DC settings to their original values
    RestoreDC(dc, storedDC);

    // Release the DC
    ReleaseDC(hWnd, dc);
}
Last edited on
thanks for your responses.

i haven't yet finished analyzing everything, i'll just examine these more and see what i get
-i'm still a beginner and i need time to understand long code-.
and looks like we're in different time regions, i may be asleep when you respond to my posts.

i'll keep you guys up to date.
FYI

I have now run the modified app on Windows 8 (desktop mode, of course) and it seems to be happy enough.

Andy
@andywestken:

i copied the code you posted, and downloaded the remaining from the link you provided.
i tested the project and it looks convenient.

i've been analyzing this code for a few hours now, and after some exhausting search in MSDN i think i got it:

this program creates a window that's the same size as the desktop area, and makes this window transparent.
this window is a layered window, and it's the lowest layer, so every other window is drown on top of it.
the window is also inactive, so it will ignore any user interaction, tricking the user to think that no window even exists.
next the program draws whatever it wants on the client area of the window depending on a timer event.

am i right in this conclusion??
if not, can you help me understand this piece of code??

thank you very much Andy.
you too ahcfan.
That's pretty much it, apart from the bit about the window being inactive.

At it's doing something, it's obviously active. But a layered window lets mouse clicks, etc through to underlying windows (in this case the desktop and its icons) wherever the window is the key color (which is most of the window in this case) or its alpha value is 0.

Andy

Hit Testing

Hit testing of a layered window is based on the shape and transparency of the window. This means that the areas of the window that are color-keyed or whose alpha value is zero will let the mouse messages through.

From: Layered Windows
http://msdn.microsoft.com/en-us/library/ms997507.aspx
Last edited on
hi Andy.
sorry for the late reply, i just hate to trouble people with my issues so i try to solve everything myself before asking for help -that takes time you know-.

so, my project now works fine, but i had to take some "special" measures to make this possible, and unfortunately, some of those measures i can't understand.

in my project i encapsulated the window in a class that will handle all
inter-communications with the program.
this is the class and its constructor:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class FloatingWindow
{
	static bool isRegistered;
	static WNDCLASSEX wc;
	static HBRUSH hbrBackGround;
	static COLORREF crefBackGround;
	HDC hdcMem;
	HBITMAP hbmImage;
	BITMAP bm;
	HWND hwnd;
	COORD Pos;
	COORD delta;
	
	int ID_StoredDC;


	// functions:
	void UpdatePosition();
	void DrawImage();
public:
	static bool RegisterFloatingWindowClass();

	void SetHwnd(HWND w) { this->hwnd = w; }
	void UpdateStatus();
	const HWND GetWindowHandle() { return this->hwnd ; }
	FloatingWindow(void);
	~FloatingWindow(void);
};

FloatingWindow::FloatingWindow(void)
{
	if( this->isRegistered == false )
	{
		if ( this->RegisterFloatingWindowClass() == false )
		{
			return;
		}
		this->isRegistered = true;
	}

	this->hbmImage = LoadBitmap(GetModuleHandle(NULL) , MAKEINTRESOURCE(IDB_BITMAP1));
	GetObject( this->hbmImage , sizeof(BITMAP) , &bm);

	RECT rcDesktop;
	SystemParametersInfo(SPI_GETWORKAREA , NULL , &rcDesktop , NULL);

	this->hwnd = CreateWindowEx(	WS_EX_LAYERED | WS_EX_TRANSPARENT,
									_T("FloatingWindowClass"),
									NULL,
									WS_POPUP,
									rcDesktop.left , rcDesktop.top ,
									rcDesktop.right - rcDesktop.left , rcDesktop.bottom - rcDesktop.top,
									NULL , NULL , GetModuleHandle(NULL) , NULL);
	
	

	if( this->hwnd == NULL )
		PostQuitMessage(-1);

	ShowWindow(this->hwnd , SW_SHOW );
	SetWindowPos(	this->hwnd , HWND_TOPMOST , 0 , 0 , 0 , 0 , SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
	SetLayeredWindowAttributes(hwnd , this->crefBackGround , 0 , LWA_COLORKEY);
	UpdateWindow(this->hwnd);


	this->Pos.X = 0;
	this->Pos.Y = 0;

	this->delta.X = 5;
	this->delta.Y = 5;

	this->hdcMem = NULL;
	this->ID_StoredDC = NULL;
}


when i tried to run this program, nothing happened, and after some exhausting debugging i figured that at line (47), the window creation process, the application creates the window, but hwnd is recieving an invalid handle after the window created.
the debugger stated that hwnd contans a handle that is unused, and draw a small red X marker left of it.

and to make sure of this, i added this lines to the WM_CREATE section:
1
2
3
4
5
6
7
8
9
10
LRESULT CALLBACK FloatingWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) 
    {
    case WM_CREATE:
         //  . . . 
  		wnd.SetHwnd(hWnd);
         //  . . .
        break;
      // . . . 

after this modification, the program worked just fine.

the object named wnd is an instance of this class and is created in the global namespace.

i just can't understand why do i have to re-initialize the HWND after the creation of the window, can this be because of the encapsulation of the HWND

i still have other issues, but i'll leave them for after this is answered.
Your change to WM_CREATE doesn't really make sense to me.

If the global wnd is the FloatingWindow you're constructing, then setting its hwnd value after CreateWindow has returned will overwrite the value with whatever CreateWindow returns.

So it might be something going on in you WM_CREATE handle that is the problem??

Andy
you know what, sometimes i just hate this language.
i deleted the call to FloatingWindow::SetHwnd() and the program still works as predicted.
maybe my IDE is pranking me :P.

maybe the error was actually something else, and maybe i corrected it accidentally in the debugging process.

anyway, i have another question:
in your code, you used the extended style WS_EX_LAYERED only.
when i used this in my program, the window still catches input and mouse clicks don't penetrate it.
i had to use WS_EX_LAYERED | WS_EX_TRANSPARENT in order to make it transparent to mouse events.
can you explain this for me please?
I think it's because you've got more non-background pixels than the toy I posted.

WS_EX_TRANSPARENT instructs the system to let all clicks though everywhere in your window, whereas WS_EX_LAYERED alone only lets clicks through where the transparency is zero or it's the key color.

The MSDN docs on this are a bit confusing...

Andy
Last edited on
true, i'm actually painting a bitmap on the window.

however, the bitmap doesn't occupy so much space of the window, and like you mentioned, wherever the color is the same as the reference color then mouse clicks should get through.

i still can't see why WS_EX_LAYERED failed in my case, yet worked in yours.

my program catches input whenever you click anywhere on the desktop, even away from the bitmap.
Well, you've got my test code!

What do you mean by your program catching the mouse clicks? Have you got a WM_LBUTTONDOWN handler??

Andy

PS Does using just WS_EX_LAYERED allow you interact with the desktop icons? You're using HWND_TOPMOST where I use HWND_BOTTOM, so I only have the desktop icons below me.
Use both WS_EX_LAYERED and WS_EX_TRANSPARENT ?
I can provide an extended example using them both, reply "ok then" if you want to.
@andy:
no i haven't got a WM_LBUTTONDOWN handler.
i know that my program is catching mouse input because, once you click on an empty space on the desktop, you can't send any more mouse clicks to any other application, you can't even access the old (right click on desktop) menu.

in my current experiment i returned it to HWND_BOTTOM, and it still catches mouse clicks.
looks like my window is transparent in its colors only.

@EssGeEich:
if you think your code can demonstrate something i'm unaware of, then i'd be pleased to have a look at it.


i think you can't figure it out without the code i used, here it is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>
#include "FloatingWindow.h"
#include "resource.h"

// Global Variables:
HWND hDlg        = NULL;
FloatingWindow wnd;

INT_PTR CALLBACK DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);


// Main function
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    MSG msg;
    while( GetMessage(&msg, NULL, 0, 0) ) 
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    //DeleteObject(hbrBkgnd);

    return msg.wParam;
}


LRESULT CALLBACK FloatingWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) 
    {
    case WM_USER:
        // Close the main window when the dialog tells us to
        DestroyWindow(hWnd);
        return TRUE;

    case WM_CREATE:
        // Create a dialog so we have a way of closing the app
        hDlg = CreateDialog(GetModuleHandle(0), MAKEINTRESOURCE(IDD_DIALOG1), 0, (DLGPROC)DialogProc);
		RECT rcDesktop , rcDlg;
		GetClientRect(GetDesktopWindow() , &rcDesktop);
		GetClientRect(hDlg , &rcDlg);
		SetWindowPos(hDlg , HWND_BOTTOM ,	rcDesktop.right - (rcDlg.right - rcDlg.left) , 0 ,0 , 0 , SWP_NOSIZE | SWP_NOSENDCHANGING);

        ShowWindow(hDlg, SW_SHOW);
		//wnd.Sethwnd(hWnd);
        // Create a timer so that we can animate our drawing
        SetTimer(hWnd, 1, 25, 0);
        break;

    case WM_DESTROY:
        // Kill our timer
        KillTimer(hWnd, 1);

        // Post the quit message so that our main loop will know that we have quit
        PostQuitMessage(0);
        break;

    case WM_TIMER:
        {

			//SetWindowPos(wnd.GetWindowHandle() , HWND_TOPMOST , 0 , 0 , 0 , 0 , SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
			wnd.UpdateStatus();
        }
        break;

        default:
            return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }

    return 0;
}

INT_PTR CALLBACK DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_COMMAND:
		// Close the dialog no matter what command it is
		// AND tell parent window to close, too
		PostMessage(wnd.GetWindowHandle(), WM_USER, 0, 0);
        DestroyWindow(hDlg);
        return TRUE;
    }
   
    return FALSE;
}

the class functions:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include "FloatingWindow.h"
#include "resource.h"
#include <Windows.h>
#include <tchar.h>
#define MYWAY
#ifdef MYWAY


bool		FloatingWindow::isRegistered	= false;
COLORREF	FloatingWindow::crefBackGround	= NULL;
HBRUSH		FloatingWindow::hbrBackGround = NULL;
WNDCLASSEX	FloatingWindow::wc = {NULL};

LRESULT CALLBACK FloatingWindowProc( HWND hwnd , UINT msg , WPARAM wparam , LPARAM lparam);


bool	FloatingWindow::RegisterFloatingWindowClass()
{
	FloatingWindow::crefBackGround		= RGB (255,255,0);
	FloatingWindow::hbrBackGround		= CreateSolidBrush(FloatingWindow::crefBackGround);

	FloatingWindow::wc	.cbClsExtra = 0;
	wc.cbSize = sizeof(wc);
	wc.cbWndExtra = 0;
	wc.hbrBackground = hbrBackGround;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hIcon = LoadIcon(GetModuleHandle(0) , MAKEINTRESOURCE(IDI_ICON1));
	wc.hIconSm = LoadIcon(GetModuleHandle(0) , MAKEINTRESOURCE(IDI_ICON1SM));
	wc.hInstance = GetModuleHandle(NULL);
	wc.lpfnWndProc = FloatingWindowProc;
	wc.lpszClassName = _T("FloatingWindowClass");
	wc.lpszMenuName = NULL;
	wc.style = NULL;

	if( RegisterClassEx( &wc ) == NULL)
		return false;
	else
		return true;
}


FloatingWindow::FloatingWindow(void)
{
	if( this->isRegistered == false ) // register the class only when the first object gets created
	{
		if ( this->RegisterFloatingWindowClass() == false )
		{
			return;
		}
		this->isRegistered = true;
	}

	this->hbmImage = LoadBitmap(GetModuleHandle(NULL) , MAKEINTRESOURCE(IDB_BITMAP1));
	GetObject( this->hbmImage , sizeof(BITMAP) , &bm);

	RECT rcDesktop;
	SystemParametersInfo(SPI_GETWORKAREA , NULL , &rcDesktop , NULL);

	this->hwnd = CreateWindowEx(	WS_EX_LAYERED ,
					_T("FloatingWindowClass"),
					NULL,
					WS_POPUP,
					rcDesktop.left , rcDesktop.top ,
					rcDesktop.right - rcDesktop.left , rcDesktop.bottom - rcDesktop.top,
					NULL , NULL , GetModuleHandle(NULL) , NULL);
	
	

	if( this->hwnd == NULL )
		PostQuitMessage(-1);

	ShowWindow(this->hwnd , SW_SHOW );
	SetWindowPos(	this->hwnd , HWND_BOTTOM , 0 , 0 , 0 , 0 , SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
	SetLayeredWindowAttributes(hwnd , this->crefBackGround , 0 , LWA_COLORKEY);
	UpdateWindow(this->hwnd);


	this->Pos.X = 0;
	this->Pos.Y = 0;

	this->delta.X = 5;
	this->delta.Y = 5;

	this->hdcMem = NULL;
	this->ID_StoredDC = NULL;
}


FloatingWindow::~FloatingWindow(void)
{
	DeleteDC(this->hdcMem);
	RestoreDC(this->hdcMem , this->ID_StoredDC);

	DeleteObject(this->hbmImage);
}


void FloatingWindow::UpdateStatus()
{
	// Redraw the old position of the image to erase it.
	RECT rc = { this->Pos.X , this->Pos.Y , this->Pos.X + this->bm.bmWidth , this->Pos.Y + this->bm.bmHeight };
	RedrawWindow(this->hwnd , &rc , NULL , RDW_ERASE |  RDW_INVALIDATE | RDW_UPDATENOW );


	this->UpdatePosition();

	this->DrawImage();

}



void FloatingWindow::UpdatePosition()
{
	RECT rcWindow;
	SystemParametersInfo(SPI_GETWORKAREA , NULL , &rcWindow , NULL);


	// if the bitmap reaches the border of the window, reverse it move direction.
	if( (this->Pos.X < 0) || ((this->Pos.X + this->bm.bmWidth) > rcWindow.right) )
		this->delta.X = - this->delta.X;

	if( (this->Pos.Y < 0) || ((this->Pos.Y + this->bm.bmHeight) > rcWindow.bottom) )
		this->delta.Y = - this->delta.Y;


	
	this->Pos.X += this->delta.X;
	this->Pos.Y += this->delta.Y;
}


void FloatingWindow::DrawImage()
{
	HDC hdcWnd = GetDC(this->hwnd);
	this->hdcMem = CreateCompatibleDC(hdcWnd);
	this->ID_StoredDC = SaveDC(this->hdcMem);

	SelectObject(this->hdcMem , this->hbmImage);

	BitBlt(hdcWnd , this->Pos.X , this->Pos.Y , this->bm.bmWidth , this->bm.bmHeight , this->hdcMem , 0 , 0 , SRCCOPY);

	DeleteDC(this->hdcMem);
	RestoreDC(this->hdcMem , this->ID_StoredDC);
	ReleaseDC(this->hwnd , hdcWnd);

}
#endif 


maybe you can see something i couldn't see.
I see you want a work-area window.
This could be easier as I did a work-area program (Using a layered, so 32-bit-per-pixel, hardware-accelerated window), so I know how to deal with things.

Firstly:
You WILL use UpdateLayeredWindow.
No matter what, you MUST use it.
It's not hard to use it, but I see you're not using it.
This could be the issue.

Now, to the code:
I have my class to deal with basic initialization so I'll skip the WNDCLASSEX things and so on and I'll compress everything in:
Init/Paint/OnMessage/Free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// Stores per-window data
struct WindowData {
    // initialize them
    WindowData() : MemDC(0), DIBSectionBitmap(0), OldDC(0), Handle(0) {}
    HWND Handle;
    HDC MemDC;
    HBITMAP DIBSectionBitmap;
    HDC OldDC;
};

// In WM_CREATE, Returns 1 for success, 0 for failure.
bool Initialize(HWND Handle, WindowData& wd)
{
    wd.Handle = Handle;
    RECT r = {0};
    GetWindowRect(Handle,&r);
    HDC scrDC = GetDC(0);
    wd.MemDC = CreateCompatibleDC(scrDC);
    ReleaseDC(0,scrDC);
    if(!wd.MemDC)
        return 0;
    BITMAPINFO bmi = {0};
    bmi.bmiHeader.biBitCount = 32;
    bmi.bmiHeader.biCompression = BI_RGB;
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biWidth = r.right-r.left;
    bmi.bmiHeader.biHeight = r.bottom-r.top;
    bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
    wd.DIBSectionBitmap = CreateDIBSection(MemDC,&bmi,DIB_RGB_COLORS,0,0,0);
    if(!wd.DIBSectionBitmap)
        return 0;
    wd.OldBmp = (HBITMAP)SelectObject(wd.MemDC,wd.DIBSectionBitmap);
    return 1;
}

// call this AFTER painting to MemDC! Don't use BeginPaint or GetDC for this!
// Draw DIRECTLY into wd.MemDC!
void Paint(WindowData& wd)
{
    BLENDFUNCTION bf = {0};
    bf.BlendOp = AC_SRC_OVER;
    bf.SourceConstantAlpha = 0xFF;
    bf.AlphaFormat = AC_SRC_ALPHA;
    RECT rcWin = {0};
    GetWindowRect(wd.Handle,&rcWin);
    POINT ptw = {rcWin.left,rcWin.top};
    SIZE pts = {rcWin.right-rcWin.left,rcWin.bottom-rcWin.top};
    POINT ptsrc = {0};
    HDC scrDC = GetDC(0);
    UpdateLayeredWindow( wd.Handle,
        scrDC,&ptw,&pts,wd.MemDC,&ptsrc,0,&bf,ULW_ALPHA);
    ReleaseDC(0,scrDC);
}

// In WM_DESTROY
void Free(WindowData& wd)
{
    if(wd.MemDC && wd.OldBmp)
        SelectObject(wd.MemDC,wd.OldBmp);
    if(wd.DIBSectionBitmap)
        DeleteObject(wd.DIBSectionBitmap);
    if(wd.MemDC)
        DeleteDC(wd.MemDC);
}

// Your window procedure
LRESULT OnMessage(HWND Window, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    if(Msg == WM_NCHITTEST)
        return HTNOWHERE; // ignore clicks
    // ... your window proc's other stuff
}


There's nothing special besides that.
You SHOULD specify CS_HREDRAW|CS_VREDRAW for WNDCLASSEX's flags.
For CreateWindow's flags:
Regular flags: WS_POPUP
Extended flags: WS_EX_LAYERED|WS_EX_TRANSPARENT

You can also use WS_EX_TOPMOST to make it an overlay-ish program.

Another suggestion:
GDI f*cks Alpha channel up.
Before debugging, make sure you know which key is bound for your "Terminate Debug" command.
If you see the Alpha channel f*cked up, you should use something else for the drawings.
I use D2D1 but it only works on Vista SP2, 7 SP1 and 8.
D3D9 does NOT work, it fails to create the device if the window has WS_EX_LAYERED property, and applying it afterwards seems to have no effect.
I don't know about 10 and 11.
Otherwise you can get a pointer to the raw MemDC's memory.

For further questions, leave a note, I'll reply with (if possible) answers.
Last edited on
Now I've see your program in action, I think EssGeEich's suggestion to use UpdateLayeredWindow (with a small window, the size of you ball, which moves around) might make more sense. Particularly if your bitmap is fixed and it's just bouncing about.

The approach I suggested came from an old school approach (drawing onto the desktop), which was only ever a hack. Hence the move to a layered window, in an attempt to modernize.

But I wouldn't go so far to say you should use it no matter what. for example, if you want to have lots of small balls bouncing around the screen at the same time, then the alternative approach might come into it's own; it's (sort of) the approach used by this Snow app:
Falling Snow on Your Desktop! Part II
http://www.codeproject.com/Articles/22153/Falling-Snow-on-Your-Desktop-Part-II
(I haven't looked at the code closely, but it's (a) using RedrawWindow with the desktop windows, (b) probably a bit wasteful of resources, and written in MFC...)

What size bitmap are you using for your ball? I'm getting a lot of flicker with my test one...

Andy
Last edited on
OK, after seeing that last code by EssGeish, i think i'm taking a bite i can't swallow.

i'm really so new to all this GDI thing, i don't even know what is the difference between a DIB and a normal bitmap.

for now, i'll mark this question as solved (although its not), i just want some link to a good tutorial about GDI which is really "thorough".

the tutorial i need must take me most of the way to become an expert in GDI and graphics programming, maybe after i've finished with it i can post whatever else questions that cross my mind.
You mentioned that your a beginner eariler on:

Have you worked through (the relevant bits of) Petzold's Programming Windows? That's the usual place to start when it comes to classic Windows API programming (formally known as Win32, before the 64-bit version appeared.)

Aside: as you're already using a window class, rather than coding against the Windows API in a totally C-style, you might be interested in the WM_NCCREATE/SetWindowLongPtr trick as used in this article:
The new scratch program
http://blogs.msdn.com/b/oldnewthing/archive/2005/04/22/410773.asp
(Googling "WM_NCCREATE SetWindowLongPtr" will find plenty of other examples, including some on this very site.)

I don't know what the current best book for Direct3D etc is. I tend to learn from MSDN plus the sample code (which sometimes illustrates things which are unclear in, or even missed out of, the documention.) Plus reading peoples questions and complaints about the APIs on various forums!

But I'm not sure focussing on GDI would be right these days. It's not going away anytime soon, so you need to know GDI. But for advanced graphics there are other preferred APIs. EssGeEich has already mentions Direct2D and Direct3D in passing.

These overviews might help to see what technologies are available, and how they relate to each other. I'm not yet up to speed with the latest round of changes, as the apps I've worked on have all had to work from Windows XP SP2 and up, so couldn't use too many modern features (sometimes you do use different APIs for different versions of Windows, but you usually try to keep these to a minimum.)

Overview of the Windows Graphics Architecture
http://msdn.microsoft.com/en-us/library/windows/desktop/ff684176%28v=vs.85%29.aspx

Interoperability Overview
http://msdn.microsoft.com/en-us/library/windows/desktop/dd940320%28v=vs.85%29.aspx

Andy

PS One of the articles I bumped into when checking my facts for this thread is this: Redirecting GDI, DirectX, and WPF applications
http://blogs.msdn.com/b/greg_schechter/archive/2006/05/02/588934.aspx

It's quite techical, and a from a while ago, but it might be worth reading.

I found it when looking for more information about the Desktop Window Manager, which I normally just leave to get on with its job.

I was aware that the old (i.e. Windows XP and earlier) trick of drawing on the desktop window could be problematic in Windows Vista and later, due to the DWM, so looked into the matter.

To get your bouncing ball program to work correctly on post-Windows XP machines you will (probably) have to learn about the DWM and Direct3D, etc.

Last edited on
Pages: 12