Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw on the windows desktop using the GDI API? [duplicate]

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 :)

like image 484
brnby Avatar asked Feb 18 '23 10:02

brnby


2 Answers

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.

like image 131
Adrian McCarthy Avatar answered Feb 24 '23 23:02

Adrian McCarthy


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.

like image 32
Hans Passant Avatar answered Feb 25 '23 00:02

Hans Passant