I've made two simple classes to use for displaying UIs with Awesomium. However creating the Awesomium WebView and setting the parent window to my Win32 window causes the program to hang and the page is never displayed. The classes are simple and creating a window is simple so there isn't much I can think of that could be going wrong. Perhaps there is something else required than what I've done to display a WebView?
To clarify: Creating the Win32 window without creating the WebView works fine, the window functions including the drag code etc... The hang only happens when you call set_parent_window.
UI.h
#pragma once
#include <Windows.h>
#include <windowsx.h>
#include <Awesomium/WebCore.h>
#include <Awesomium/STLHelpers.h>
using namespace Awesomium;
LRESULT CALLBACK LoginUICallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
class UI
{
public:
//Window Variables
HINSTANCE instance;
HWND window;
BOOL drag_window = false;
SHORT mouse_x, mouse_y, mouse_x_prev, mouse_y_prev;
//Awesomium Variables
WebCore* webcore = 0;
WebView* webview;
static HWND InitWindow(INT width, INT height, WNDPROC callback)
{
HWND hwnd;
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = callback;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(0);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "MyUI";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
char msg[100];
sprintf(msg, "System Error: %i", GetLastError());
MessageBox(NULL, msg, "ERROR", MB_OK);
return NULL;
}
hwnd = CreateWindow("MyUI",
"",
WS_POPUP,
CW_USEDEFAULT,
CW_USEDEFAULT,
width,
height,
NULL,
NULL,
GetModuleHandle(0),
NULL);
if (!hwnd)
{
char msg[100];
sprintf(msg, "System Error: %i", GetLastError());
MessageBox(NULL, msg, "ERROR", MB_OK);
return NULL;
}
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
SetTimer(hwnd, 0, 15, NULL);
return hwnd;
}
};
class LoginUI : public UI
{
public:
INT width = 600;
INT height = 600;
INT RunUI()
{
this->window = UI::InitWindow(this->width, this->height, ::LoginUICallback);
if (!this->window)
return 0;
WebConfig config;
this->webcore = WebCore::Initialize(config);
this->webview = this->webcore->instance()->CreateWebView(this->width, this->height, 0, kWebViewType_Window);
this->webview->set_parent_window(this->window);
this->webview->LoadURL(WebURL(WSLit("http://www.google.com")));
MSG msg;
while(GetMessage(&msg, this->window, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
WebCore::Shutdown();
return 1;
}
}login_ui;
LRESULT CALLBACK LoginUICallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
case WM_TIMER:
break;
case WM_MOUSEMOVE:
{
if (login_ui.drag_window && (wParam & MK_LBUTTON))
{
// code executed when the dialog window is moved around on the screen
RECT win_rect;
GetWindowRect(hWnd, &win_rect);
int x_coord = GET_X_LPARAM(lParam);
int y_coord = GET_Y_LPARAM(lParam);
MoveWindow(hWnd,
win_rect.left + x_coord - login_ui.mouse_x_prev,
win_rect.top + y_coord - login_ui.mouse_y_prev,
win_rect.right - win_rect.left,
win_rect.bottom - win_rect.top,
false
);
}
break;
}
case WM_LBUTTONDOWN:
{
login_ui.mouse_x = GET_X_LPARAM(lParam);
login_ui.mouse_y = GET_Y_LPARAM(lParam);
if (login_ui.mouse_y < 41)
{
login_ui.mouse_x_prev = login_ui.mouse_x;
login_ui.mouse_y_prev = login_ui.mouse_y;
SetCapture(hWnd);
login_ui.drag_window = true;
}
break;
}
case WM_LBUTTONUP:
{
if (login_ui.drag_window)
{
login_ui.drag_window = false;
ReleaseCapture();
}
break;
}
case WM_SIZE:
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_QUIT:
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Main.Cpp
#include "UI.h"
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, char*, int nCmdShow)
{
login_ui.RunUI();
}
Have not used Awesomium but your GetMessage only pumps messages for the WebCore window. So instead you should pass NULL so your message pump dispatches messages for all windows created on that thread.
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.
First of all, this question can be a duplicate but the question doesnt have enough information to solve the problem.
I have two windows in my native Win32 application. The first is a layered window with WS_EX_NOACTIVATE extended style and the second is a normal window. I want the layered one to be non-activatable. The problem is that, when I have two window in the same application, the layered one which must be non-activatable, gets activated when switching between them. But there is no problem when switching between two external windows, one being not belong to my application. How can I solve this problem? Or Can I solve that? Is there anything I missed? The following is a minimal reproducable example (didn't include any error checking for minimality.) Thank you for taking time.
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <windowsx.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void init_device_resources(int cx, int cy, void** rgb);
void update_content();
void set_window_size(int width, int height);
HWND layeredhWnd;
HWND otherhWnd;
WNDCLASSEX wc;
MSG msg;
HDC hdcDesktop;
HDC hdcContent;
POINT dstPoint = { 100, 100 };
SIZE windowSize = { 800, 600 };
BLENDFUNCTION bf;
BITMAPINFO bi;
BYTE* rgb_data = NULL;
HBITMAP hBitmap;
HBITMAP hOldBitmap;
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = L"LayeredWindowClass";
RegisterClassEx(&wc);
wc.lpszClassName = L"OtherWindowClass";
RegisterClassEx(&wc);
layeredhWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_NOACTIVATE,
L"LayeredWindowClass",
L"",
WS_POPUP | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME,
0, 0,
800, 600,
NULL,
NULL,
hInstance,
NULL);
otherhWnd = CreateWindowEx(NULL,
L"OtherWindowClass",
L"",
WS_OVERLAPPEDWINDOW,
0, 0,
800, 600,
NULL,
NULL,
hInstance,
NULL);
init_device_resources(800, 600, &rgb_data);
set_window_size(800, 600);
ShowWindow(layeredhWnd, nCmdShow);
ShowWindow(otherhWnd, nCmdShow);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
{
DestroyWindow(hWnd);
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
case WM_ACTIVATE:
{
if(hWnd == layeredhWnd)
update_content();
break;
}
case WM_PAINT:
{
if (hWnd == layeredhWnd)
update_content();
break;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
void init_device_resources(int cx, int cy, void** rgb)
{
hdcDesktop = GetDC(NULL);
hdcContent = CreateCompatibleDC(hdcDesktop);
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 255;
bf.AlphaFormat = AC_SRC_ALPHA;
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = cx;
bi.bmiHeader.biHeight = -cy;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biSizeImage = cx * cy * 4;
bi.bmiHeader.biXPelsPerMeter = 0;
bi.bmiHeader.biYPelsPerMeter = 0;
bi.bmiHeader.biClrUsed = 0;
bi.bmiHeader.biClrImportant = 0;
bi.bmiColors[0] = (RGBQUAD){ 0 };
hBitmap = CreateDIBSection(hdcContent, &bi, DIB_RGB_COLORS, rgb, NULL, 0);
for (int i = 0; i < cx * cy * 4; i++)
{
rgb_data[i] = 255;
}
hOldBitmap = SelectObject(hdcContent, hBitmap);
}
void update_content()
{
UpdateLayeredWindow(layeredhWnd, hdcDesktop, &dstPoint,
&windowSize, hdcContent, &(POINT){ 0, 0 }, RGB(0, 0, 0), & bf, ULW_ALPHA);
}
void set_window_size(int width, int height)
{
SetWindowPos(layeredhWnd, NULL, 0, 0, width, height, SWP_NOMOVE);
windowSize = (SIZE){ width, height };
}
Although I still don't understand the cause of the problem, with the help of #IInspectable's guidance and documentation, I was able to prevent the window from being activated by processing the WM_MOUSEACTIVATE message. The updated window procedure is as follows.
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
{
DestroyWindow(hWnd);
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
case WM_MOUSEACTIVATE:
{
if (hWnd == layeredhWnd)
return MA_NOACTIVATE;
break;
}
case WM_ACTIVATE:
{
if(hWnd == layeredhWnd)
update_content();
break;
}
case WM_PAINT:
{
if (hWnd == layeredhWnd)
update_content();
break;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
I'd like to create a new POPUP style window in a new thread. Here is the code I have so far.
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
#include <tchar.h>
#include <thread>
#include <string>
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;
using namespace std;
const wchar_t g_szClassName[] = L"Skeleton";
const wchar_t g_szChildClassName[] = L"Child";
wchar_t msgbuf[100];
char msgbuf_ansi[100];
WNDCLASSEX wc;
struct MyStruct
{
WNDCLASSEX wc;
HWND hWnd;
HINSTANCE hInst;
int nCmdShow;
};
MyStruct g_myStruct;
LRESULT CALLBACK WndProcChild(HWND, UINT, WPARAM, LPARAM);
void Example_DrawImage9(HDC hdc) {
Graphics graphics(hdc);
Image image(L"C:/Users/Darek/Fallout2_older/data/art/iface/armor_info.bmp");
graphics.DrawImage(&image, 0, 0);
}
int task1(MyStruct myStruct)
{
sprintf_s(msgbuf_ansi, ("thread\n"));
OutputDebugStringA(msgbuf_ansi);
HWND hwnd_child;
myStruct.wc.lpfnWndProc = WndProcChild;
myStruct.wc.lpszClassName = g_szChildClassName;
if (!RegisterClassEx(&myStruct.wc)) {
MessageBox(NULL, L"thread - Window Registration Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
hwnd_child = CreateWindowEx(0, g_szChildClassName, L"Child", WS_POPUP | WS_BORDER, 200, 0, 190, 110, myStruct.hWnd, 0, myStruct.hInst, 0);
swprintf_s(msgbuf, _T("THREAD - CHILD - hwnd: %02X\n"), (int)hwnd_child);
OutputDebugString(msgbuf);
if (hwnd_child == NULL) {
MessageBox(NULL, L"thread - Window Creation Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
SetWindowLong(hwnd_child, GWL_EXSTYLE, GetWindowLong(hwnd_child, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hwnd_child, 0, 128, LWA_ALPHA);
ShowWindow(hwnd_child, myStruct.nCmdShow);
UpdateWindow(hwnd_child);
}
thread t1;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
//case WM_KEYDOWN:
case WM_CLOSE:
swprintf_s(msgbuf, _T("WM_CLOSE - PARENT \n"));
OutputDebugString(msgbuf);
if (MessageBox(hwnd, L"Really quit?", L"My application", MB_OKCANCEL) == IDOK)
{
DestroyWindow(hwnd);
}
return 0;
case WM_DESTROY:
swprintf_s(msgbuf, _T("WM_DESTROY - PARENT \n"));
OutputDebugString(msgbuf);
PostQuitMessage(0);
return 0;
case WM_CREATE: {
swprintf_s(msgbuf, _T("WM_CREATE - PARENT \n"));
OutputDebugString(msgbuf);
thread t1(task1, g_myStruct);
t1.join();
return 0;
}
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int g_fMouseTracking = FALSE;
LRESULT CALLBACK WndProcChild(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_PAINT:
HDC hdc;
PAINTSTRUCT ps;
swprintf_s(msgbuf, _T("WM_PAINT - CHILD - hwnd: %02X\n"), (int)hwnd);
OutputDebugString(msgbuf);
hdc = BeginPaint(hwnd, &ps);
Example_DrawImage9(hdc);
EndPaint(hwnd, &ps);
return 0;
//case WM_KEYDOWN:
case WM_CREATE: {
swprintf_s(msgbuf, _T("WM_CREATE - CHILD \n"));
OutputDebugString(msgbuf);
return 0;
}
case WM_MOUSEMOVE:
swprintf_s(msgbuf, _T("WM_MOUSEMOVE - CHILD - hwnd: %02X\n"), (int)hwnd);
OutputDebugString(msgbuf);
if (!g_fMouseTracking)
{
// start tracking if we aren't already
TRACKMOUSEEVENT tme = {};
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_HOVER | TME_LEAVE;
tme.hwndTrack = hwnd;
tme.dwHoverTime = HOVER_DEFAULT;
g_fMouseTracking = TrackMouseEvent(&tme);
}
return 0;
case WM_MOUSELEAVE:
swprintf_s(msgbuf, _T("WM_MOUSELEAVE - CHILD - hwnd: %02X\n"), (int)hwnd);
OutputDebugString(msgbuf);
g_fMouseTracking = FALSE; // tracking now canceled
return 0;
case WM_CLOSE:
swprintf_s(msgbuf, _T("WM_CLOSE - CHILD \n"));
OutputDebugString(msgbuf);
/*if (MessageBox(hwnd, L"Really quit?", L"My application", MB_OKCANCEL) == IDOK)
{
DestroyWindow(hwnd);
}*/
return 0;
case WM_DESTROY:
swprintf_s(msgbuf, _T("WM_DESTROY - CHILD \n"));
OutputDebugString(msgbuf);
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
//WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
ULONG_PTR token;
GdiplusStartupInput input = { 0 };
input.GdiplusVersion = 1;
GdiplusStartup(&token, &input, NULL);
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
g_myStruct.wc = wc;
g_myStruct.hInst = hInstance;
g_myStruct.nCmdShow = nCmdShow;
if (!RegisterClassEx(&wc)) {
MessageBox(NULL, L"Window Registration Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
hwnd = CreateWindowEx(0, g_szClassName, L"Skeleton", WS_BORDER, 0, 0, 190, 110, 0, 0, hInstance, 0);
g_myStruct.hWnd = hwnd;
if (hwnd == NULL) {
MessageBox(NULL, L"Parent Window Creation Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
swprintf_s(msgbuf, _T("MAIN - PARENT - hwnd: %02X\n"), (int)hwnd);
OutputDebugString(msgbuf);
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hwnd, 0, 128, LWA_ALPHA);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&Msg, NULL, 0, 0) > 0) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
The problems is that as I've debugged the child window is indeed created but automatically/magically destroyed when the WndProcChild returns.
How to correct the code to have it run as expected (the child window stays open until the main is't destroyed)?
The thread own the window it had created and its message queue and therefore must provide an event loop. It's destroyed because in Win API ownership is treated in RAII way - when owner ceases to exist, so do the acquired objects.
I have my window file (Window.h):
LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);
class Window
{
private:
HWND hWnd;
HINSTANCE hInstance;
bool running = true;
const char* ID = "WINAPI_JVM64";
public:
Window()
{
init();
}
virtual void draw(Gdiplus::Graphics*) = 0;
void init()
{
hInstance = (HINSTANCE)GetModuleHandle(NULL);
WNDCLASS wc;
wc = {};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MessageHandler;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_HAND);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = ID;
assert(RegisterClass(&wc));
hWnd = CreateWindow(ID, "Title", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
200, 200, 400, 400, NULL, NULL, hInstance, NULL);
ShowCursor(true);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
}
void run()
{
MSG msg;
PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE);
while(running)
{
if(PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT)
running = false;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Here, the draw function is called.
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
Gdiplus::Graphics* g = Gdiplus::Graphics::FromHDC(hdc);
draw(g);
EndPaint(hWnd, &ps);
}
}
UnregisterClass(ID, hInstance);
}
};
And the main file (main.cpp):
#include "Window.h"
LRESULT CALLBACK MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
class AppWindow : public Window
{
public:
void draw(Gdiplus::Graphics* g) override
{
Gdiplus::SolidBrush brown_brush(Gdiplus::Color(255, 128, 57, 0));
g->FillRectangle(&brown_brush, 0, 0, 200, 200);
}
};
int main()
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
AppWindow w;
w.run();
Gdiplus::GdiplusShutdown(gdiplusToken);
return 0;
}
I have the problem that it just won't draw!
It handles every message, everything is good, but it doesn't draw. Even messages of type WM_PAINT are sent, but nothing happens.
Can you spot the problem?
I just want a window class that has an overridable draw() function with a run() function that handles all events, such as WM_LBUTTONDOWN. All of that works fine, the screen just stays blank.
Also, I can't close the window, when pressing the X button in the top-right corner, the window just stays; only after resizing and quickly pressing X, it closes.
As you can see, I have some pretty weird behaviour, and I don't know what the problem is.
Your drawing logic is in the wrong place. It needs to be inside the MessageHandler when processing a WM_PAINT message. PeekMessage() will generate a WM_PAINT message if the window needs to be drawn and no other messages are pending. You can't draw on the window from outside of a WM_PAINT handler.
Also, you are assigning the wrong value to wc.hbrBackground in init(). If you use a color constant like COLOR_WINDOW, you need to add 1 to it. This is stated as much in the WNDCLASS documentation.
Also, in run(), your 1st PeekMessage() to create the message queue is discarding an initial message if one is pending, that message does not get processed by your dispatch loop. That 1st call should be using the PM_NOREMOVE flag instead.
Also, be aware of the dangers of filtering window messages in your message loop.
With that said, try this instead:
LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);
class Window
{
private:
HWND hWnd;
HINSTANCE hInstance;
const char* ID = "WINAPI_JVM64";
public:
Window()
{
init();
}
~Window()
{
cleanup();
}
virtual void draw(Gdiplus::Graphics*) = 0;
void init()
{
hInstance = (HINSTANCE)GetModuleHandle(NULL);
WNDCLASS wc{};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = &MessageHandler;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_HAND);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = ID;
assert(RegisterClass(&wc));
hWnd = CreateWindow(ID, "Title", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
200, 200, 400, 400, NULL, NULL, hInstance, this);
assert(hWnd != NULL);
ShowCursor(true);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
}
void cleanup()
{
UnregisterClass(ID, hInstance);
}
void run()
{
MSG msg;
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
};
#include "Window.h"
LRESULT CALLBACK MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCCREATE:
{
Window *pThis = static_cast<Window*>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
break;
}
// DefWindowProc(WM_CLOSE) calls DestroyWindow(),
// WM_CLOSE is not the right place to call PostQuitMessage()...
//case WM_CLOSE:
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
{
Window *pThis = reinterpret_cast<Window*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
if (pThis)
{
Gdiplus::Graphics* g = Gdiplus::Graphics::FromHDC(hdc);
pThis->draw(g);
delete g;
}
EndPaint(hWnd, &ps);
return 0;
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
class AppWindow : public Window
{
public:
void draw(Gdiplus::Graphics* g) override
{
Gdiplus::SolidBrush brown_brush(Gdiplus::Color(255, 128, 57, 0));
g->FillRectangle(&brown_brush, 0, 0, 200, 200);
}
};
int main()
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
AppWindow w;
w.run();
Gdiplus::GdiplusShutdown(gdiplusToken);
return 0;
}
I'm trying to create a second window if a dialog box from my first window returns a certain way. Unfortunately, if I call CreateWindowEx() from within the callback process of the first window, it doesn't recognise the second window's HWND. I also tried defining the first window in WinMain and making it visible within the callback, but it failed to recognise the HWND once again. Am I trying to do this wrong, or am I just missing something?
Edit:
#include "stdafx.h"
#include "md5.h"
#include "Coursework.h"
#include "accounts.h"
const char g_szClassName[] = "myMainWindow";
const char g_szLoggedInClass[] = "loggedInWindow";
#define IDC_CREATE_ACCOUNT 101
#define IDC_LOG_IN 110
MYSQL *conn;
static std::string strUserName;
BOOL CALLBACK CreateDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
{
CreateAccount(hwnd);
}
break;
case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;
}
default:
return FALSE;
}
}
BOOL CALLBACK LogInDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;
case IDOK:
{
if(bLogIn(hwnd, strUserName))
{
EndDialog(hwnd, IDOK);
}
}
break;
}
break;
default:
return FALSE;
}
}
LRESULT CALLBACK LoggedInProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_CREATE:
{
if (*mysql_error(conn)) //Checks if the connection succeeded, and
{ //exits the program if false.
MessageBox(hwnd, "No connection to the server could be established.\r\nPlease contact your administrator.", "Error", MB_ICONERROR);
exit(-1);
}
HWND hCreateButton = CreateWindowEx(NULL, "Button", "Create account", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON, 500, 250, 120, 24, hwnd, (HMENU)IDC_CREATE_ACCOUNT, GetModuleHandle(NULL), NULL);
HWND hLogInButton = CreateWindowEx(NULL, "Button", "Log in", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON, 500, 210, 120, 24, hwnd, (HMENU)IDC_LOG_IN, GetModuleHandle(NULL), NULL);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_CREATE_ACCOUNT:
{
int ret = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), hwnd, CreateDlgProc);
if (ret == -1)
{
MessageBox(hwnd, "Dialog failed to appear", "Error", MB_OK | MB_ICONINFORMATION);
}
}
break;
case IDC_LOG_IN:
{
int ret = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG2), hwnd, LogInDlgProc);
if(ret == -1)
{
MessageBox(hwnd, "Dialog failed to appear", "Error", MB_OK | MB_ICONINFORMATION);
}
else if(ret == IDOK)
{
ShowWindow(hwnd, SW_HIDE);
}
}
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
WNDCLASSEX wc2;
HWND hwnd;
HWND hLoggedIn;
MSG Msg;
conn = mysql_init(NULL); //Initialises mySQL connection
mysql_real_connect(conn, "localhost", "root", "SecurityRules", "coursework", 0, NULL, 0);
//Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
//Registering the second Window Class
wc2.cbSize = sizeof(WNDCLASSEX);
wc2.style = 0;
wc2.lpfnWndProc = LoggedInProc;
wc2.cbClsExtra = 0;
wc2.cbWndExtra = 0;
wc2.hInstance = hInstance;
wc2.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc2.hCursor = LoadCursor(NULL, IDC_ARROW);
wc2.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc2.lpszMenuName = NULL;
wc2.lpszClassName = g_szLoggedInClass;
wc2.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!\r\nContact your administrator.", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
if(!RegisterClassEx(&wc2))
{
MessageBox(NULL, "Window Registration failed!\r\nContact your administrator.", "Error!", MB_ICONEXCLAMATION|MB_OK);
return 0;
}
//Creating the main Window
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, g_szClassName, "Coursework", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 1080, 640, NULL, NULL, hInstance, NULL);
hLoggedIn = CreateWindowEx(WS_EX_CLIENTEDGE, g_szLoggedInClass, "Coursework", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 1080, 640, NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!\r\nContact your adminstrator.", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// The Message Loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
mysql_close(conn);
return Msg.wParam;
}
}
Edit 2:
Intellisense (I'm in Visual Studio) states that 'identifier hLoggedIn is undefined', as well as nCmdShow.