I'm need to draw border on top another application's window (the main purpose is to highlight window user chooses from running applications list). I'm trying to draw border on top of native window border, but the border isn't drawn. Here is the code:
HPEN framePen = ::CreatePen(PS_SOLID, 5, RGB(255, 0, 0));
HWND handle = FindWindow(L"ConsoleWindowClass", L"C:\\WINDOWS\\system32\\cmd.exe");
WINDOWPLACEMENT winPlacement;
GetWindowPlacement(handle, &winPlacement);
if (winPlacement.showCmd == SW_SHOWMINIMIZED)
{
ShowWindow(handle, SW_RESTORE);
}
SetWindowPos(handle, HWND_TOP, 0, 0, 0, 0, SWP_ASYNCWINDOWPOS | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
SetForegroundWindow(handle);
PAINTSTRUCT ps;
RECT rect = {};
::GetClientRect(handle, &rect);
HDC hdc = ::BeginPaint(handle, &ps);
::SelectObject(hdc, framePen);
::Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
::EndPaint(handle, &ps);
In this example used handle of cmd window, but in fact it doesn't matter.
Could you please tell why border isn't drawn and how to draw it?
Thanks.
You can't draw directly on another window, because the system may refresh the window at any time, overwriting your drawing.
To make your drawing persistent, create a layered window, positioned on top of the other window.
Create the window with WS_EX_LAYERED flag.
Pass a color key to SetLayeredWindowAttributes().
In your WM_PAINT handler, draw the inside of the rectangle with the color key (by using it for the brush). Everything you draw with the color key will become transparent. Draw the border of the rectangle with the desired color (by using it for the pen).
Here is a minimal example to get you started. The frame can be moved around by drag-n-drop.
Note there is no error handling to keep the sample code concise. You should check the return value of each Windows API call.
#include <windows.h>
const COLORREF MY_COLOR_KEY = RGB( 255, 0, 255 );
int APIENTRY wWinMain(
HINSTANCE hInstance, HINSTANCE /*hPrevInst*/, LPWSTR /*lpCmdLine*/, int nCmdShow )
{
WNDCLASSW wc{ sizeof( wc ) };
wc.hInstance = hInstance;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hCursor = LoadCursor( nullptr, IDC_ARROW );
wc.hbrBackground = reinterpret_cast<HBRUSH>( COLOR_BTNFACE + 1 );
wc.lpszClassName = L"MyTransparentFrame";
wc.lpfnWndProc = []( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) -> LRESULT
{
switch( msg )
{
case WM_PAINT:
{
PAINTSTRUCT ps{};
HDC hdc = BeginPaint( hwnd, &ps );
RECT rc{}; GetClientRect( hwnd, &rc );
HPEN hPen = CreatePen( PS_SOLID, 20, GetSysColor( COLOR_HIGHLIGHT ) );
HBRUSH hBrush = CreateSolidBrush( MY_COLOR_KEY );
HGDIOBJ hOldPen = SelectObject( hdc, hPen );
HGDIOBJ hOldBrush = SelectObject( hdc, hBrush );
Rectangle( hdc, rc.left, rc.top, rc.right, rc.bottom );
if( hOldPen )
SelectObject( hdc, hOldPen );
if( hOldBrush )
SelectObject( hdc, hOldBrush );
if( hPen )
DeleteObject( hPen );
if( hBrush )
DeleteObject( hBrush );
EndPaint( hwnd, &ps );
}
break;
case WM_DESTROY:
PostQuitMessage( 0 );
break;
case WM_NCHITTEST:
return HTCAPTION; // to be able to drag the window around
break;
default:
return DefWindowProcW( hwnd, msg, wp, lp );
}
return 0;
};
RegisterClassW( &wc );
HWND hwnd = CreateWindowExW( WS_EX_LAYERED, wc.lpszClassName, L"", WS_POPUP,
200, 200, 800, 600, nullptr, nullptr, hInstance, nullptr );
SetLayeredWindowAttributes( hwnd, MY_COLOR_KEY, 255, LWA_COLORKEY );
ShowWindow( hwnd, nCmdShow );
MSG msg;
while( GetMessage( &msg, nullptr, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return (int) msg.wParam;
}
Finally I managed to solve the problem with the following code:
const COLORREF MY_COLOR_KEY = RGB(255, 128, 0);
HWND cmdHanlde = NULL;
constexpr unsigned int timerIdWindowUpdate = 1;
constexpr unsigned int timerIdFrameColor = 2;
bool tick = false;
bool minimized = false;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
WNDCLASSEX wc = {};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"MyTransparentFrame";
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpfnWndProc = [](HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) -> LRESULT
{
switch (msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps{};
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc{}; GetClientRect(hwnd, &rc);
HPEN hPen = CreatePen(PS_SOLID, 5, tick ? RGB(255, 128, 1) : RGB(255, 201, 14));
HBRUSH hBrush = CreateSolidBrush(MY_COLOR_KEY);
HGDIOBJ hOldPen = SelectObject(hdc, hPen);
HGDIOBJ hOldBrush = SelectObject(hdc, hBrush);
Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
if (hOldPen)
SelectObject(hdc, hOldPen);
if (hOldBrush)
SelectObject(hdc, hOldBrush);
if (hPen)
DeleteObject(hPen);
if (hBrush)
DeleteObject(hBrush);
EndPaint(hwnd, &ps);
}
break;
case WM_TIMER:
{
if (wp == timerIdWindowUpdate)
{
WINDOWPLACEMENT windowPlacement = { sizeof(WINDOWPLACEMENT), };
if (::GetWindowPlacement(cmdHanlde, &windowPlacement))
{
if (windowPlacement.showCmd == SW_SHOWMINIMIZED
|| !IsWindowVisible(cmdHanlde))
{
ShowWindow(hwnd, SW_HIDE);
minimized = true;
}
else
{
RECT rect = {};
DwmGetWindowAttribute(cmdHanlde, DWMWA_EXTENDED_FRAME_BOUNDS, &rect, sizeof(rect));
MONITORINFO monInfo;
monInfo.cbSize = sizeof(MONITORINFO);
GetMonitorInfoW(MonitorFromWindow(cmdHanlde, MONITOR_DEFAULTTONEAREST), &monInfo);
if (cmdHanlde != NULL && ::IsZoomed(cmdHanlde))
{
rect.left = monInfo.rcWork.left;
rect.top = monInfo.rcWork.top;
rect.bottom = monInfo.rcWork.bottom > rect.bottom ? rect.bottom : monInfo.rcWork.bottom;
rect.right = monInfo.rcWork.right > rect.right ? rect.right : monInfo.rcWork.right;
}
if (minimized)
{
::SetWindowPos(hwnd, cmdHanlde, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
minimized = false;
}
else
{
::SetWindowPos(cmdHanlde, hwnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
::SetWindowPos(hwnd, 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
SWP_SHOWWINDOW);
}
}
}
}
else if (wp == timerIdFrameColor)
{
tick = !tick;
::RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE);
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, msg, wp, lp);
}
return 0;
};
RegisterClassEx(&wc);
HWND hwnd = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_LAYERED | WS_EX_TRANSPARENT, wc.lpszClassName, L"", WS_POPUP | WS_VISIBLE | WS_DISABLED,
0, 0, 0, 0, nullptr, nullptr, nullptr, nullptr);
::SetTimer(hwnd, timerIdWindowUpdate, 50, NULL);
::SetTimer(hwnd, timerIdFrameColor, 500, NULL);
SetLayeredWindowAttributes(hwnd, MY_COLOR_KEY, 255, LWA_COLORKEY);
ShowWindow(hwnd, SW_SHOW);
cmdHanlde = FindWindow(L"ConsoleWindowClass", L"C:\\WINDOWS\\system32\\cmd.exe");
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
May be it is not the best solution, but it works for me. Could you please take a look on it and tell if there something to improve?
Related
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.
I found this code in stackoverflow and it really draws a rectangle on a transparent window using a bitmap. But somehow I cannot change the transparency of the rectangle. More precisely, I can, but it gets darker. As if the bitmap itself has a black background. How do I make the rectangle transparent?
void paintRect(HDC hdc, RECT dim, COLORREF penCol, COLORREF brushCol, int opacity)
{
HDC tempHDC = CreateCompatibleDC(hdc);
BITMAPINFO bitmapInfo;
ZeroMemory(&bitmapInfo, sizeof(BITMAPINFO));
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = dim.right - dim.left;
bitmapInfo.bmiHeader.biHeight = dim.bottom - dim.top;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = (dim.right - dim.left) * (dim.bottom - dim.top) * 4;
HBITMAP hBitmap = CreateDIBSection(tempHDC, &bitmapInfo, DIB_RGB_COLORS, NULL, NULL, 0x0);
SelectObject(tempHDC, hBitmap);
SetDCPenColor(tempHDC, RGB(0, 0, 255));
SetDCBrushColor(tempHDC, RGB(0, 0, 255));
FillRect(tempHDC, &dim, CreateSolidBrush(RGB(0, 0, 255)));
BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, 0 };
AlphaBlend(hdc, dim.left, dim.top, dim.right, dim.bottom, tempHDC, dim.left, dim.top, dim.right, dim.bottom, blend);
}
Full code
#include <Windows.h>
int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow);
LRESULT CALLBACK windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
WNDCLASS windowClass {};
windowClass.lpfnWndProc = windowProc;
windowClass.hInstance = hInstance;
windowClass.lpszClassName = L"Keystrokes";
windowClass.style = CS_NOCLOSE;
windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
if (!RegisterClass(&windowClass))
{
return 0;
}
HWND hwnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, L"Keystrokes", L"Keystrokes", WS_POPUP, 0, 0, 148, 140, 0, 0, hInstance, 0);
if (!hwnd)
{
return 0;
}
SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LWA_COLORKEY);
ShowWindow(hwnd, nCmdShow);
MSG msg {};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
void paintRect(HDC hdc, RECT dim, COLORREF penCol, COLORREF brushCol, int opacity)
{
HDC tempHDC = CreateCompatibleDC(hdc);
BITMAPINFO bitmapInfo;
ZeroMemory(&bitmapInfo, sizeof(BITMAPINFO));
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = dim.right - dim.left;
bitmapInfo.bmiHeader.biHeight = dim.bottom - dim.top;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = (dim.right - dim.left) * (dim.bottom - dim.top) * 4;
HBITMAP hBitmap = CreateDIBSection(tempHDC, &bitmapInfo, DIB_RGB_COLORS, NULL, NULL, 0x0);
SelectObject(tempHDC, hBitmap);
SetDCPenColor(tempHDC, RGB(0, 0, 255));
SetDCBrushColor(tempHDC, RGB(0, 0, 255));
FillRect(tempHDC, &dim, CreateSolidBrush(RGB(0, 0, 255)));
BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, 0 };
AlphaBlend(hdc, dim.left, dim.top, dim.right, dim.bottom, tempHDC, dim.left, dim.top, dim.right, dim.bottom, blend);
}
LRESULT CALLBACK windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_LBUTTONDOWN:
{
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
}
break;
case WM_MBUTTONDOWN:
{
PostQuitMessage(0);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hwnd, &ps);
paintRect(hDC, { 0, 0, 48, 48 }, RGB(255, 0, 0), RGB(255, 255, 255), 255);
EndPaint(hwnd, &ps);
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
“As if the bitmap itself has a black background.”
Because your background is set to:
windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
Of course you can set it to NULL_BRUSH to make it look transparent:
windowClass.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
But when you move it, you will find that this is not to make the window transparent, but to stop drawing the background:
As #Ben answers here:
You haven't made the window transparent, you have just stopped drawing the background. What you see as the background is just whatever happened to be underneath the window when it was first drawn.
And you need to refer to Layered Windows
You can't add rectangles directly on the window, you should use a layered window, and then add what you need.
Here is a sample that you can refer to it:
#include <Windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR szCmdLine, _In_ int iCmdShow)
{
static TCHAR szAppName[] = TEXT("test window");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
}
hwnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, szAppName,
TEXT("the hello program"),
WS_POPUP,
CW_USEDEFAULT,
CW_USEDEFAULT,
148,
148,
NULL,
NULL,
hInstance,
NULL);
SetLayeredWindowAttributes(hwnd, 0, 1, LWA_ALPHA);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessageW(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return msg.wParam;
}
VOID FadeRect(RECT* prc, HDC hdc)
{
BOOL fFade = FALSE;
static HWND hwnd;
SIZE size;
POINT ptSrc = { 0, 0 };
BLENDFUNCTION blend;
SystemParametersInfo(SPI_GETSELECTIONFADE, 0, &fFade, 0);
if (!fFade)
return;
if (!hwnd) hwnd = CreateWindowEx(WS_EX_LAYERED |
WS_EX_TRANSPARENT |
WS_EX_TOPMOST | WS_EX_TOOLWINDOW,
L"static", L"static", WS_POPUP | WS_VISIBLE, prc->left,
prc->top, prc->right, prc->bottom, NULL, (HMENU)0, NULL, NULL);
else MoveWindow(hwnd, prc->left, prc->top, prc->right, prc->bottom, TRUE);
RECT rect{ prc->left,prc->top,prc->right,prc->bottom };
size.cx = prc->right - prc->left;
size.cy = prc->bottom - prc->top;
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.AlphaFormat = 0;
blend.SourceConstantAlpha = 150;
UpdateLayeredWindow(hwnd, NULL, NULL, &size, hdc, &ptSrc, 0,
&blend, ULW_ALPHA);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN:
{
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
case WM_MBUTTONDOWN:
{
PostQuitMessage(0);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
RECT rect;
GetClientRect(hwnd, &rect);
MapWindowPoints(hwnd, GetParent(hwnd), (LPPOINT)&rect, 2);
HDC hDC = BeginPaint(hwnd, &ps);
RECT rc{ rect.left,rect.top,rect.left + 48,rect.top + 48 };
FadeRect(&rc, hDC);
EndPaint(hwnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
I needed to display flashing border in on top of chosen application's window (in my example the application is cmd.exe). I used layered windows for this purpose. Everything works fine, except the one thing: I can't bring target window (in my case - cmd.exe) in front if it is overlapped by another window (if another window is in the foreground). It works when I maximize/minimize target window, but doesn't work in case when target windows is overlapped. I can't restore in clicking on app's icon in the taskbar.
const COLORREF MY_COLOR_KEY = RGB(255, 128, 0);
HWND cmdHanlde = NULL;
constexpr unsigned int timerIdWindowUpdate = 1;
constexpr unsigned int timerIdFrameColor = 2;
bool tick = false;
bool minimized = false;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
WNDCLASSEX wc = {};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"MyTransparentFrame";
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpfnWndProc = [](HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) -> LRESULT
{
switch (msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps{};
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc{}; GetClientRect(hwnd, &rc);
HPEN hPen = CreatePen(PS_SOLID, 5, tick ? RGB(255, 128, 1) : RGB(255, 201, 14));
HBRUSH hBrush = CreateSolidBrush(MY_COLOR_KEY);
HGDIOBJ hOldPen = SelectObject(hdc, hPen);
HGDIOBJ hOldBrush = SelectObject(hdc, hBrush);
Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
if (hOldPen)
SelectObject(hdc, hOldPen);
if (hOldBrush)
SelectObject(hdc, hOldBrush);
if (hPen)
DeleteObject(hPen);
if (hBrush)
DeleteObject(hBrush);
EndPaint(hwnd, &ps);
}
break;
case WM_TIMER:
{
if (wp == timerIdWindowUpdate)
{
WINDOWPLACEMENT windowPlacement = { sizeof(WINDOWPLACEMENT), };
if (::GetWindowPlacement(cmdHanlde, &windowPlacement))
{
if (windowPlacement.showCmd == SW_SHOWMINIMIZED
|| !IsWindowVisible(cmdHanlde))
{
minimized = true;
}
else
{
RECT rect = {};
DwmGetWindowAttribute(cmdHanlde, DWMWA_EXTENDED_FRAME_BOUNDS, &rect, sizeof(rect));
MONITORINFO monInfo;
monInfo.cbSize = sizeof(MONITORINFO);
GetMonitorInfoW(MonitorFromWindow(cmdHanlde, MONITOR_DEFAULTTONEAREST), &monInfo);
if (cmdHanlde != NULL && ::IsZoomed(cmdHanlde))
{
rect.left = monInfo.rcWork.left;
rect.top = monInfo.rcWork.top;
rect.bottom = monInfo.rcWork.bottom > rect.bottom ? rect.bottom : monInfo.rcWork.bottom;
rect.right = monInfo.rcWork.right > rect.right ? rect.right : monInfo.rcWork.right;
}
if (minimized)
{
::SetWindowPos(hwnd, cmdHanlde, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
minimized = false;
}
else
{
::SetWindowPos(cmdHanlde, hwnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
::SetWindowPos(hwnd, 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
SWP_SHOWWINDOW);
}
}
}
}
else if (wp == timerIdFrameColor)
{
tick = !tick;
::RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE);
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, msg, wp, lp);
}
return 0;
};
RegisterClassEx(&wc);
HWND hwnd = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_LAYERED | WS_EX_TRANSPARENT, wc.lpszClassName, L"", WS_POPUP | WS_VISIBLE | WS_DISABLED,
0, 0, 0, 0, nullptr, nullptr, nullptr, nullptr);
::SetTimer(hwnd, timerIdWindowUpdate, 50, NULL);
::SetTimer(hwnd, timerIdFrameColor, 500, NULL);
SetLayeredWindowAttributes(hwnd, MY_COLOR_KEY, 255, LWA_COLORKEY);
ShowWindow(hwnd, SW_SHOW);
cmdHanlde = FindWindow(L"ConsoleWindowClass", L"C:\\WINDOWS\\system32\\cmd.exe");
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
So the question here is how to restore target window after it was overlapped by another window?
if (minimized) {
ShowWindow(hwnd, SW_HIDE);
minimized = false;
} else {
SetWindowPos(hwnd, 0, rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top, SWP_NOZORDER);
SetWindowPos(hwnd, GetNextWindow(cmdHandle, GW_HWNDPREV), 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
}
I have the "simple" goal of drawing a bitmap with some transparency around it on the screen. That bit wasn't so hard:
#include <windows.h>
#include "BBKG.h"
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static int wH = 156;
static int wW = 166;
HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent)
{
HDC mem0, mem1;
HBITMAP hbmMask;
BITMAP bm;
GetObject(hbmColour, sizeof(BITMAP), &bm);
hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);
mem0 = CreateCompatibleDC(0);
mem1 = CreateCompatibleDC(0);
SelectObject(mem0, hbmColour);
SelectObject(mem1, hbmMask);
SetBkColor(mem0, crTransparent);
BitBlt(mem1, 0, 0, bm.bmWidth, bm.bmHeight, mem0, 0, 0, SRCCOPY);
BitBlt(mem0, 0, 0, bm.bmWidth, bm.bmHeight, mem1, 0, 0, SRCINVERT);
DeleteDC(mem0);
DeleteDC(mem1);
return hbmMask;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
hInst = hInstance;
MSG msg;
HWND hwnd;
WNDCLASSW wc;
wc.style = 0;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.lpszClassName = L"nope";
wc.hInstance = hInst;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassW(&wc);
hwnd = CreateWindowW(wc.lpszClassName, L"",
WS_VISIBLE | WS_POPUP| WS_EX_TRANSPARENT,
100, 100, wW, wH, NULL, NULL, hInst, NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) {
//Workaround for focusables stealing my Esc key
if (msg.message == WM_KEYDOWN){
if (msg.wParam == VK_ESCAPE) {
SendMessage(hwnd, WM_CLOSE, 0, 0);
}
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
static int px;
static int py;
static HBITMAP bhbm;
static RECT nRect = { 0, 0, wW, wH };
switch (msg)
{
case WM_CREATE:
{
HWND bb = CreateWindowW(L"STATIC", L"",
WS_VISIBLE | WS_CHILD ,
0, 0, wW, wH,
hwnd, (HMENU)11, hInst, NULL);
//SetTimer(hwnd, 1, 80, NULL);
return 0;
}
case WM_PAINT: {
//Vars
RECT wRect;
if (GetUpdateRect(hwnd, &wRect, FALSE) == 0) {
return 0; //Nothing to paint
}
PAINTSTRUCT gps;
PAINTSTRUCT ps;
BeginPaint(hwnd, &gps);
HWND bb = GetDlgItem(hwnd, 11);
HDC bbhdc = BeginPaint(bb, &ps);
HDC mdc = CreateCompatibleDC(bbhdc);
//Load Image
BITMAP pBM;
HBITMAP pHBM = (HBITMAP)LoadImage(NULL, L"twi00.bmp", 0, 0, 0, LR_LOADFROMFILE);
HBITMAP pMBM = CreateBitmapMask((HBITMAP)pHBM, 0x00000000);
GetObject(pHBM, sizeof(pBM), &pBM);
//Paint
HBITMAP oldBM = (HBITMAP)SelectObject(mdc, pMBM);
BitBlt(bbhdc, 0, 0, pBM.bmWidth, pBM.bmHeight, mdc, 0, 0, SRCAND);
SelectObject(mdc, pHBM);
BitBlt(bbhdc, 0, 0, pBM.bmWidth, pBM.bmHeight, mdc, 0, 0, SRCPAINT);
//Cleanup
SelectObject(mdc, oldBM);
DeleteObject(pHBM);
DeleteObject(pMBM);
DeleteDC(mdc);
EndPaint(bb, &ps);
EndPaint(hwnd, &gps);
return 1;
}
case WM_ERASEBKGND: {
return 0;
}
case WM_DESTROY:
{
DeleteObject(bhbm);
PostQuitMessage(0);
return 0;
}
case WM_LBUTTONDOWN:
SetCapture(hwnd);
px = LOWORD(lParam);
py = HIWORD(lParam);
return 1;
case WM_LBUTTONUP:
{
ReleaseCapture();
return 1;
}
case WM_MOUSEMOVE:
{
if (GetCapture() == hwnd)
{
RECT rcWindow;
GetWindowRect(hwnd, &rcWindow);
SetWindowPos(hwnd, NULL, rcWindow.left + LOWORD(lParam) - px, rcWindow.top + HIWORD(lParam) - py, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
break;
}
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
Use any generic bmp with a black border will do, I used this:
Now the question is, how can I make it so that when I move the window (click/drag) the background updates? I was hoping for something like putting the bitmap into a transparent window so that it's overlayed on top of things but it seems to just grab the pixels of what ever is behind it.
I'm attempting to do this without GDI+ or other libraries, if possible.
CreateWindow() does not accept extended window styles, such as WS_EX_TRANSPARENT (which is why it has EX in its name). You have to use CreateWindowEx() instead:
hwnd = CreateWindowExW(WS_EX_TRANSPARENT,
wc.lpszClassName, L"",
WS_VISIBLE | WS_POPUP,
100, 100, wW, wH, NULL, NULL, hInst, NULL);
A better option is to create a layered window (see also this) by using the WS_EX_LAYERED extended style). Then you can use the UpdateLayeredWindow() function to provide the window with the bitmap and the transparent color (you can also specify alpha as well). Let the window manage all of the hard work of drawing the bitmap transparently for you.
Your WndProc() can also respond to the WM_NCHITTEST message to tell the OS that all clicks on the window should be treated as if the user were clicking on the window's titlebar. Let the window handle the mouse tracking and auto-positioning for you.
Try something more like this:
#include <windows.h>
HINSTANCE hInst;
static int wH = 156;
static int wW = 166;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
hInst = hInstance;
WNDCLASSW wc = {0};
wc.lpszClassName = L"nope";
wc.hInstance = hInst;
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassW(&wc);
HWND hwnd = CreateWindowEx(WS_EX_LAYERED,
wc.lpszClassName, L"",
WS_POPUP, 100, 100, wW, wH, NULL, NULL,
hInst, NULL);
HBITMAP hBmp = (HBITMAP) LoadImage(NULL, L"twi00.bmp", 0, 0, 0, LR_LOADFROMFILE);
HDC hdcScreen = GetDC(0);
HDC hdcBmp = CreateCompatibleDC(hdcScreen);
HBITMAP oldBM = (HBITMAP) SelectObject(hdcBmp, hBmp);
POINT pt = {0};
UpdateLayeredWindow(hwnd,
hdcScreen,
NULL, NULL,
hdcBmp, &pt,
RGB(0, 0, 0), // black
NULL, ULW_COLORKEY
);
SelectObject(hdcBmp, oldBM);
DeleteDC(hdcBmp);
ReleaseDC(0, hdcScreen);
DeleteObject(hBmp);
ShowWindow(hwnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
//Workaround for focusables stealing my Esc key
if ((msg.message == WM_KEYDOWN) && (msg.wParam == VK_ESCAPE) {
SendMessage(hwnd, WM_CLOSE, 0, 0);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_NCHITTEST:
{
return HTCAPTION;
}
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
I'm studying sprite drawing with GDI in Visual Studio 2013 with C++.
I can draw sprite using two bmp files, image with black background, and black mask. But after program start, sprite shown some time (about 15 second) and disappear. I can't find the reason.
Here is my code.
#include <Windows.h>
#include "resource.h"
using namespace std;
LRESULT WINAPI WinProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
HWND hWnd = 0;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
ZeroMemory(&msg, sizeof(msg));
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
wc.hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_HAND));
wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ASTERISK));
wc.hInstance = hInst;
wc.lpfnWndProc = WinProc;
wc.lpszClassName = "ClassName";
wc.lpszMenuName = 0;
wc.style = CS_VREDRAW | CS_HREDRAW;
if (!RegisterClass(&wc))
MessageBox(0, "Class was not registered!", "Error", 0);
hWnd=CreateWindow("ClassName", "WindowName", WS_OVERLAPPEDWINDOW, 100, 100, 640, 480, 0, 0, hInst, 0);
if (hWnd==0)
MessageBox(0, "Window was not created!", "Error", 0);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while (msg.message!=WM_QUIT)
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
HDC hdc = GetDC(hWnd);
HDC hBuffDC = CreateCompatibleDC(hdc);
HDC hMemDC = CreateCompatibleDC(hdc);
HBITMAP hBuffBitmap = CreateCompatibleBitmap(hdc, 640, 480);
HBITMAP hSpriteBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_MASK));
SelectObject(hBuffDC, hBuffBitmap);
SelectObject(hBuffDC, (HBRUSH)GetStockObject(GRAY_BRUSH));
Rectangle(hBuffDC, 0, 0, 640, 480);
SelectObject(hMemDC, hSpriteBitmap);
BitBlt(hBuffDC, 0, 0, 640, 480, hMemDC, 0, 0, SRCAND);
hSpriteBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_SPRITE));
SelectObject(hMemDC, hSpriteBitmap);
BitBlt(hBuffDC, 0, 0, 640, 480, hMemDC, 0, 0, SRCPAINT);
BitBlt(hdc, 0, 0, 640, 480, hBuffDC, 0, 0, SRCCOPY);
DeleteObject(hBuffBitmap);
DeleteObject(hSpriteBitmap);
DeleteDC(hMemDC);
DeleteDC(hBuffDC);
ReleaseDC(hWnd, hdc);
}
return 0;
}
LRESULT WINAPI WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_KEYDOWN:
switch (wParam)
{
case VK_ESCAPE:
DestroyWindow(hWnd);
break;
}
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
images are just a black circle with white background (mask), and green circle with black background.
//Here is corrected code without memory leak and ReleaseDc return 1
#include <Windows.h>
#include "resource.h"
using namespace std;
LRESULT WINAPI WinProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
HWND hWnd = 0;
HDC hdc = 0;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
ZeroMemory(&msg, sizeof(msg));
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wc.hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_HAND));
wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ASTERISK));
wc.hInstance = hInst;
wc.lpfnWndProc = WinProc;
wc.lpszClassName = "ClassName";
wc.lpszMenuName = 0;
wc.style = CS_VREDRAW | CS_HREDRAW;
if (!RegisterClass(&wc))
MessageBox(0, "Class was not registered!", "Error", 0);
hWnd=CreateWindow("ClassName", "WindowName", WS_OVERLAPPEDWINDOW, 100, 100, 640, 480, 0, 0, hInst, 0);
if (hWnd==0)
MessageBox(0, "Window was not created!", "Error", 0);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
hdc = GetDC(hWnd);
while (msg.message!=WM_QUIT)
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
HDC hBuffDC = CreateCompatibleDC(hdc);
HDC hMemDC = CreateCompatibleDC(hdc);
HBITMAP hBuffBitmap = CreateCompatibleBitmap(hdc, 640, 480);
HBITMAP hSpriteBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_SPRITE));
HBITMAP hMaskBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_MASK));
HBITMAP hOldBuffBitmap= (HBITMAP)SelectObject(hBuffDC, hBuffBitmap);
HBRUSH hOldBrush= (HBRUSH)SelectObject(hBuffDC, (HBRUSH)GetStockObject(GRAY_BRUSH));
Rectangle(hBuffDC, 0, 0, 640, 480);
HBITMAP hOldSpriteBitmap= (HBITMAP)SelectObject(hMemDC, hMaskBitmap);
BitBlt(hBuffDC, 0, 0, 640, 480, hMemDC, 0, 0, SRCAND);
SelectObject(hMemDC, hSpriteBitmap);
BitBlt(hBuffDC, 0, 0, 640, 480, hMemDC, 0, 0, SRCPAINT);
BitBlt(hdc, 0, 0, 640, 480, hBuffDC, 0, 0, SRCCOPY);
SelectObject(hBuffDC, hOldBuffBitmap);
SelectObject(hMemDC, hOldSpriteBitmap);
SelectObject(hBuffDC, hOldBrush);
DeleteObject(hBuffBitmap);
DeleteObject(hSpriteBitmap);
DeleteObject(hMaskBitmap);
DeleteDC(hMemDC);
DeleteDC(hBuffDC);
}
return 0;
}
LRESULT WINAPI WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_KEYDOWN:
switch (wParam)
{
case VK_ESCAPE:
DestroyWindow(hWnd);
break;
}
return 0;
case WM_CLOSE:
DestroyWindow(hWnd);
return 0;
case WM_DESTROY:
int Res = ReleaseDC(hWnd, hdc); //Release main window device context before destroying window
DWORD err = GetLastError();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}