Bitmap disappearing when program is minimized - c++

I'm a student for c++ game-programming. We have to program a textadventure on console for windows.
We get points for the implementation of bitmaps.
I have the following code to Display the bitmap. It works well but when i minimize my window, my bitmap disappears.
Where's the problem?
bool DisplayBitmap(char *szBitmap, int PosX = -1, int PosY = -1, bool AutoZoom = false)
{
HWND hWnd = FindWindow("ConsoleWindowClass",NULL); // Konsolen-Fenster identifizieren
if (!hWnd) return false;
HDC hDC = GetDC(hWnd); // Device Context des Fensters ermitteln
if (!hDC) return false;
HDC hBitmapDC = CreateCompatibleDC(hDC); // Device Context für Bitmap bereitstellen
if (!hBitmapDC) return false;
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL,szBitmap, // Bild aus Datei laden
IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
if (!hBitmap) return false;
BITMAP bmp;
GetObject(hBitmap,sizeof(bmp),&bmp); // Zeiger auf Bitmap-Objekt anfordern
RECT Client;
GetClientRect(hWnd, &Client); // Fenstergröße bestimmen
if (PosX == -1) PosX = ((Client.right-Client.left)-bmp.bmWidth) / 2;
if (PosY == -1) PosY = 250-bmp.bmHeight; // ggfs. automatisch zentrieren
if (PosY < 0) PosY = 0; // und Zoomfaktor berechnen
double ZoomFactor = (Client.right-Client.left-2*PosX) / double(bmp.bmWidth);
SelectObject(hBitmapDC,hBitmap); // Bitmap kopieren (evtl. mit Zoom)
if (!AutoZoom) BitBlt(hDC,PosX,PosY,bmp.bmWidth,bmp.bmHeight,hBitmapDC,0,0,SRCCOPY);
else StretchBlt(hDC,PosX,PosY,int(bmp.bmWidth*ZoomFactor),int(bmp.bmHeight*ZoomFactor),hBitmapDC,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);
DeleteObject(hBitmap); // Objekte und Device Contexts wieder freigeben
ReleaseDC(hWnd,hBitmapDC);
ReleaseDC(hWnd,hDC);
return true;
}

The bitmap disappears because the console window is repainted when restored and the repainting procedure knows nothing about your bitmap.
Windows are repainted quite often: every time your window is brought up or resized or restored from iconic state the window is repainted (well, may be not every time, because the system can make some caching... but still). When the window should be repainted, Windows sends WM_PAINT message to the window. Tha application handles this message and redraws the window contents in the window DC. This is how GUI-applications work. But if you use console, you can not handle window messages -- the system does it for you, and you can not change the repainting procedure.
If you want your bitmap to be always there, you should create your own window and process the WM_PAINT message (or, you can use some predefined control intended for displaying bitmaps). To do this you must create a GUI-application. You can not do this with console application.
The code you provided is just a hack and it is not supporsed to work as you expect. When the code is executed the bitmap is drawn. But once the part of the window is hidden beyond another window the image will be lost and nobody will restore it.
By the way, if your professor gave you this example, probably he will be satisfied by the result it produces. But never do like this in real life :)

Related

Why when I paint, and the mouse is moving in the window, the painting is not done at the same time everywhere? (WinAPI)

After asking this question, I changed my code. It works, but when WM_PAINT paints the window and the cursor is moving in it, the painting is not done at the same time everywhere. Here you have a video to see it better. This is my WM_PAINT:
//TV is a struct with the dimensions of the window.
//BitmapBuffer is the bitmap containing the painting.
static int cont;
cont++;
HDC HDc;
PAINTSTRUCT ps;
HDc = BeginPaint(identifier, &ps);
Gdiplus::Graphics lienzo (HDc);
AlphaBlend(HDc, 0, 0, TV.width+4, TV.height+4, buffer, 0, 0, TV.width+4, TV.height+4, CopyInfo);
EndPaint(identifier, &ps);
Since the problem is when moving the mouse, maybe the WM_NCHITTEST message has something to do with it:
case WM_NCHITTEST:
POINTS point1 = MAKEPOINTS(lParam);
POINT point2;
point2.x = point1.x;
point2.y = point1.y;
ScreenToClient(hwnd, &point2);
if (PtInRegion(region, point2.x, point2.y))
{
if (inWindow == false) //inWindow is true when the cursor is in the window
{
inWindow = true;
TrackMouseEvent(&structure);
Repaint(); //this function repaint the buffer and after call invalidrect
}
return HTCLIENT;
}
else
{
if (inWindow == true)
{
inWindow = false;
Repaint();
}
return HTTRANSPARENT;
}
break;
Does anyone have an idea why this happens?
It's hard to see the problem, because your code doesn't define Repaint. However, you should be using InvalidateRect to tell Windows which area is being updated.
THAT SAID...
Windows is hard to program for updating images with out using a double buffering technique. This is when you draw to a memory (bitmap) then bitblt the bitmap to the screen.
You find this answer useful Reduce flicker with GDI+ and C++

Why can't i set a thumbnail on the Taskbar using DwmSetIconicThumbnail?

I need your help for a project at my job.
The main software, using 4D language, works as a MDI, it creates many windows included in the main window. The main issue is that we have a lot of windows and we need an easy way to switch from a window to another.
We decided to create a little c++ plugin which is a dll to resolve this issue.
This plugin will create a tab on the taskbar for each window opened like Windows Explorer. The creation and the deletion of tabs already works.
But the current problem is that no thumbnail is set on the Tab.
The parameter given is the ID of the window from the main software. The method called PA_GetHWND is the method given by 4D to obtain the handle of the window using the windowID.
I have already check where is the problem. The bitmap created from the window already exists and is good. For this test, i put the bitmap in the clipboard and paste it on Paint and the Bitmap was good.
Here is the code introducing the method to refresh the bitmap on the tab.
bool CManageTaskBar::UpdateWindow(long WindowID)
{
HRESULT res;
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
//Get the Handle from 4D
HWND nHandle = (HWND)(PA_GetHWND((PA_WindowRef)(WindowID)));
// Retrieve the handle to a display device context for the client
// area of the window.
hdcScreen = GetDC(NULL);
hdcWindow = GetDC(nHandle);
// Create a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC(hdcWindow);
// Get the client area for size calculation
RECT rcClient;
GetClientRect(nHandle, &rcClient);
// Create a compatible bitmap from the Window DC
hbmScreen = CreateCompatibleBitmap(hdcWindow,rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
// Select the compatible bitmap into the compatible memory DC.
SelectObject(hdcMemDC, hbmScreen);
// Bit block transfer into our compatible memory DC.
if (!BitBlt(hdcMemDC,
0, 0,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
hdcWindow,
0, 0,
SRCCOPY))
{
MessageBox(nHandle, L"BitBlt has failed", L"Failed", MB_OK);
//goto done;
}
ITaskbarList3* ptbl = NULL;
HRESULT hr = CoCreateInstance(my_CLSID_TaskbarList, NULL, CLSCTX_ALL, my_IID_ITaskbarList3, (LPVOID*)&ptbl);
BOOL fForceIconic = TRUE;
BOOL fHasIconic = TRUE;
res = DwmSetWindowAttribute(nHandle, DWMWA_FORCE_ICONIC_REPRESENTATION, &fForceIconic, sizeof(fForceIconic));
res = DwmSetWindowAttribute(nHandle, DWMWA_HAS_ICONIC_BITMAP, &fHasIconic, sizeof(fHasIconic));
if (hbmScreen)
{
res = DwmSetIconicThumbnail(nHandle, hbmScreen,0);//DWM_SIT_DISPLAYFRAME);
}
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL, hdcScreen);
ReleaseDC(nHandle, hdcWindow);
return true;
}
The calls to DwmSetWindowAttribute return Invalid Handle. This handle works to get a bitmap but not to set an attribute.
And the call to DwmSetIconicThumbnail returns E_INVALIDARG maybe because the handle given is wrong.
Why cannot I set an attribute to this handle and why the call to set the thumbnail returns E_INVALIDARG ?
Thanks to everyone who will take care of my problem.
It's my first question, be friendly please :)

draw cartesian system using win32 api

I'm trying to draw sine wave using win32 api. I done this.
hDC = GetDC(hWnd);
while (TRUE)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
DispatchMessage(&msg);
}
if (msg.message == WM_QUIT)
break;
wavefunc(hWnd, hDC);
}
void wavefunc(HWND hWnd, HDC hDC)
{
double full = 2 * pi * _freq;
static double _x = 0;
short int _y = 0;
short _y = (short)(sin(_x / _freq)*_amp) + 300;
if (_x >= full)
_x -= full;
SetPixel(hDC, 600, _y, blue);
ScrollWindow(hWnd, -1, 0, NULL, NULL);
Sleep(_sTime);
_x++;
}
Now i'm trying to figure out how to draw cartesian system. But with no result.
I have a window which is scrolled at each sin value.
I try to draw to lines on hdc. When window is scrolled there is no way to stop hdc to not scroll.
Then i create another hDc from windows but no succes.
How to do this? The problem is to have window scrolling but with some point fixes..
The way to do this is to paint everything all at once: clear the window, draw the grid, and then draw the entire waveform (not just the next point) over the grid. To make the waveform scroll, you keep redrawing with a different offset for the waveform.
To avoid flicker, you will likely need to do some double-buffering.
Typical WinAPI programs paint the window when handling the WM_PAINT command. The WM_PAINT message is generated when any or all of the window is invalid and needs to be repainted. Each time you scroll the window, you're creating an invalid region, but since you're ignoring the WM_PAINT message, nothing happens.
Here is how I would animate the sin() function:
Create a bitmap (the size of your window) and draw your static parts on it (axis, title, labels, etc.).
Create another bitmap (the height of your sin() and the width of your window + the width of the sin()’s period at your scale), and draw sin() on it.
In the wavefunc(), get the precise time and calculate the offset for your sin bitmap.
Use BitBlt() function to first blit the static part onto your window’s DC, then use TransparentBlt() to paint the sin, starting at pre-calculated offset.

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

This question already has an answer here:
Closed 10 years ago.
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 :)
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.
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.

Creating a transparent window in C++ Win32

I'm creating what should be a very simple Win32 C++ app whose sole purpose it to ONLY display a semi-transparent PNG. The window shouldn't have any chrome, and all the opacity should be controlled in the PNG itself.
My problem is that the window doesn't repaint when the content under the window changes, so the transparent areas of the PNG are "stuck" with what was under the window when the application was initially started.
Here's the line where I setup the new window:
hWnd = CreateWindowEx(WS_EX_TOPMOST, szWindowClass, szTitle, WS_POPUP, 0, height/2 - 20, 40, 102, NULL, NULL, hInstance, 0);
For the call to RegisterClassEx, I have this set for the background:
wcex.hbrBackground = (HBRUSH)0;
Here is my handler for WM_PAINT message:
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
Gdiplus::Graphics graphics(hdc);
graphics.DrawImage(*m_pBitmap, 0, 0);
EndPaint(hWnd, &ps);
break;
}
One thing to note is that the application is always docked to the left of the screen and doesn't move. But, what's underneath the application may change as the user opens, closes or moves windows under it.
When the application first starts, it looks perfect. The transparent (and simi-transparent) parts of the PNG show through perfectly. BUT, when the background underneath the application changes, the background DOESN'T update, it just stays the same from when the application first started. In fact, WM_PAINT (or WM_ERASEBKGND does not get called when the background changes).
I've been playing with this for quite a while and have gotten close to getting 100% right, but not quite there. For instance, I've tried setting the background to (HBRUSH) NULL_BRUSH and I've tried handling WM_ERASEBKGND.
What can be done to get the window to repaint when the contents under it changes?
I was able to do exactly what I wanted by using the code from Part 1 and Part 2 of this series:
Displaying a Splash Screen with C++
Part 1: Creating a HBITMAP archive
Part 2: Displaying the window archive
Those blog posts are talking about displaying a splash screen in Win32 C++, but it was almost identical to what I needed to do. I believe the part that I was missing was that instead of just painting the PNG to the window using GDI+, I needed to use the UpdateLayeredWindow function with the proper BLENDFUNCTION parameter. I'll paste the SetSplashImage method below, which can be found in Part 2 in the link above:
void SetSplashImage(HWND hwndSplash, HBITMAP hbmpSplash)
{
// get the size of the bitmap
BITMAP bm;
GetObject(hbmpSplash, sizeof(bm), &bm);
SIZE sizeSplash = { bm.bmWidth, bm.bmHeight };
// get the primary monitor's info
POINT ptZero = { 0 };
HMONITOR hmonPrimary = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO monitorinfo = { 0 };
monitorinfo.cbSize = sizeof(monitorinfo);
GetMonitorInfo(hmonPrimary, &monitorinfo);
// center the splash screen in the middle of the primary work area
const RECT & rcWork = monitorinfo.rcWork;
POINT ptOrigin;
ptOrigin.x = 0;
ptOrigin.y = rcWork.top + (rcWork.bottom - rcWork.top - sizeSplash.cy) / 2;
// create a memory DC holding the splash bitmap
HDC hdcScreen = GetDC(NULL);
HDC hdcMem = CreateCompatibleDC(hdcScreen);
HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpSplash);
// use the source image's alpha channel for blending
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
// paint the window (in the right location) with the alpha-blended bitmap
UpdateLayeredWindow(hwndSplash, hdcScreen, &ptOrigin, &sizeSplash,
hdcMem, &ptZero, RGB(0, 0, 0), &blend, ULW_ALPHA);
// delete temporary objects
SelectObject(hdcMem, hbmpOld);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdcScreen);
}
Use the SetLayeredWindowAttributesarchive function, this allows you to set a mask color that will become transparent, thus allowing the background to show through.
You will also need to configure your window with the layered flag, e.g.:
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
After that it's fairly simple:
// Make red pixels transparent:
SetLayeredWindowAttributes(hwnd, RGB(255,0,0), 0, LWA_COLORKEY);
When your PNG contains semi-transparent pixels that you want to blend with the background, this becomes more complicated. You could try looking at the approach in this CodeProject article:
Cool, Semi-transparent and Shaped Dialogs with Standard Controls for Windows 2000 and Above