4

Possible Duplicate:
Draw on screen with GDI+ (or GDI) similar to Inspect

I'm attempting to write a snake game that has no windows, but freezes the foreground and draws the snake on top of it. When the game ends the foreground should be unfrozen.

I have written some testing code that is supposed to draw a square on the foreground, but all it seems to do is freeze the desktop for a second and freeze the window in the foreground until I minimize, maximize, close it, or bring another window into the foreground, and it doesn't draw anything. In the code, I try to store a bitmap of the desktop so that I can essentially reset it to it's original state and paint the square in a different position. Can anybody spot the problem with my code?

//Handle to the desktop window
HWND hDesktopWindow = GetDesktopWindow();

//Lock the window to prevent other applications drawing on it
if(!LockWindowUpdate(hDesktopWindow)){
    return 1;
}

//Calling GetDC with argument NULL retrieves the desktop's DC
HDC hdcDesktop = GetDCEx(hDesktopWindow, NULL, DCX_CACHE | DCX_LOCKWINDOWUPDATE);

//Create a compatible DC to allow us to store a bitmap of the desktop
HDC hdcCompatible;
if((hdcCompatible = CreateCompatibleDC(hdcDesktop)) == NULL){
    return 1;
}

//Create a compatible bitmap with the same dimensions as the desktop
HBITMAP hScrBitmap;
int cx = GetSystemMetrics(SM_CXSCREEN);
int cy = GetSystemMetrics(SM_CYSCREEN);
if((hScrBitmap = CreateCompatibleBitmap(hdcDesktop, cx, cy)) == NULL){
    return 1;
}

//Select the bitmap into the compatible DC
SelectObject(hdcCompatible, hScrBitmap);

//Copy the Desktop into the bitmap
if(!BitBlt(hdcCompatible, 0, 0, cx, cy, hdcDesktop, 0, 0, SRCCOPY)){
    return 1;
}

//Create a DC compatible with the bitmaps DC for drawing the rectangle
HDC hdcRectangle;
if(!CreateCompatibleDC((HDC)hScrBitmap)){
    return 1;
}

//Create a compatible bitmap for the rectangle to be drawn in
HBITMAP hRectangleBitmap;
if(!CreateCompatibleBitmap(hdcRectangle, 100, 100)){
    return 1;
}

//Fill the rectangle bitmap
if(!FloodFill(hdcRectangle, 0, 0, RGB(255,0,0))){
    return 1;
}

//Copy the rectangle onto the desktop bitmap
if(!BitBlt(hdcCompatible, 100, 100, 100, 100, hdcRectangle, 0, 0, SRCCOPY)){
    return 1;
}

//Copy the bitmap onto the actual desktop
if(!BitBlt(hdcDesktop, 0, 0, cx, cy, hdcCompatible, 0, 0, SRCCOPY)){
    return 1;
}

//Allow time to view the result
Sleep(1000);

//Allow other applications to draw on the desktop again
LockWindowUpdate(NULL);

//Cleanup
ReleaseDC(hDesktopWindow, hdcDesktop);
DeleteDC(hdcCompatible);
DeleteObject(hScrBitmap);

Any help would be greatly appreciated :)

Community
  • 1
  • 1
brnby
  • 1,433
  • 1
  • 19
  • 35
  • you forgot to select `hRectangleBitmap` into `hdcRectangle` –  Nov 21 '12 at 12:09
  • 1
    Short answer: don't draw directly on the desktop. – Andriy Nov 21 '12 at 12:10
  • @Andrey, well he doesn't. He draws to the buffer and then does bitblt. I don't see any problem with that. –  Nov 21 '12 at 12:12
  • 1
    @aleguna: `BitBlt(hdcDesktop, ...)` draws on the desktop. – Andriy Nov 21 '12 at 12:15
  • @Andrey, that's what I said. Anyway, so what? Many applications do that, it's entirely legal in widnows. –  Nov 21 '12 at 12:16
  • @aleguna: Could you give some examples of "many applications"? – Andriy Nov 21 '12 at 12:19
  • [How to effectively draw on desktop in C#?](http://stackoverflow.com/a/2905852/1345960) – Andriy Nov 21 '12 at 12:23
  • @Andrey, screenshot grabbers –  Nov 21 '12 at 12:40
  • 1
    let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/19887/discussion-between-andrey-and-aleguna) – Andriy Nov 21 '12 at 12:43
  • Drawing directly onto the desktop is a necessity for my program :( – brnby Nov 21 '12 at 12:51
  • 1
    @aleguna: Decent applications don't draw directly on desktop - it will cause problems, because OS will keep repainting portions of desktop, destroying the image. Use fullscreen mode in SDL, DirectX or OpenGL if you want to draw on screen. Or use maximized windows. – SigTerm Nov 21 '12 at 17:56

2 Answers2

5

Trying to do this directly on the desktop is going to be problematic. You'd be better off by taking a snapshot of the desktop, then create a window that's the size of the whole desktop, copy the snapshot to the window, and do all your drawing there. (This was a common trick done in old screensavers that did things like "erode" the desktop.)

You don't own the desktop window, so you'll always have problems with invalidation and repaints.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
3
if(!CreateCompatibleDC((HDC)hScrBitmap)){
    return 1;
}

When you write C code like this then a single-point-of-return tends to be very important. A call like this is going to return FALSE, can't cast a HBITMAP to HDC, and the show is over badly. No diagnostic and no call to unlock again.

Favor the C++ RAII pattern to ensure that you always unlock:

class DesktopLocker {
public:
    DesktopLocker() { LockWindowUpdate(GetDesktopWindow()); }
    ~DesktopLocker() { LockWindowUpdate(NULL); }
};

void foo() {
   DesktopLocker lock;
   // etc...
}

There's not much wisdom of painting directly to the desktop window, there's little guarantee that whatever you draw will last. Sleeping and locking updates are just band-aids. Take a look at the source of Rainmeter, it's well done.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thank you for your advice. Although my code was written as a test (so I didn't really care about the cleanup too much) your advice will certainly come in handy when I use this code in my project and in future projects, thank you :) I'm going to look into rainmeter and also edit my question to state why I need to draw on the desktop and exactly what I need to achieve :D – brnby Nov 21 '12 at 13:00
  • 1
    Excellent advice but off-topic for this question. – Adrian McCarthy Nov 21 '12 at 17:32