bitblt failed on Windows 10 version 1703 (15063.138) - c++

using Visual Studio 2017, vc141, the following code should got a screenshot from front game window but now it return a black and blank image.
only issue with games(tried OpenGL and Vulkan, ogl return black, vulkan return white)
before upgrade to windows 10 1703, it works on windows 10 1607 and windows 7 sp1
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
code:
BOOL ScreenShot(cv::Mat *img, HWND hWnd = NULL) {
HBITMAP hBitmap;
HDC hdcSys = GetDC(hWnd);
HDC hdcMem = CreateCompatibleDC(hdcSys);
void *ptrBitmapPixels;
BITMAPINFO bi;
HDC hdc;
RECT rect;
if (!GetWindowRect(hWnd, &rect) || (hWnd == NULL)) {
return FALSE;
}
ZeroMemory(&bi, sizeof(BITMAPINFO));
LONG lWidth = rect.right - rect.left;
LONG lHeight = rect.bottom - rect.top;
bi.bmiHeader.biSize = sizeof(BITMAPINFO);
bi.bmiHeader.biWidth = lWidth;
bi.bmiHeader.biHeight = -lHeight;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
hdc = GetDC(hWnd);
hBitmap = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, &ptrBitmapPixels, NULL, 0);
SelectObject(hdcMem, hBitmap);
*img = cv::Mat(lHeight, lWidth, CV_8UC4, ptrBitmapPixels, 0);
BitBlt(hdcMem, 0, 0, lWidth, lHeight, hdcSys, 0, 0, SRCCOPY);
//DeleteObject(hBitmap);
DeleteDC(hdcMem);
ReleaseDC(hWnd, hdcSys);
ReleaseDC(hWnd, hdc);
return TRUE;
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
/*...*/
HotKeyId = GlobalAddAtom(L"DBKGNDSCREENSHOT");
RegisterHotKey(hWnd, HotKeyId, NULL, VK_F10);
/*...*/
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
/*...*/
case WM_HOTKEY:
if (wParam == HotKeyId) {
cv::Mat t;
HWND MainHWND;
MainHWND = GetForegroundWindow();
ScreenShot(&t, MainHWND);
cv::imshow("1", t);
}
break;
/*...*/
}
and still black even PrintWindow(at least we got a titlebar)
PrintWindow(hWnd, hdcMem, 0);
//BitBlt(hdcMem, 0, 0, lWidth, lHeight, hdcSys, 0, 0, SRCCOPY);
I send this program to my friend (without any modify, his OS=win7 x64), but he got the correct result.
so what should I do?

GDI is a very old technology and is slowly getting deprecated. The more reliable method to capture desktop on Windows 10 would be through Desktop Duplication API.

Related

How to erase the background of a window?

Please another way than using a layered window.
Setting SetLayeredWindowAttributes caused some issues in my GUI, for example, some controls inherit the background color as they contains transparency, same with pictures containing transparency, also there are a lot of other controls loaded on it which makes it hard to work with a layered window.
My goal is to create a GUI with rounded borders, for that, I will load a picture to behave as the background.
SetWindowRgn would not help, as it doesn't produce good edges borders because the pictures are being drawn with rounded corners and anti-aliasing.
Result usingSetLayeredWindowAttributes and SetWindowRgn:
I have tried to set WM_ERASEBKGND to true and inside of WM_PAINT use BitBlt with the rasters SRCCOPY | CAPTUREBLT painting an empty bitmap into the window DC, but the window still contains a background.
Also tried to just paint the image above, but the empty area is painted with the default background color.
The image used: https://i.imgur.com/TTLHoCf.png
I have created a similar ask-for-help topic in the Microsoft forum, the code below was adapted from an answer given by the user Castorix:
#include <windows.h>
#include <tchar.h>
// Gdiplus
#pragma comment( lib, "gdiplus.lib" )
#pragma comment( lib, "Msimg32.lib" )
#include <gdiplus.h>
#include <wingdi.h>
//#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int nWidth = 601, nHeight = 301;
#define IDC_BUTTON 11
HBITMAP hBitmap = NULL;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
hInst = hInstance;
WNDCLASSEX wcex =
{
sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInst, LoadIcon(NULL, IDI_APPLICATION),
LoadCursor(NULL, IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1), NULL, TEXT("WindowClass"), NULL,
};
if (!RegisterClassEx(&wcex))
return MessageBox(NULL, TEXT("Cannot register class !"), TEXT("Error"), MB_ICONERROR | MB_OK);
int nX = (GetSystemMetrics(SM_CXSCREEN) - nWidth) / 2, nY = (GetSystemMetrics(SM_CYSCREEN) - nHeight) / 2;
HWND hWnd = CreateWindowEx(0, wcex.lpszClassName, TEXT("Test"), WS_POPUP, nX, nY, nWidth, nHeight, NULL, NULL, hInst, NULL);
if (!hWnd)
return MessageBox(NULL, TEXT("Cannot create window !"), TEXT("Error"), MB_ICONERROR | MB_OK);
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
//WndProc(hWnd, 15, 0, 0);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND hWndButton = NULL, hWndStatic = NULL;
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hDC;
BLENDFUNCTION bf{};
int x = 0, y = 0, dx = 0, dy = 0;
switch (message)
{
case WM_CREATE:
{
hWndButton = CreateWindowEx(0, L"Button", L"Click", WS_CHILD | WS_VISIBLE | BS_PUSHLIKE, 50, 60, 60, 32, hWnd, (HMENU)IDC_BUTTON, hInst, NULL);
// https://i.imgur.com/TTLHoCf.png
// Start Gdiplus
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
// Load the image
Gdiplus::Color Color{ 255, 255, 255 };
hBitmap = NULL;
Gdiplus::Bitmap* bitmap = Gdiplus::Bitmap::FromFile(L"C:\\MEGAsync\\pic2.png", false);
if (bitmap)
{
bitmap->GetHBITMAP(Color, &hBitmap);
delete bitmap;
}
//hBitmap = (HBITMAP)LoadImage(NULL, L"C:\\MEGAsync\\pic.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
return 0;
}
break;
case WM_COMMAND:
{
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)
{
case IDC_BUTTON:
{
if (wmEvent == BN_CLICKED)
{
Beep(1000, 10);
}
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_ERASEBKGND:
return 0;
case WM_NCHITTEST:
return HTCAPTION;
case WM_NCRBUTTONDOWN:
{
Sleep(200);
DestroyWindow(hWnd);
}
break;
case WM_PAINT:
{
hDC = BeginPaint(hWnd, &ps);
OutputDebugString(L"WM_PAINT");
if (hBitmap)
{
BITMAP bm;
GetObject(hBitmap, sizeof(bm), &bm);
HDC hDCMem = CreateCompatibleDC(NULL);
HBITMAP hBitmapOld = (HBITMAP)SelectObject(hDCMem, hBitmap);
//SetBkMode(hDC, TRANSPARENT);
//SetBkMode(hDCMem, TRANSPARENT);
//TransparentBlt(hDC, 0, 0, bm.bmWidth, bm.bmHeight, hDCMem, 0, 0, bm.bmWidth, bm.bmHeight, RGB(192, 0, 192));
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.AlphaFormat = 1; // 0 - ignore source alpha, AC_SRC_ALPHA (1) - use source alpha
bf.SourceConstantAlpha = 10;
x = 0;
y = 0;
dx = nWidth;
dy = nHeight;
AlphaBlend(hDC, x, y, dx, dy, hDCMem, x, y, dx, dy, bf);
auto err = GetLastError();
//BitBlt(hDC, 0, 0, bm.bmWidth, bm.bmHeight, hDCMem, 0, 0, SRCCOPY | CAPTUREBLT);
//SelectObject(hDCMem, hBitmapOld);
DeleteDC(hDCMem);
}
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Working into the WM_PAINT message, I tried to draw the picture into the background using AlphaBlend, but it resulted in the window background being drawn with whatever be underneath it:
A layered window with the LWA_COLORKEY attribute is problematic if you want (Windows standard) child controls because there is no perfect color to pick as the transparent color.
However, layered windows have another mode; UpdateLayeredWindow. This function is perfect if you have an image (with alpha transparency) you want to use as the background. Just make sure the bitmap is pre-multiplied 32-bit ARGB before selecting it into the DC.
If for some crazy reason you can't use layered windows, the older option is SetWindowRgn.
The newer option is DirectComposition but I'm not sure if you are forced to set the layered style on the window.

PrintWindow function makes mistake in notepad.exe

PrintWindow function in Win32API could capture the image of a program. By the following code we can get the copy of the screenshot in clipboard.
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <iostream>
using namespace std;
WCHAR programName[] = L"Notepad";
int main()
{
HWND hwnd = FindWindow(programName, NULL);
if (hwnd == NULL)
{
cerr << "Cannot find window" << endl;
return -1;
}
WINDOWINFO wi;
wi.cbSize = sizeof(WINDOWINFO);
GetWindowInfo(hwnd, &wi);
RECT rc = {
wi.rcClient.left - wi.rcWindow.left,
wi.rcClient.top - wi.rcWindow.top,
wi.rcClient.right - wi.rcWindow.left,
wi.rcClient.bottom - wi.rcWindow.top
};
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen,
wi.rcWindow.right - wi.rcWindow.left,
wi.rcWindow.bottom - wi.rcWindow.top);
SelectObject(hdc, hbmp);
HBITMAP hbmp2 = CreateCompatibleBitmap(hdcScreen,
wi.rcClient.right - wi.rcClient.left,
wi.rcClient.bottom - wi.rcClient.top);
HDC hdc2 = CreateCompatibleDC(hdcScreen);
SelectObject(hdc2, hbmp2);
PrintWindow(hwnd, hdc, 0);
BitBlt(hdc2,
0, 0, rc.right - rc.left, rc.bottom - rc.top,
hdc,
rc.left, rc.top,
SRCCOPY);
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hbmp2);
CloseClipboard();
DeleteDC(hdc);
DeleteObject(hbmp);
DeleteDC(hdc2);
DeleteObject(hbmp2);
ReleaseDC(NULL, hdcScreen);
cout << "Success" << endl;
return 0;
}
When I use the code to capture a screenshot, it works but returns a wrong picture into my clipboard. Like this:
My screenshot of notepad.exe
It seems like part of the notepad program. But why it is just the part of the program. I tried other programs and they works well. Is there any bugs in notepad or win32api?
You only get the client area. This code gets the whole window:
HWND hwnd = FindWindow(programName, nullptr);
if (hwnd == nullptr)
return -1;
WINDOWINFO wi;
wi.cbSize = sizeof(WINDOWINFO);
GetWindowInfo(hwnd, &wi);
const LONG w = wi.rcWindow.right - wi.rcWindow.left;
const LONG h = wi.rcWindow.bottom - wi.rcWindow.top;
HDC hdcScreen = GetDC(nullptr);
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen, w, h);
SelectObject(hdc, hbmp);
PrintWindow(hwnd, hdc, 0); // or use PW_RENDERFULLCONTENT
BitBlt(hdc, 0, 0, w, h, hdc, 0, 0, SRCCOPY);
OpenClipboard(nullptr);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hbmp);
CloseClipboard();
DeleteDC(hdc);
DeleteObject(hbmp);
ReleaseDC(nullptr, hdcScreen);
return 0;

C++ Win32 loading multiple bitmaps in WM_CREATE won't load

I am trying to load some bitmaps when starting up the application. I am trying to load them in WM_CREATE but only the last bitmap i load will stay loaded.
I can load the pictures in WM_PAINT but i have been told it's better to load them in WM_CREATE.
//before switch statement
static HBITMAP bitmap1, bitmap2;
case WM_CREATE: {
HINSTANCE hInstance = GetModuleHandle(NULL);
bitmap1 = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(IDB_BITMAP1),
IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
bitmap2 = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(IDB_BITMAP2),
IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
if (!bitmap1 || !bitmap2) MessageBox(NULL, _T("Error while loading images"), _T("Error!"), MB_ICONEXCLAMATION | MB_OK);
case WM_PAINT:{
//Draw bitmap...
}
I expected that both bitmaps got loaded as they do when loaded inside WM_PAINT
EDIT:
The problem weren't were i thought. The problem is it draws behind the filled area i had made. I still don't know how to fix.
//Draw bitmap function
bool DrawBitmap(HBITMAP hBitmap, int posX, int posY, int sizeX, int sizeY)
{
BITMAP bmp;
HWND hWnd = FindWindow(windowClassName, NULL);
if (!hWnd) return false;
HDC hdc = GetDC(hWnd);
if (!hdc) return false;
HDC hBitmapDC = CreateCompatibleDC(hdc);
if (!hBitmapDC) return false;
GetObject(hBitmap, sizeof(bmp), &bmp);
SelectObject(hBitmapDC, hBitmap);
BitBlt(hdc, posX, posY, sizeX, sizeY, hBitmapDC, 0, 0, SRCCOPY);
DeleteObject(hBitmap);
ReleaseDC(hWnd, hBitmapDC);
ReleaseDC(hWnd, hdc);
return true;
}
//Before switch
RECT recRect;
STATIC HBITMAP bitmap1;
//case WM_CREATE:
case WM_CREATE: {
HINSTANCE hInstance = GetModuleHandle(NULL);
bitmap1 = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(IDB_BITMAP1),
IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
if (!bitmap) MessageBox(NULL, _T("Error while loading images"), _T("Error!"), MB_ICONEXCLAMATION | MB_OK);
return 0;
break;
//case WM_PAINT
case WM_PAINT:
DefWindowProc(hwnd, msg, wParam, lParam);
hdc = GetWindowDC(hwnd);
recRect = { -1, -1, 50 + 1, 30 + 1 };
FillRect(hdc, &recRect, (HBRUSH)CreateSolidBrush(RGB(30, 30, 30)));
if (DrawBitmap(bitmap1, 5, 5, 10, 10) == false) MessageBox(NULL, _T("Error while drawing images"), _T("Error!"), MB_ICONEXCLAMATION | MB_OK);
return DefWindowProc(hwnd, msg, wParam, lParam);
return 0;
break;
The problem does not occur when i declare the bitmap in WM_PAINT
GetWindowDC returns the dc for the whole window, including the non-client area. You usually need GetDC instead.
Always use BeginPaint/EndPaint when responding to WM_PAINT and return 0.
CreateSolidBrush is a GDI resource leak. After creating brush or other GDI objects, you have to destroy those objects, otherwise you have resource leak, the program will crash after 10,000 GDI leaks.
In your draw function you have DeleteObject(hBitmap) This will destroy the bitmap immediately. But you probably want to keep the bitmap handle and destroy it only after the program is finished.
Suggestion:
bool DrawBitmap(HDC hdc, HBITMAP hBitmap, int posX, int posY, int sizeX, int sizeY)
{
HDC memdc = CreateCompatibleDC(hdc);
HGDIOBJ oldbmp = SelectObject(memdc, hBitmap);
BitBlt(hdc, posX, posY, sizeX, sizeY, memdc, 0, 0, SRCCOPY);
SelectObject(memdc, oldbmp);
DeleteDC(memdc);
return true;
}
...
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HBRUSH hbrush = CreateSolidBrush(RGB(255, 0, 0));
FillRect(hdc, &ps.rcPaint, hbrush);
DeleteObject(hbrush);
BITMAP bm1;
GetObject(bitmap1, sizeof(bm1), &bm1);
DrawBitmap(hdc, bitmap1, 0, 0, bm1.bmWidth, bm1.bmHeight);
int x = bm1.bmWidth;
int y = bm1.bmHeight;
BITMAP bm2;
GetObject(bitmap2, sizeof(bm2), &bm2);
DrawBitmap(hdc, bitmap2, x, y, bm2.bmWidth, bm2.bmHeight);
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
DeleteObject(bitmap1);
DeleteObject(bitmap2);
PostQuitMessage(0);
return 0;

CreateBitmap image is not printing on window

I need to load a bmp image from file and load image data into buffer and then create new image from it. (I have a reason for doing this way). I wrote a sample program to mimic my project scenario.
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
static BITMAP bm;
static HBITMAP bmpSource = NULL;
static HBITMAP hbmp=NULL;
static HDC hdcSource = NULL;
static HDC hdcDestination= NULL;
static PAINTSTRUCT ps;
BITMAPINFO bitmap_info[2] = {0};
if (Msg == WM_CREATE) {
bmpSource = (HBITMAP)LoadImage(NULL, L"bmp00004.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
GetObject(bmpSource, sizeof(BITMAP), &bm);
bitmap_info->bmiHeader.biSize = sizeof(bitmap_info->bmiHeader);
int ret = GetDIBits(hdcSource, bmpSource, 0, bm.bmHeight, NULL, bitmap_info,DIB_RGB_COLORS);
LPVOID *lpvBits = (LPVOID*) malloc(3*(bm.bmWidth * bm.bmHeight * sizeof(DWORD)));
int gotData = GetDIBits(hdcSource, bmpSource, 0,bm.bmHeight,lpvBits, bitmap_info, DIB_RGB_COLORS);
hbmp = (HBITMAP) CreateBitmap(bm.bmWidth, bm.bmHeight, bm.bmPlanes, bm.bmBitsPixel ,lpvBits);
hdcSource = CreateCompatibleDC(GetDC(0));
SelectObject(hdcSource, hbmp);
return 0;
}else if (Msg == WM_PAINT) {
hdcDestination = BeginPaint(hWnd, &ps);
BitBlt(hdcDestination, 0, 0,bm.bmWidth , bm.bmHeight, hdcSource, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
return 0;
}else if (Msg == WM_DESTROY) {
DeleteDC(hdcSource);
EndPaint(hWnd, &ps);
DeleteObject(bmpSource);
DeleteObject(hbmp);
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
I am expecting program to print the image on the window.
Now the newly created image is not displayed on window. All I can see is a gray box.
Your code can be considerably simplified. There is no need to call GetDIBits (which you are doing before you set up hdcSource anyway), and you should clean up properly and minimise the use of static variables. Something like the following (error checking omitted for clarity):
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
static HBITMAP bmpSource;
if (Msg == WM_CREATE) {
bmpSource = (HBITMAP)LoadImage(NULL, L"bmp00004.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
return 0;
}
if (Msg == WM_PAINT) {
PAINTSTRUCT ps;
HDC hdcDestination = BeginPaint(hWnd, &ps);
BITMAP bm;
GetObject(bmpSource, sizeof(bm), &bm);
HDC hdcSource = CreateCompatibleDC(hdcDestination);
HGDIOBJ hOldBmp = SelectObject(hdcSource, bmpSource);
BitBlt(hdcDestination, 0, 0,bm.bmWidth , bm.bmHeight, hdcSource, 0, 0, SRCCOPY);
SelectObject(hdcSource, hOldBmp);
DeleteDC(hdcSource);
EndPaint(hWnd, &ps);
return 0;
}
if (Msg == WM_DESTROY)
DeleteObject(bmpSource);
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
It seems you are attempting to read a bitmap file and write to another HBITMAP handle, for whatever reason...
LoadImage returns a compatible bitmap (unless you specify LR_CREATEDIBSECTION), therefore you need CreateCompatibleBitmap followed by SetDIBits to duplicate that source bitmap.
For some reason you have declared BITMAPINFO bminfo[2] But you only need one bminfo object. You need new or malloc to allocate enough memory for one object. Note that you cannot randomly swap arrays for pointers and vice-versa. If you used a debugger you would notice the functions failed and bminfo was not properly filled.
It's easier if you setup BITMAPINFOHEADER yourself, rather than let GetDIBits fill that parameter:
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
static HDC hdc_memory;
static HBITMAP hbmp_src;
static HBITMAP hbmp_dst;
static BITMAP bm;
if(Msg == WM_CREATE)
{
hbmp_src = (HBITMAP)LoadImage(NULL, L"file.bmp",
IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HDC hdc = GetDC(0);
hdc_memory = CreateCompatibleDC(hdc);
GetObject(hbmp_src, sizeof(bm), &bm);
int w = bm.bmWidth;
int h = bm.bmHeight;
if(h < 0) h = -h;
int size = ((w * bm.bmBitsPixel + 31) / 32) * 4 * h;
BITMAPINFOHEADER bi = { sizeof(bi), w, h, bm.bmPlanes, bm.bmBitsPixel };
BYTE* bits = new BYTE[size];
GetDIBits(hdc, hbmp_src, 0, h, bits, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
//change the pixel data in bits if that's your goal ...
hbmp_dst = CreateCompatibleBitmap(hdc, w, h);
SetDIBits(hdc, hbmp_dst, 0, h, bits, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
SelectObject(hdc_memory, hbmp_dst);
delete[] bits;
ReleaseDC(0, hdc);
return 0;
}
else if(Msg == WM_PAINT)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdc_memory, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
return 0;
}
else if(Msg == WM_DESTROY)
{
DeleteDC(hdc_memory);
DeleteObject(hbmp_src);
DeleteObject(hbmp_dst);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}

C++ Stop displaying bitmap

How can I stop displaying the bitmap in my Win32 Project. I have a method that I'm calling in WM_PAINT called LoadBitmap. It's code looks like this:
bool LoadBitmap(LPTSTR szfilename, HDC winhdc, int x, int y) {
HBITMAP bitmap;
bitmap = (HBITMAP)LoadImage(NULL, szfilename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (bitmap == NULL) {
::MessageBox(NULL, _T("Bitmap failed"), NULL, MB_OK);
return false;
}
HDC hdc;
hdc = ::CreateCompatibleDC(winhdc);
if (hdc == NULL)
{
::MessageBox(NULL, _T("HDC FAILED"), NULL, MB_OK);
return false;
}
BITMAP qbitmap;
int ireturn = GetObject(reinterpret_cast<HGDIOBJ>(bitmap), sizeof(BITMAP), reinterpret_cast<LPVOID>(&qbitmap));
if (!ireturn) {
::MessageBox(NULL, _T("RETURN FAILED"), NULL, MB_OK);
return false;
}
HBITMAP holdbitmap = (HBITMAP)::SelectObject(hdc, bitmap);
if (holdbitmap == NULL) {
::MessageBox(NULL, _T("HOLD FAILED"), NULL, MB_OK);
return false;
}
BOOL qRetBlit = ::BitBlt(winhdc, x, y, qbitmap.bmWidth, qbitmap.bmHeight, hdc, 0, 0, SRCCOPY);
if (!qRetBlit)
{
::MessageBox(NULL, _T("BLIT FAILED"), NULL, MB_OK);
return false;
}
::SelectObject(hdc, holdbitmap);
::DeleteDC(hdc);
::DeleteObject(bitmap);
return true;
}
NOTE x and y change continuously.
When x and y change however, the previous instance of them stays behind.
How can I stop displaying a bitmap once it has been painted in a new position?
If there is background brush then the old bitmap is erased with each paint call. If there is no background then erase it manually for example with FillRect Here is example with double buffering, assumes there is no background brush.
case WM_PAINT:
{
...
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP membitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
HGDIOBJ oldbitmap = SelectObject(memdc, membitmap);
//double-buffer ready, do custom paintings here:
FillRect(memdc, &rc, GetSysColorBrush(COLOR_3DFACE));
LoadBitmap(filename, memdc, x, y);
//BitBlt to hdc
BitBlt(hdc, 0, 0, rc.right, rc.bottom, memdc, 0, 0, SRCCOPY);
//cleanup:
SelectObject(hdc, oldbitmap);
DeleteObject(membitmap);
DeleteDC(memdc);
EndPaint(hwnd, &ps);
return 0;
}
Edit *************
It will be faster to load the bitmap file only once. For each paint request, we need only to draw the bitmap, instead of LoadImage every time + draw.
You can declare HBITMAP handles as static values in window's procedure. Set it up once in WM_CREATE, and clean it up in WM_NCDESTROY. Now we can use them anywhere in side window procedure:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
static HBITMAP hbitmap_background = NULL;
static HBITMAP hbitmap_sprite = NULL;
switch (msg)
{
case WM_CREATE:
{
hbitmap_background = (HBITMAP)LoadImage(NULL,
L"background.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (!hbitmap_background)
OutputDebugStringW(L"!hbitmap_background\n");
hbitmap_sprite = (HBITMAP)LoadImage(NULL,
L"sprite.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (!hbitmap_sprite)
OutputDebugStringW(L"!hbitmap_sprite\n");
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
//setup double-buffering:
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP membitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
HGDIOBJ oldbitmap = SelectObject(memdc, membitmap);
//double-buffer ready, do custom paintings here:
FillRect(memdc, &rc, GetSysColorBrush(COLOR_3DFACE));
DrawBitmap(hbitmap_background, memdc, 0, 0);
DrawBitmap(hbitmap_sprite, memdc, 0, 0);
//BitBlt to hdc
BitBlt(hdc, 0, 0, rc.right, rc.bottom, memdc, 0, 0, SRCCOPY);
//cleanup:
SelectObject(hdc, oldbitmap);
DeleteObject(membitmap);
DeleteDC(memdc);
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_NCDESTROY:
{
//cleapup bitmap handles
if (hbitmap_background)
DeleteObject(hbitmap_background);
if (hbitmap_sprite)
DeleteObject(hbitmap_sprite);
}
}
return DefWindowProc(hwnd, msg, wp, lp);
}
We can change DrawBitmap so it only needs HBITMAP handle
void DrawBitmap(HBITMAP hbitmap, HDC hdc, int x, int y)
{
if (!hbitmap)
{
OutputDebugStringW(L"error\n");
return;
}
BITMAP bm;
GetObject(hbitmap, sizeof(BITMAP), &bm);
HDC memdc = CreateCompatibleDC(hdc);
HGDIOBJ oldbitmap = SelectObject(memdc, hbitmap);
BitBlt(hdc, x, y, bm.bmWidth, bm.bmHeight, memdc, 0, 0, SRCCOPY);
SelectObject(memdc, oldbitmap);
DeleteDC(memdc);
}
Note, in this example I assume background.bmp is very large, and sprite.bmp is very small. If it is painted the other way around then background will hide the sprite.