Having a window with gif and some elements c++ winapi(Non-MFC) using gdi+. SO I want to make transparent elements(Edit,Static,Button). I want to make transparent gray background of elements.
I have tried to handle WM_CTLCOLORSTATIC and adding style WS_EX_TRANSPARENT and m. but it didn't give correct result. But when I handle WM_CTLCOLORSTATIC with code:
hdcStatic = (HDC) wParam;
SetTextColor(hdcStatic, RGB(0,0,0));
SetBkMode (hdcStatic, TRANSPARENT);
return (LRESULT)GetStockObject(NULL_BRUSH);
And it showed STATIC styles transparent, but I could see background of Windows 10.
Minimum code is:
#include <memory>
#include "Resource.h"
#include <vector>
#include "TESTING.h"
#include "framework.h"
#include <algorithm>
#include <windows.h>
#include <objidl.h>
#include <GdiPlus.h>
#include <gdiplusimaging.h>
#include <shlwapi.h>
#include<CommCtrl.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
#pragma comment (lib,"shlwapi.lib")
#pragma comment (lib,"Comctl32.lib")
#define TIMER_ID 101
static HFONT s_hFont = NULL;
static HWND hWnd;
static HWND hwndText;
static HWND hwndButton;
static HWND hwndLabel;
static HWND hwndCode;
static HWND hwndCode2;
static HWND hwndTime;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK SubWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR);
int WINAPI _WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
ULONG_PTR m_gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
HMODULE hMod = GetModuleHandle(NULL);
HRSRC hRes = FindResource(hMod, MAKEINTRESOURCEW(MY_GIF_ID), RT_RCDATA);
if (!hRes) MessageBox(NULL, L"hRes!!", L"ERROR", 0);
HGLOBAL hGlobal = LoadResource(hMod, hRes);
if (!hGlobal)MessageBox(NULL, L"hGlobal!!", L"ERROR", 0);
void* pResData = LockResource(hGlobal);
if (!pResData) MessageBox(NULL, L"pResData!!", L"ERROR", 0);
DWORD dwResData = SizeofResource(hMod, hRes);
IStream* pStream = SHCreateMemStream((BYTE*)pResData, dwResData);
if (!pStream) MessageBox(NULL, L"pStream!!", L"ERROR", 0);
Image gif(pStream);
pStream->Release();
MSG msg = { 0 };
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = NULL; // <= Do not provide a background brush.
wc.lpszClassName = L"anim_gif_player";
if (!RegisterClass(&wc))
return -1;
hWnd = CreateWindow(wc.lpszClassName,
L"",
WS_EX_TOPMOST | WS_CLIPCHILDREN & ~WS_CAPTION & ~WS_SYSMENU,
0, 0, 640, 480, 0, 0, hInstance, &gif);
if (!hWnd) {
MessageBox(0, L"SSSSSSSSSSSSSSSSSSS", L"kkkkkkkkkkkkkkkkkkkkkk", 0);
return -2;
}
hwndLabel = CreateWindowEx(WS_EX_TRANSPARENT,L"STATIC",
NULL,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_CLIPSIBLINGS,
325, 90, 440, 45,
hWnd,
NULL,
NULL,
NULL);
SetWindowSubclass(hwndLabel, SubWndProc, 0, 0);
const TCHAR* fontName = TEXT("Croobie");
const long nFontSize = 24;
HDC hdc = GetDC(hwndLabel);
LOGFONT logFont = { 0 };
logFont.lfHeight = -MulDiv(nFontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
logFont.lfWeight = FW_BOLD;
wcscpy_s(logFont.lfFaceName, fontName);
s_hFont = CreateFontIndirect(&logFont);
SendMessage(hwndLabel, WM_SETFONT, (WPARAM)s_hFont, (LPARAM)MAKELONG(TRUE, 0));
ShowWindow(hWnd, SW_SHOWMAXIMIZED);
UpdateWindow(hWnd);
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//DeleteObject(wc.hbrBackground);
return msg.wParam;
}
std::vector<unsigned int> LoadGifFrameInfo(Image* image)
{
// I think animated gifs will always only have 1 frame dimension...
// the "dimension" being the frame count, but I could be wrong about this
int count = image->GetFrameDimensionsCount();
if (count != 1)
return std::vector<unsigned int>();
GUID guid;
if (image->GetFrameDimensionsList(&guid, 1) != 0)
return std::vector<unsigned int>();
int frame_count = image->GetFrameCount(&guid);
auto sz = image->GetPropertyItemSize(PropertyTagFrameDelay);
if (sz == 0)
return std::vector<unsigned int>();
// copy the frame delay property into the buffer backing an std::vector
// of bytes and then get a pointer to its value, which will be an array of
// unsigned ints
std::vector<unsigned char> buffer(sz);
PropertyItem* property_item = reinterpret_cast<PropertyItem*>(&buffer[0]);
image->GetPropertyItem(PropertyTagFrameDelay, sz, property_item);
unsigned int* frame_delay_array = (unsigned int*)property_item[0].value;
// copy the delay values into an std::vector while converting to milliseconds.
std::vector<unsigned int> frame_delays(frame_count);
std::transform(frame_delay_array, frame_delay_array + frame_count, frame_delays.begin(),
[](unsigned int n) {return n * 10; }
);
return frame_delays;
}
void GenerateFrame(Bitmap* bmp, Image* gif)
{
Graphics dest(bmp);
SolidBrush white(Color::White);
dest.FillRectangle(&white, 0, 0, bmp->GetWidth(), bmp->GetHeight());
if (gif)
dest.DrawImage(gif, 0, 0);
}
std::unique_ptr<Bitmap> CreateBackBuffer(HWND hWnd)
{
RECT r;
GetClientRect(hWnd, &r);
return std::make_unique<Bitmap>(r.right - r.left, r.bottom - r.top);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static Image* animated_gif;
static std::unique_ptr<Bitmap> back_buffer;
static std::vector<unsigned int> frame_delays;
static int current_frame;
switch (message) {
case WM_CREATE: {
animated_gif = reinterpret_cast<Image*>(
reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams
);
if (!animated_gif || animated_gif->GetLastStatus() != 0) {
MessageBox(hWnd, L"Unable to load animated gif", L"error", MB_ICONERROR);
return 0;
}
// Create a bitmap the size of the window's clent area
back_buffer = CreateBackBuffer(hWnd);
// get the frame delays and thereby test that this is really an animated gif
frame_delays = LoadGifFrameInfo(animated_gif);
if (frame_delays.empty()) {
MessageBox(hWnd, L"Invalid gif or not an animated gif", L"error", MB_ICONERROR);
return 0;
}
current_frame = 0;
animated_gif->SelectActiveFrame(&FrameDimensionTime, current_frame);
GenerateFrame(back_buffer.get(), animated_gif);
SetTimer(hWnd, TIMER_ID, frame_delays[0], nullptr);
InvalidateRect(hWnd, nullptr, FALSE);
}
break;
case WM_TIMER: {
KillTimer(hWnd, TIMER_ID);
current_frame = (current_frame + 1) % frame_delays.size();
animated_gif->SelectActiveFrame(&FrameDimensionTime, current_frame);
GenerateFrame(back_buffer.get(), animated_gif);
SetTimer(hWnd, TIMER_ID, frame_delays[current_frame], nullptr);
InvalidateRect(hWnd, nullptr, FALSE);
} break;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
Graphics g(hdc);
g.DrawImage(back_buffer.get(), 0, 0);
EndPaint(hWnd, &ps);
} break;
case WM_SIZE: {
back_buffer = CreateBackBuffer(hWnd);
GenerateFrame(back_buffer.get(), animated_gif);
} break;
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK SubWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR) {
switch (msg) {
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc; GetClientRect(hwnd, &rc);
SetTextColor(hdc, Color::Yellow);
SetBkMode(hdc, TRANSPARENT);
wchar_t buf[256] = { 0 };
GetWindowTextW(hwnd, buf, sizeof(buf) / sizeof(*buf));
DrawTextW(hdc, buf, wcslen(buf), &rc, DT_LEFT | DT_TOP);
EndPaint(hwnd, &ps);
return 0;
}
case WM_NCDESTROY://safely remove subclass
RemoveWindowSubclass(hwnd, SubWndProc, 0);
return DefSubclassProc(hwnd, msg, wParam, lParam);
}
return DefSubclassProc(hwnd, msg, wParam, lParam);
}
The WS_EX_TRANSPARENT style doesn’t mean “transparent”; it means
“paint over siblings.”
The style is called “transparent” not because it makes the window
transparent but because it makes transparency possible.
Please refer: Why isn't my transparent static control transparent?
The only way I've found to do this reliably is to sub-class the static control and paint the background manually.
You can catch the WM_ERASEBKGND message and paint the appropriate portion of the underlying bitmap.
Please refer:
Is it possible to make a Static control transparent?
C++ Win32 Static Control Transparent Background
Also, if you want remove grey background, you can change static control background and make it transparent.
Demon of custom static control:
#include "stdafx.h"
#include "Test_WM_CTLCOLORSTATIC.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
HWND hWndStatic;
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WndProcPanel(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_TESTWMCTLCOLORSTATIC, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTWMCTLCOLORSTATIC));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTWMCTLCOLORSTATIC));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_TESTWMCTLCOLORSTATIC);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
WNDPROC StaticWndProc = NULL;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{ LRESULT lRes = DefWindowProc(hWnd, message, wParam, lParam);
hWndStatic = CreateWindowEx(0, L"Static", NULL, WS_CHILD | WS_VISIBLE | SS_LEFT, 10, 130, 200, 40, hWnd, NULL, hInst, NULL); //v2 deleted HWND
StaticWndProc = (WNDPROC)SetWindowLong(hWndStatic, GWL_WNDPROC, (LPARAM)WndProcPanel);
return lRes;
}
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_ERASEBKGND:
{
HBRUSH brush;
RECT rect;
brush = CreateSolidBrush(RGB(0, 255, 0));
SelectObject((HDC)wParam, brush);
GetClientRect(hWnd, &rect);
Rectangle((HDC)wParam, rect.left, rect.top, rect.right, rect.bottom);
}
break;
case WM_DESTROY:
SetWindowLong(hWndStatic, GWL_WNDPROC, (LPARAM)StaticWndProc);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
TCHAR szText[] = _T("TestString");;
LRESULT CALLBACK WndProcPanel(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_PAINT)
{
RECT rc;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rc);
SetBkMode(hdc, TRANSPARENT);
DrawText(hdc, szText, _tcslen(szText), &rc, DT_CENTER | DT_VCENTER);
EndPaint(hWnd, &ps);
return 0;
}
return CallWindowProc(StaticWndProc, hWnd, message, wParam, lParam);
}
thx #IInspectable's remind, the best way to subclass controls is to use SetWindowsSubclass, see Subclassing Controls
I'll update the new code later, and I'm sorry for my shallow knowledge.
Updated:
case WM_TIMER: {
KillTimer(hWnd, TIMER_ID);
current_frame = (current_frame + 1) % frame_delays.size();
animated_gif->SelectActiveFrame(&FrameDimensionTime, current_frame);
GenerateFrame(back_buffer.get(), animated_gif);
SetTimer(hWnd, TIMER_ID, frame_delays[current_frame], nullptr);
InvalidateRect(hWnd, nullptr, FALSE);
InvalidateRect(hwndLabel, nullptr, FALSE); //Here add new code
}
Debug Result:
Related
I'm trying to learn how to subclass a GUI control and 'modify' its hdc.
This is my subclass callback:
mygui.h
#include <commctrl.h> // SetWindowSubclass
#pragma comment(lib, "Comctl32.lib")
#include <windows.h> // GDI includes.
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
using namespace DllExports;
#pragma comment (lib,"Gdiplus.lib")
typedef UCHAR GuiControls;
enum GuiControlTypes {
GUI_CONTROL_BUTTON
};
struct GuiControlOptionsType
{
int x;
int y;
int width;
int height;
LPCWSTR text;
GuiControlTypes controltype;
bool ERASEDBKGND = false; // Used on the subclass proc.
HDC dc;
};
class Gui
{
public:
std::map<HWND, GuiControlOptionsType> control_list;
HWND GuihWnd;
HWND A_LasthWnd;
LRESULT Create();
LRESULT AddControl(GuiControls aControlType, GuiControlOptionsType opt);
};
LRESULT CALLBACK ButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
mygui.cpp
/* Window Procedure. */
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// TODO
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow)
{
Gui mygui;
mygui.Create();
}
LRESULT Gui::Create()
{
WNDCLASSEX wc{};
MSG Msg;
HWND hWnd = nullptr;
wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0;
wc.hInstance = 0; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL;
wc.lpszClassName = L"classname";
if (!RegisterClassEx(&wc))
// TODO
this->GuihWnd = CreateWindowW(
wc.lpszClassName,
L"Title",
WS_EX_COMPOSITED | WS_EX_LAYERED | // Double buffering
WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 200,
nullptr, nullptr, nullptr, nullptr);
DWORD err = GetLastError();
if (this->GuihWnd == NULL)
// TODO
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
GuiControlOptionsType opt;
opt.x = 10; opt.y = 10; opt.width = 100; opt.height = 100; opt.text = L"test";
this->AddControl(GUI_CONTROL_BUTTON, opt);
SetWindowSubclass(this->A_LasthWnd, ButtonProc, 1, (DWORD_PTR)this);
ShowWindow(this->GuihWnd, SW_SHOW);
UpdateWindow(this->GuihWnd);
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return 0; // Msg.wParam;
}
LRESULT Gui::AddControl(GuiControls aControlType, GuiControlOptionsType opt)
{
switch (aControlType)
{
case GUI_CONTROL_BUTTON:
{
HWND hWnd = CreateWindow(
L"BUTTON", // Predefined class; Unicode assumed
opt.text, // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
opt.x, // x position
opt.y, // y position
opt.width, // Button width
opt.height, // Button height
this->GuihWnd, // Parent window
NULL, // No menu.
NULL, //(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
NULL); // Pointer not needed.
opt.controltype = GUI_CONTROL_BUTTON;
this->control_list.emplace(hWnd, opt);
this->A_LasthWnd = hWnd;
}
break;
default:
break;
}
return 1;
}
LRESULT CALLBACK ButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
Gui* pThis = (Gui*)dwRefData;
switch (uMsg)
{
case WM_ERASEBKGND:
{
if (pThis->control_list[hWnd].ERASEDBKGND)
return 1;
// Create/save the new button dc.
GpBitmap* pBitmap;
GdipCreateBitmapFromScan0(pThis->control_list[hWnd].width, pThis->control_list[hWnd].height, 0, PixelFormat32bppPARGB, 0, &pBitmap);
GpGraphics* g;
GdipGetImageGraphicsContext(pBitmap, &g);
GdipGraphicsClear(g, 0xFF2400ff);
HBITMAP hbm;
GdipCreateHBITMAPFromBitmap(pBitmap, &hbm, 0);
HDC dc = CreateCompatibleDC(NULL);
SelectObject(dc, hbm);
pThis->control_list[hWnd].dc = dc;
GdipDisposeImage(pBitmap);
GdipDeleteGraphics(g);
DeleteObject(hbm);
pThis->control_list[hWnd].ERASEDBKGND = 1;
}
break;
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
{
InvalidateRect(hWnd, 0, 1);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
BLENDFUNCTION bf;
bf.SourceConstantAlpha = 255;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.AlphaFormat = AC_SRC_ALPHA;
GdiAlphaBlend(hdc, 0, 0, pThis->control_list[hWnd].width, pThis->control_list[hWnd].height,
pThis->control_list[hWnd].dc, 0, 0, pThis->control_list[hWnd].width, pThis->control_list[hWnd].height, bf);
EndPaint(hWnd, &ps);
DeleteObject(hdc);
return TRUE;
}
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
The problem is... when I click on the button it restores its default hdc, when I minimize/restore it draws with my 'custom' hdc.
I tried adding a call to InvalidateRect() under WM_LBUTTONDOWN, but it resulted in the same thing.
I also tried creating the Gui with 'double buffering' adding the styles WS_EX_COMPOSITED | WS_EX_LAYERED.
What am I missing?
BS_OWNERDRAW should be "her" style, I think.
I Was working on a win32 project which uses the WM_MOVING message from WndProc to detect the new position where window is moved. but when i try to snap the window to left side the LPARAM gives wrong values for just a millisecond and gives the real value after that.
Please have a look into the output:
Left: 0, Right:683
Left: -205, Right:295
Left: 0, Right:683
Left: -205, Right:295
Left: 0, Right:683
Left: -205, Right:295
The real output is -205 but in between I get zero which cause the content of my window to flicker. In my case the position of the content is depend on the window position, so it causes flicker if I receive a wrong value.
Here is what i am trying to achieve : Manually creating acrylic effect
When ever I move the window in this mode (just before the aero snap mode) the value will get changed to zero.
This is only affecting The Top and Left coordinates of the window and also flickers when aero snapping to any sides.
Here is a simple reproduceable example:
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
RECT* hostRect;
char buffer[200];
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(0,CLASS_NAME,L"Learn to Program Windows",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,NULL,NULL,hInstance,NULL);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_MOVING:
{
hostRect = reinterpret_cast<RECT*>(lParam);
sprintf_s(buffer, "Left: %d, Top:%d, Right:%d, Bottom:%d\n", hostRect->left, hostRect->top, hostRect->right, hostRect->bottom);
OutputDebugStringA(buffer);
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Run this program and check the value in output while you move the window to any side for aero snap.
Note: In my case i will not be able to use GetWindowRect() in WM_MOVING, because calling GetWindowRect() slow down the render() function (just a directx painting) in my window.
You can try to make temporary changes to the style of the window.
Here is an example:
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <stdio.h>
#define WM_RESTOREORIGINALSTYLE WM_USER + 1
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
RECT* hostRect;
char buffer[200];
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(0, CLASS_NAME, L"Learn to Program Windows", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_MOVING:
{
hostRect = reinterpret_cast<RECT*>(lParam);
sprintf_s(buffer, "Left: %d, Top:%d, Right:%d, Bottom:%d\n", hostRect->left, hostRect->top, hostRect->right, hostRect->bottom);
OutputDebugStringA(buffer);
break;
}
case WM_SYSCOMMAND:
{
if (wParam == (SC_MOVE | 2)) wParam = SC_SIZE | 9;
if ((wParam & 0xFFE0) == SC_SIZE && (wParam & 0x000F)) // handles MOVE and SIZE in one "if"
{
long int oldStyle = GetWindowLongW(hwnd, GWL_STYLE);
PostMessageW(hwnd, WM_RESTOREORIGINALSTYLE, GWL_STYLE, oldStyle);
SetWindowLongW(hwnd, GWL_STYLE, oldStyle & 0xFEFEFFFF); // disable WS_MAXIMIZE and WS_MAXIMIZEBOX
DefWindowProcW(hwnd, WM_SYSCOMMAND, wParam, lParam);
return 0;
}
return DefWindowProcW(hwnd, WM_SYSCOMMAND, wParam, lParam);
}
case WM_RESTOREORIGINALSTYLE:
{
if ((long int)wParam == GWL_STYLE)
SetWindowLongW(hwnd, GWL_STYLE, lParam);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
More reference : Win32 prevent window “snap”
I have a button which is subclassed and I would like it to display a bitmap with a transparent background. Upon searching the internet, I found that you have to do it with the function AlphaBlend( I tried this but that didn't work either). I also saw this bitmap transparency in Visual Studio 2013 C++ thread and I tried the steps but I couldn't do it. I would be fine with either GDI or GDI+ just as long as i can get button with a transparent background image on it.
Example:
Here is my code—— I know it's messy but please bare with it as I was trying a lot of things to try and make it work (there was a lot of copying and pasting)
Update : Here is the code again but trimmed without all the fat and yes I did try WS_EX_LAYERED on both the windows.
// CustomButton2.cpp : Defines the entry point for the application.
//
#include "framework.h"
#include "CustomButton2.h"
#include <commctrl.h>
#include <gdiplus.h>
#include <system_error>
#include "SkinClass/skin.h"
#define MAX_LOADSTRING 100
#define CRAPPY 567
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' " "version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
const char g_szClassName[] = "MyClassName";
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
void RegisterClass1(HINSTANCE);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
/// Turn given window into a layered window and load a bitmap from given resource ID
/// into it.
/// The window will be resized to fit the bitmap.
/// Bitmap must be 32bpp, top-down row order, premultiplied alpha.
///
/// \note For child windows, this requires Win 8 or newer OS
/// (and "supportedOS" element for Win 8 in application manifest)
///
/// \exception Throws std::system_error in case of any error.
void SetLayeredWindowFromBitmapResource(
HWND hwnd, UINT bitmapResourceId, HINSTANCE hInstance = nullptr)
{
// Enable "layered" mode for the child window. This enables full alpha channel
// transparency.
// GetWindowLong() won't reset the last error in case of success.
// As we can't judge from the return value of GetWindowLong() alone if
// the function was successful (0 may be returned even in case of
// success), we must reset the last error to reliably detect errors.
::SetLastError(0);
DWORD exStyle = ::GetWindowLong(hwnd, GWL_EXSTYLE);
if (!exStyle)
{
// NOTE: Call GetLastError() IMMEDIATELY when a function's return value
// indicates failure and it is documented that said function supports
// GetLastError().
// ANY other code (be it your own or library code) before the next line
// must be avoided as it may invalidate the last error value.
if (DWORD err = ::GetLastError())
throw std::system_error(static_cast<int>(err),
std::system_category(),
"SetLayeredWindowFromBitmapResource: Could not get extended window style");
}
// SetWindowLong() won't reset the last error in case of success.
// As we can't judge from the return value of GetWindowLong() alone if
// the function was successful (0 may be returned even in case of
// success), we must reset the last error to reliably detect errors.
::SetLastError(0);
if (!::SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED))
{
if (DWORD err = ::GetLastError())
throw std::system_error(static_cast<int>(err),
std::system_category(),
"SetLayeredWindowFromBitmapResource: Could not set extended window style");
}
// Use RAII ( https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization )
// to cleanup resources even in case of exceptions.
// This greatly simplifies the code because now we don't have to manually cleanup the
// resources at every location in the code where we throw an exception.
struct Resources {
HBITMAP hImage = nullptr;
HGDIOBJ hOldImage = nullptr;
HDC hMemDC = nullptr;
// This destructor will be automatically called before the function
// SetLayeredWindowFromBitmapResource() returns aswell as any locations
// in the code where the "throw" keyword is used to throw an exception.
~Resources()
{
if (hMemDC)
{
if (hOldImage)
::SelectObject(hMemDC, hOldImage);
::DeleteDC(hMemDC);
}
if (hImage)
::DeleteObject(hImage);
}
} res;
// Make it possible to use nullptr as an argument for the hInstance parameter of
// this function. This means we will load the resources from the current executable
// (instead of another DLL).
if (!hInstance)
hInstance = ::GetModuleHandle(nullptr);
// Load bitmap with alpha channel from resource.
// Flag LR_CREATEDIBSECTION is required to create a device-independent bitmap that
// preserves the alpha channel.
res.hImage = reinterpret_cast<HBITMAP>(::LoadImage(
hInstance, MAKEINTRESOURCE(bitmapResourceId), IMAGE_BITMAP,
0, 0, LR_CREATEDIBSECTION));
if (!res.hImage)
{
DWORD err = ::GetLastError();
throw std::system_error(static_cast<int>(err),
std::system_category(),
"SetLayeredWindowFromBitmapResource: Could not load bitmap resource");
}
// Get bitmap information (width, height, etc.)
BITMAP imgInfo{ 0 };
if (!::GetObject(res.hImage, sizeof(imgInfo), &imgInfo))
{
DWORD err = ::GetLastError();
throw std::system_error(static_cast<int>(err),
std::system_category(),
"SetLayeredWindowFromBitmapResource: Could not get bitmap information");
}
if (imgInfo.bmBitsPixel != 32 || imgInfo.bmPlanes != 1)
{
// Use a constant error value here because this is our own error condition.
// Of course GetLastError() wouldn't return anything useful in this case.
DWORD err = ERROR_INVALID_DATA;
throw std::system_error(err, std::system_category(),
"SetLayeredWindowFromBitmapResource: bitmap must be 32 bpp, single plane");
}
// Create a memory DC that will be associated with the image.
// UpdateLayeredWindow() can't use image directly, it must be in a memory DC.
res.hMemDC = ::CreateCompatibleDC(nullptr);
if (!res.hMemDC)
{
DWORD err = ::GetLastError();
throw std::system_error(static_cast<int>(err),
std::system_category(),
"SetLayeredWindowFromBitmapResource: Could not create memory DC");
}
res.hOldImage = ::SelectObject(res.hMemDC, res.hImage);
if (!res.hOldImage)
{
DWORD err = ::GetLastError();
throw std::system_error(static_cast<int>(err),
std::system_category(),
"SetLayeredWindowFromBitmapResource: Could not select bitmap into memory DC");
}
// Assign the image to the child window, making it transparent.
SIZE size{ imgInfo.bmWidth, imgInfo.bmHeight };
POINT ptSrc{ 0, 0 };
BLENDFUNCTION blend{ AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
if (!::UpdateLayeredWindow(hwnd, nullptr, nullptr, &size, res.hMemDC, &ptSrc,
0, &blend, ULW_ALPHA))
{
DWORD err = ::GetLastError();
throw std::system_error(static_cast<int>(err),
std::system_category(),
"SetLayeredWindowFromBitmapResource: Could not update layered window");
}
// Destructor of res object will cleanup resources here!
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
RegisterClass1(hInstance);
HWND hWnd = CreateWindowExA(WS_EX_LAYERED, g_szClassName, "Scenes", WS_OVERLAPPEDWINDOW | WS_EX_LAYERED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,
hInstance, NULL);
HWND hButton = CreateWindow(TEXT("BUTTON"), TEXT("START EDITING!"), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW | WS_EX_LAYERED, 1, 1,228, 228,
hWnd, (HMENU)CRAPPY, NULL, NULL);
//SetLayeredWindowFromBitmapResource(hButton, ID_THECIRCLE, hInstance);
SetLayeredWindowAttributes(hWnd, 0, 249, LWA_ALPHA);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
SetLayeredWindowFromBitmapResource(hButton, ID_THECIRCLE);
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
//
// FUNCTION: RegisterClass1()
//
// PURPOSE: Registers the class
//
// COMMENTS:
//
//
//
//
void RegisterClass1(HINSTANCE hInstance) {
WNDCLASSEXA wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
//wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hbrBackground = CreateSolidBrush(RGB(255, 0, 0));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = g_szClassName;
wc.lpszMenuName = "MENU";
wc.style = CS_HREDRAW | CS_VREDRAW;
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
}
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Starting from Window 8, WS_EX_LAYERED can be used for child controls.
Method: A manifest file is required to specify at least Window 8 compatibility (sub-layering is only supported from Window 8).
Refer: Targeting your application for Windows
Code Sample: (error check has been removed)
// Test_CustomButton.cpp : Defines the entry point for the application.
//
#include "framework.h"
#include "Test_CustomButton.h"
#include <commctrl.h>
#include <system_error>
#pragma comment (lib,"Comctl32.lib")
#define MAX_LOADSTRING 100
#define IDC_OWNERDRAWBUTTON 101
#define CRAPPY 567
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
void SetLayeredWindowFromBitmapResource(
HWND hwnd, UINT bitmapResourceId, HINSTANCE hInstance = nullptr)
{
DWORD exStyle = ::GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED);
struct Resources {
HBITMAP hImage = nullptr;
HGDIOBJ hOldImage = nullptr;
HDC hMemDC = nullptr;
~Resources()
{
if (hMemDC)
{
if (hOldImage)
::SelectObject(hMemDC, hOldImage);
::DeleteDC(hMemDC);
}
if (hImage)
::DeleteObject(hImage);
}
} res;
if (!hInstance)
hInstance = ::GetModuleHandle(nullptr);
res.hImage = reinterpret_cast<HBITMAP>(::LoadImage(
hInstance, MAKEINTRESOURCE(bitmapResourceId), IMAGE_BITMAP,
0, 0, LR_CREATEDIBSECTION));
BITMAP imgInfo{ 0 };
GetObject(res.hImage, sizeof(imgInfo), &imgInfo);
res.hMemDC = ::CreateCompatibleDC(nullptr);
res.hOldImage = ::SelectObject(res.hMemDC, res.hImage);
// Assign the image to the child window, making it transparent.
SIZE size{ imgInfo.bmWidth, imgInfo.bmHeight };
POINT ptSrc{ 0, 0 };
BLENDFUNCTION blend{ AC_SRC_OVER, 0, 200, AC_SRC_ALPHA };
UpdateLayeredWindow(hwnd, nullptr, nullptr, &size, res.hMemDC, &ptSrc,
0, &blend, ULW_ALPHA);
// Destructor of res object will cleanup resources here!
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_TESTCUSTOMBUTTON, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTCUSTOMBUTTON));
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTCUSTOMBUTTON));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_TESTCUSTOMBUTTON);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
HWND hButton = CreateWindowEx(0, L"button", NULL, WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 150, 50, 80, 80, hWnd, (HMENU)IDC_OWNERDRAWBUTTON, hInst, NULL);
SetWindowSubclass(hButton, &OwnerDrawButtonProc, IDC_OWNERDRAWBUTTON, 0);
}
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// Used be test
HPEN hPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
SelectObject(hdc, hPen);
MoveToEx(hdc, 150, 150, NULL);
LineTo(hdc, 200, 60);
LineTo(hdc, 250, 150);
LineTo(hdc, 150, 150);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_PAINT:
{
RECT rc;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
SetLayeredWindowFromBitmapResource(hWnd, IDB_BITMAP1);
EndPaint(hWnd, &ps);
return 0;
}
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &OwnerDrawButtonProc, 1);
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
Debug:
Note:
When I tested, I found that the following conditions need to be met:
The image to load must be a 32 bpp, top-down bitmap with
premultiplied alpha channel.
Here is my 32 bpp image for your test.
As I said at the beginning, you need to add a manifest file, you can
create a .manifest file yourself. Then add it in the compiler.
You only need to add the WS_EX_LAYERED style to the child control,
because your requirement is to make the buttons transparent.
In my case the code that was provided didn't work because it was throwing an exception and I didn't add the manifest file properly. I would like to note some things:
Bitmap Images made with gimp do not work; use what was suggested in the thread that I linked in my question and that would be PixelFormer. In Pixel Former go "file", then "Import" and choose your file(in my case it was transparent png of a circle). Next go to "export" and choose bitmap bmp (windows bitmap it will say). Once you do that, already A8:R8:G8:B8 (32bpp) will be selected and you have to next check the last two boxes "Premultiplied Alpha" and "Top-down row order". What has to be said is that image does indeed have an alpha channel and it's transparent. Don't be fooled in opening up in paint and you see a black background and you think the transparency isn't there—— it's there the transparency works... If you open it up it in photoshop (i think) or a sophisiticated image editing program you will see a transparent background in your image.
manifest.xml
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>
Copy the above and save it as manifest.xml
The other thing that I would like to note is adding a manifest entry file: in my case I had to add it to C:\Users\Rubel\source\repos\CustomButton2\CustomButton2 so whoever is looking at this in the future, replace that with your directory of your project;for example, C:\Users\blah\source\repos\blah\blah (the blahs with the name of your stuff). Paste your manifest file there (in the directory).
You then have to add it to visual studio. You do this by right clicking in your project in the solution explorer (in my case it was CustomButton2 ) and go to "Manifest Tool" in "Input and Output". In here type in "Additional Manifest Files" the path of your manifest file (in my case it was manifest.xml but if you were following along, then this should be the case as well for your path) and click ok.
That is it, if you used the code ( Strive Sun - MSFT ) that was given it should work.
Here is my final code if anyone was interested:
// CustomButton2.cpp : Defines the entry point for the application.
//
#include "framework.h"
#include "CustomButton2.h"
#include <commctrl.h>
#include <gdiplus.h>
#include <system_error>
#define MAX_LOADSTRING 100
#define CRAPPY 567
#define IDC_OWNERDRAWBUTTON 101
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' " "version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
const char g_szClassName[] = "MyClassName";
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
void RegisterClass1(HINSTANCE);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
void SetLayeredWindowFromBitmapResource(
HWND hwnd, UINT bitmapResourceId, HINSTANCE hInstance = nullptr)
{
DWORD exStyle = ::GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED);
struct Resources {
HBITMAP hImage = nullptr;
HGDIOBJ hOldImage = nullptr;
HDC hMemDC = nullptr;
~Resources()
{
if (hMemDC)
{
if (hOldImage)
::SelectObject(hMemDC, hOldImage);
::DeleteDC(hMemDC);
}
if (hImage)
::DeleteObject(hImage);
}
} res;
if (!hInstance)
hInstance = ::GetModuleHandle(nullptr);
res.hImage = reinterpret_cast<HBITMAP>(::LoadImage(
hInstance, MAKEINTRESOURCE(bitmapResourceId), IMAGE_BITMAP,
0, 0, LR_CREATEDIBSECTION));
BITMAP imgInfo{ 0 };
GetObject(res.hImage, sizeof(imgInfo), &imgInfo);
res.hMemDC = ::CreateCompatibleDC(nullptr);
res.hOldImage = ::SelectObject(res.hMemDC, res.hImage);
// Assign the image to the child window, making it transparent.
SIZE size{ imgInfo.bmWidth, imgInfo.bmHeight };
POINT ptSrc{ 0, 0 };
BLENDFUNCTION blend{ AC_SRC_OVER, 0, 200, AC_SRC_ALPHA };
UpdateLayeredWindow(hwnd, nullptr, nullptr, &size, res.hMemDC, &ptSrc,
0, &blend, ULW_ALPHA);
// Destructor of res object will cleanup resources here!
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
RegisterClass1(hInstance);
HWND hWnd = CreateWindowExA(WS_EX_LAYERED, g_szClassName, "Scenes", WS_OVERLAPPEDWINDOW | WS_EX_LAYERED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,
hInstance, NULL);
HWND hButton = CreateWindow(TEXT("BUTTON"), TEXT("START EDITING!"), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW | WS_EX_LAYERED, 1, 1,228, 228,
hWnd, (HMENU)CRAPPY, NULL, NULL);
SetWindowSubclass(hButton, &OwnerDrawButtonProc, IDC_OWNERDRAWBUTTON, 0);
//SetLayeredWindowFromBitmapResource(hButton, ID_THECIRCLE, hInstance);
SetLayeredWindowAttributes(hWnd, 0, 249, LWA_ALPHA);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
//SetLayeredWindowFromBitmapResource(hButton, ID_THECIRCLE);
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
//
// FUNCTION: RegisterClass1()
//
// PURPOSE: Registers the class
//
// COMMENTS:
//
//
//
//
void RegisterClass1(HINSTANCE hInstance) {
WNDCLASSEXA wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
//wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hbrBackground = CreateSolidBrush(RGB(255, 0, 0));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = g_szClassName;
wc.lpszMenuName = "MENU";
wc.style = CS_HREDRAW | CS_VREDRAW;
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
}
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_PAINT:
{
RECT rc;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
SetLayeredWindowFromBitmapResource(hWnd, ID_THECIRCLE);
EndPaint(hWnd, &ps);
return 0;
}
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &OwnerDrawButtonProc, 1);
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
Application running :
I know this was verbose but I wanted it to be easy for whoever is encountering this so that they know how to do it.
I am new to Windows Api and still learning the basics. In my project I encountered a problem that requires subclassing, so I decided to find a sample code, compile it, and see how it works in practice. I have found a relatively straightforward example at the Windows Docs webpage How to Subclass a Combo Box. After creating a parent window and making a few tweaks for the code to compile, I managed to run it, but the program appears to behave as if no subclassing was done.
This is not the first time a sample code found on the webpage is not working. Am I missing something?
My WinMain routine:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WNDCLASSEX wc = { 0 };
wc.lpszClassName = "MyClassName";
wc.hInstance = hInstance;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = ToolbarWindowProc;
RegisterClassEx(&wc)
hwndMain = CreateWindow(
"MyClassName",
"Title",
WS_POPUPWINDOW | WS_CAPTION,
0,
0,
500,
210,
nullptr,
nullptr,
hInstance,
NULL
);
ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);
MSG msg;
while (GetMessageW(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return (int)msg.wParam;
}
You need to know why we need to subclass controls.
Subclassing an existing control is another way to create a custom
control. The subclass procedure can alter selected behaviors of the
control by processing those messages that affect the selected
behaviors. All other messages pass to the original window procedure
for the control. For example, an application can display a small
bitmap next to the text in a read-only, single-line edit control by
subclassing the control and processing the WM_PAINT message.
Refer: Subclassing the Window Class of an Existing Control
From the link you provide, it seems that you didn't follow the tutorial to learn how to subclass controls.
But now it seems that you need a complete example to learn.
So the first step is to use the official template, see the screensnap below:
After creation, you can start with subclassing static control.
Add the following message event to the WndProc function.
case WM_CREATE:
{
hWndStatic = CreateWindowEx(0, L"Static", NULL, WS_CHILD | WS_VISIBLE | SS_LEFT, 10, 130, 200, 40, hWnd, (HMENU)IDC_OWNERDRAWBUTTON, hInst, NULL);
SetWindowSubclass(hWndStatic, &OwnerDrawButtonProc, IDC_OWNERDRAWBUTTON, 0);
return TRUE;
}
break;
Finally, add the callback function of the subclass control.
TCHAR szText[] = _T("TestString");
LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_PAINT:
{
RECT rc;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rc);
SetTextColor(hdc, RGB(255, 0, 255));
DrawText(hdc, szText, _tcslen(szText), &rc, DT_CENTER | DT_VCENTER);
EndPaint(hWnd, &ps);
return 0;
}
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &OwnerDrawButtonProc, 1);
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
Note: Don't forget to declare the newly created function
Complete example:
// Test_SetWindowSubclass.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "Test_SetWindowSubclass.h"
#include <commctrl.h>
#pragma comment(lib,"Comctl32.lib")
#define MAX_LOADSTRING 100
#define IDC_OWNERDRAWBUTTON 101
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
HWND hWndStatic;
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_TESTSETWINDOWSUBCLASS, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTSETWINDOWSUBCLASS));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTSETWINDOWSUBCLASS));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_TESTSETWINDOWSUBCLASS);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
hWndStatic = CreateWindowEx(0, L"Static", NULL, WS_CHILD | WS_VISIBLE | SS_LEFT, 10, 130, 200, 40, hWnd, (HMENU)IDC_OWNERDRAWBUTTON, hInst, NULL);
SetWindowSubclass(hWndStatic, &OwnerDrawButtonProc, IDC_OWNERDRAWBUTTON, 0);
return TRUE;
}
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
TCHAR szText[] = _T("TestString");
LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_PAINT:
{
RECT rc;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rc);
SetTextColor(hdc, RGB(255, 0, 255));
DrawText(hdc, szText, _tcslen(szText), &rc, DT_CENTER | DT_VCENTER);
EndPaint(hWnd, &ps);
return 0;
}
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &OwnerDrawButtonProc, 1);
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
Debug:
After you understand the process of subclassing controls, you can review the comment Remy gave you.
Safer subclass control.
So my task is to create 5 child windows and when key is pressed close windows in other order. But I have a problem, when I close first 2 windows the other ones become inactive so I have to click on it so it could be focused window, but how can I prevent other windows to become inactive?
#include "stdafx.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
int windowsCount = 1;
WCHAR buffer[5];
HWND windows[5];
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
srand(unsigned(time(NULL)));
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_OOP10, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow))
return FALSE;
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_OOP10));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0)) {
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance) {
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_OOP10));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_OOP10);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, L"Main Window", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
SetClassLong(hWnd, GCL_HBRBACKGROUND, (LONG)CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256)));
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
windows[0] = hWnd;
return TRUE;
}
BOOL CreateChildWindow(HWND hWnd) {
_itoa(windowsCount + 1, (char*)buffer, 10);
SetClassLong(hWnd, GCL_HBRBACKGROUND, (LONG)CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256)));
HWND childWnd = CreateWindow(szWindowClass, buffer, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, windows[windowsCount - 1], nullptr, hInst, nullptr);
if (!childWnd)
return FALSE;
ShowWindow(childWnd, SW_SHOWDEFAULT);
UpdateWindow(childWnd);
windows[windowsCount++] = childWnd;
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_LBUTTONUP:
{
if (windowsCount < 5) {
if (!CreateChildWindow(hWnd))
return FALSE;
}
}
break;
case WM_CLOSE:
{
if (IDOK == MessageBox(hWnd, L"Are you sure you want to quit?", L"You are leaving the program", MB_OKCANCEL | MB_ICONINFORMATION | MB_DEFBUTTON2)) {
if (windowsCount == 1) {
DestroyWindow(windows[--windowsCount]);
PostQuitMessage(NULL);
break;
}
else
DestroyWindow(windows[--windowsCount]);
}
}
break;
case WM_RBUTTONUP:
SetWindowText(hWnd, L"Changed title!");
break;
case WM_KEYDOWN: {
if (windowsCount == 1) {
DestroyWindow(windows[--windowsCount]);
PostQuitMessage(NULL);
break;
}
else
DestroyWindow(windows[--windowsCount]);
}
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
/*case WM_DESTROY:
PostQuitMessage(0);
break;*/
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
UNREFERENCED_PARAMETER(lParam);
switch (message) {
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
This should be part of your application logic. Add event handler to the WM_CLOSE message to all your windows and set focus to the appropriate window that is not closed yet.
Windows cannot decide this for you.