I have simple application with a native winapi window, and a static control on it as a child window. I'd like to draw on the child window with Direct2D.
The drawing happens outside of the WM_PAINT message, so I decided to use ID2D1DCRenderTarget. Everything works except one thing. I can't make the drawing visible when the application starts.
If I do painting in any event (for example in WM_LBUTTONDOWN) the drawing shows up. But if I do painting in WM_CREATE I see nothing on the screen.
Here is my example code:
#include <iostream>
#include <windows.h>
#include <windowsx.h>
#include <d2d1.h>
#pragma comment(lib, "d2d1.lib")
HWND windowHandle = NULL;
HWND childWindowHandle = NULL;
ID2D1Factory* direct2DFactory = nullptr;
ID2D1DCRenderTarget* renderTarget = nullptr;
static void InitDirect2D ()
{
D2D1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED, &direct2DFactory);
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(
DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_IGNORE),
0,
0,
D2D1_RENDER_TARGET_USAGE_NONE,
D2D1_FEATURE_LEVEL_DEFAULT
);
direct2DFactory->CreateDCRenderTarget (&props, &renderTarget);
renderTarget->SetAntialiasMode (D2D1_ANTIALIAS_MODE_ALIASED);
}
static void Paint ()
{
ID2D1SolidColorBrush* blackBrush = nullptr;
renderTarget->CreateSolidColorBrush (
D2D1::ColorF (D2D1::ColorF::Black),
&blackBrush
);
renderTarget->BeginDraw ();
renderTarget->SetTransform (D2D1::Matrix3x2F::Identity ());
renderTarget->Clear (D2D1::ColorF (D2D1::ColorF::LightGray));
D2D1_RECT_F rect = D2D1::RectF (10.0f, 10.0f, 100.0f, 100.0f);
renderTarget->DrawRectangle (&rect, blackBrush);
renderTarget->EndDraw ();
}
static LRESULT CALLBACK ChildWindowProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_CREATE:
{
RECT rc;
GetClientRect (hwnd, &rc);
renderTarget->BindDC (GetDC (hwnd), &rc);
Paint ();
}
break;
case WM_LBUTTONDOWN:
Paint ();
break;
case WM_PAINT:
break;
case WM_SIZE:
{
RECT rc;
GetClientRect (hwnd, &rc);
renderTarget->BindDC (GetDC (hwnd), &rc);
Paint ();
}
break;
case WM_CLOSE:
DestroyWindow (hwnd);
break;
case WM_DESTROY:
PostQuitMessage (0);
break;
break;
}
LRESULT res = DefWindowProc (hwnd, msg, wParam, lParam);
return res;
}
static void CreateChildWindow (HWND parentHandle)
{
WNDCLASSEX windowClass;
memset (&windowClass, 0, sizeof (WNDCLASSEX));
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.style = 0;
windowClass.lpfnWndProc = ChildWindowProc;
windowClass.style = CS_DBLCLKS;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = NULL;
windowClass.hCursor = LoadCursor (NULL, IDC_ARROW);
windowClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = L"ChildWindowClass";
RegisterClassEx (&windowClass);
childWindowHandle = CreateWindowEx (
0, windowClass.lpszClassName, L"", WS_CHILD,
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, parentHandle, NULL, NULL, nullptr
);
ShowWindow (childWindowHandle, SW_SHOW);
UpdateWindow (childWindowHandle);
MoveWindow (childWindowHandle, 10, 10, 780, 580, TRUE);
}
static LRESULT CALLBACK MainWindowProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_CREATE) {
CreateChildWindow (hwnd);
InvalidateRect (hwnd, NULL, FALSE);
}
switch (msg) {
case WM_SIZE:
{
int newWidth = LOWORD (lParam);
int newHeight = HIWORD (lParam);
MoveWindow (childWindowHandle, 10, 10, newWidth - 20, newHeight - 20, TRUE);
}
break;
case WM_CLOSE:
DestroyWindow (hwnd);
break;
case WM_DESTROY:
PostQuitMessage (0);
break;
}
return DefWindowProc (hwnd, msg, wParam, lParam);
}
static int CreateMainWindow ()
{
WNDCLASSEX windowClass;
memset (&windowClass, 0, sizeof (WNDCLASSEX));
windowClass.cbSize = sizeof (WNDCLASSEX);
windowClass.style = 0;
windowClass.lpfnWndProc = MainWindowProc;
windowClass.style = CS_DBLCLKS;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = NULL;
windowClass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
windowClass.hCursor = LoadCursor (NULL, IDC_ARROW);
windowClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = L"WindowClass";
windowClass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
RegisterClassEx (&windowClass);
RECT requiredRect = { 0, 0, 800, 600 };
HWND windowHandle = CreateWindowEx (
WS_EX_WINDOWEDGE, windowClass.lpszClassName, L"Example", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, requiredRect.right - requiredRect.left, requiredRect.bottom - requiredRect.top, NULL, NULL, NULL, nullptr
);
if (windowHandle == NULL) {
return 1;
}
ShowWindow (windowHandle, SW_SHOW);
UpdateWindow (windowHandle);
MSG msg;
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
return 0;
}
int main (int argc, char* argv[])
{
InitDirect2D ();
return CreateMainWindow ();
}
Do you have any idea how can I make the drawing visible on application startup?
During WM_CREATE window is not complitely created yet so painting does not work. You should write a proper WM_PAINT (and WM_ERASEBKGND) message handlers and perform all the painting operations there. Then when you need to redraw as a response to any other message (such as WM_LBUTTONDOWN) you need to invalidate window and trigger WM_PAINT instead of immediately calling Paint();.
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 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'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.
I am currently making a small win32 window wrapper class, but I have a few problem.
If I hit the close(X) button of the window the window closes immediately without sending a quit or destroy message, so I can't for example prevent the window to close or save something before closing the window.
And the second problem/question is,
If I use this small code to use the window, the computer cpu gets strongly used.
But its only a small window.
How I can change/fix this?
int main()
{
glwCreate();
while(true/*Later here comes a method that checks, wether window close is requested*/)
{
glwUpdate();
}
glwDestroy();
return 0;
}
-
#include "glw.h"
#include <windows.h>
#include <iostream>
HINSTANCE instanceHandle;
WNDCLASSEX windowClass;
HWND windowHandle;
LRESULT CALLBACK WindowMessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
std::cout<<uMsg<<'\n';
switch(uMsg)
{
case WM_QUIT:
{
std::cout<<"QUIT\n";
return 0;
}
case WM_DESTROY:
{
std::cout<<"DESTROY\n";
return 0;
}
}
return (DefWindowProc(hWnd, uMsg, wParam, lParam));
}
void glwCreate()
{
instanceHandle = GetModuleHandle(0);
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = WindowMessageHandler;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = instanceHandle;
windowClass.hCursor = LoadCursor(0,IDC_ARROW);
windowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
windowClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
windowClass.lpszClassName = "atomus_window_class";
windowClass.lpszMenuName = "menu_name";
windowClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
RegisterClassEx(&windowClass);
windowHandle = CreateWindowEx( 0,
"atomus_window_class",
"atomus title",
WS_OVERLAPPEDWINDOW,
0,
0,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
instanceHandle,
0);
ShowWindow(windowHandle, SW_SHOW);
}
void glwDestroy()
{
DestroyWindow(windowHandle);
windowHandle = 0;
UnregisterClass(windowClass.lpszClassName, instanceHandle);
}
void glwUpdate()
{
MSG message;
while (PeekMessage (&message, 0, 0, 0, PM_REMOVE) > 0) //Or use an if statement
{
TranslateMessage (&message);
DispatchMessage (&message);
}
}
If you add handling for WM_CLOSE you get to control whether your window closes or not. By not providing your own handling for that message you get the default from DefWindowProc which is to destroy your window.