In my project i have created a simple Button class:
button.hpp:
class Button : public Control {
std::string text;
int id;
std::function<void(void)>&& callback{};
static inline const char* sClassName = "Button";
static LRESULT CALLBACK ButtonSubclassProc(HWND, UINT, WPARAM, LPARAM, UINT_PTR, DWORD_PTR);
public:
Button(HWND parent, POINT position, SIZE size, int id, const std::string& text) noexcept;
virtual ~Button() noexcept;
inline void SetCallback(std::function<void(void)>&& callback) noexcept { this->callback = callback; }
NODISCARD inline int GetId() const noexcept { return id; }
private:
NODISCARD bool CreateControl(HWND) noexcept override;
void DestroyControl() noexcept override;
void Invoke() const noexcept;
};
button.cpp:
#include "button.hpp"
Button::Button(HWND parent, POINT position, SIZE size, int id, const std::string& text) noexcept
: Control(position, size), id(id), text(text) {
isCreated = CreateControl(parent);
SetWindowSubclass(hwnd, &Button::ButtonSubclassProc, id, reinterpret_cast<DWORD_PTR>(this));
}
Button::~Button() noexcept {
RemoveWindowSubclass(hwnd, &Button::ButtonSubclassProc, id);
DestroyControl();
}
NODISCARD bool Button::CreateControl(HWND parent) noexcept {
hwnd = CreateWindow(sClassName, text.c_str(), WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, position.x, position.y, size.cx, size.cy, parent, reinterpret_cast<HMENU>(id), reinterpret_cast<HINSTANCE>(GetWindowLong(parent, GWL_HINSTANCE)), NULL);
return hwnd != NULL;
}
void Button::DestroyControl() noexcept {
Control::DestroyControl();
}
void Button::Invoke() const noexcept {
if (callback) {
callback();
}
}
LRESULT CALLBACK Button::ButtonSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR id, DWORD_PTR refData) {
auto button = reinterpret_cast<Button*>(refData);
if (uMsg == WM_COMMAND) {
MessageBox(0, std::to_string(id).c_str(), 0, 0);
button->Invoke();
return TRUE;
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
that's how i pass message from main window procedure:
case WM_COMMAND: {
return SendMessage(reinterpret_cast<HWND>(lParam), WM_COMMAND, wParam, lParam);
}
And it works fine when only one button is created, but when i try to create more, it does not matter which button i would push, i always get callback of the last created button.
I suppose this happens because of SetWindowSubclass call in counstructor and passing it this pointer, but i do not know what should i do. Read about GetWindowSubclass but i dont clearly understand how to use it.
Buttons are created in main window class:
mainwnd.hpp
class MainWindow : public NonCopyable, public NonMovable {
HINSTANCE handle;
HWND hwnd;
int width;
int height;
bool isRegistered{ false };
bool isCreated{ false };
IRenderer* renderer;
Button* buttonStart;
Button* buttonStop;
static inline const char* sClassName = "MAIN_WINDOW_CLASS";
static LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
public:
explicit MainWindow(HINSTANCE handle, int width, int height) noexcept;
~MainWindow() noexcept;
NODISCARD inline bool IsRegistered() const noexcept { return isRegistered; }
NODISCARD inline bool IsCreated() const noexcept { return isCreated; }
NODISCARD bool CheckControls() const noexcept;
NODISCARD inline int Width() const noexcept { return width; }
NODISCARD inline int Height() const noexcept { return height; }
void Update() noexcept;
private:
NODISCARD bool RegisterMainClass() const noexcept;
NODISCARD bool CreateMainWindow() noexcept;
void UnregisterMainClass() noexcept;
void DestroyMainWindow() noexcept;
void CreateControls() noexcept;
void DestroyControls() noexcept;
void OnButtonStartPushed();
void OnButtonStopPushed();
};
mainwnd.cpp
#include "main_window.hpp"
MainWindow::MainWindow(HINSTANCE handle, int width, int height) noexcept
: width(width), height(height), handle(handle) {
isRegistered = RegisterMainClass();
isCreated = CreateMainWindow();
CreateControls();
}
MainWindow::~MainWindow() noexcept {
DestroyControls();
DestroyMainWindow();
UnregisterMainClass();
}
bool MainWindow::RegisterMainClass() const noexcept {
WNDCLASS wc;
memset(&wc, 0, sizeof(WNDCLASSA));
if (!GetClassInfo(handle, sClassName, &wc)) {
wc.style = 0;
wc.hInstance = handle;
wc.lpszClassName = sClassName;
wc.lpfnWndProc = WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(LONG_PTR);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.lpszMenuName = NULL;
}
return RegisterClass(&wc) != 0;
}
bool MainWindow::CreateMainWindow() noexcept {
if (IsRegistered()) {
hwnd = CreateWindow(sClassName, NULL, WS_VISIBLE | WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, handle, this);
}
return hwnd != NULL;
}
void MainWindow::UnregisterMainClass() noexcept {
UnregisterClass(sClassName, handle);
isRegistered = false;
}
void MainWindow::DestroyMainWindow() noexcept {
if (hwnd) {
DestroyWindow(hwnd);
hwnd = NULL;
}
isCreated = false;
}
void MainWindow::CreateControls() noexcept {
this->renderer = new Canvas(hwnd, { 10,10 }, { 200,200 });
buttonStart = new Button(hwnd, { 300,50 }, { 100,40 }, 145, "Start");
buttonStart->SetCallback(std::bind(&MainWindow::OnButtonStartPushed, this));
buttonStop = new Button(hwnd, { 300,150 }, { 100,40 }, 168, "Stop");
buttonStop->SetCallback(std::bind(&MainWindow::OnButtonStopPushed, this));
}
void MainWindow::DestroyControls() noexcept {
delete renderer;
delete buttonStart;
delete buttonStop;
}
bool MainWindow::CheckControls() const noexcept {
return renderer != 0;
}
void MainWindow::Update() noexcept {
renderer->Redraw();
}
void MainWindow::OnButtonStartPushed() {
MessageBox(0, "Start", 0, 0);
}
void MainWindow::OnButtonStopPushed() {
MessageBox(0, "Stop", 0, 0);
}
LRESULT CALLBACK MainWindow::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (uMsg == WM_NCCREATE) {
MainWindow* window = reinterpret_cast<MainWindow*>(lParam);
SetWindowLongPtr(hwnd, GWL_USERDATA, reinterpret_cast<LONG_PTR>(window));
return TRUE;
}
auto window = reinterpret_cast<MainWindow*>(GetWindowLongPtr(hwnd, GWL_USERDATA));
if (window == nullptr) {
return FALSE;
}
switch (uMsg) {
case WM_DESTROY: {
PostQuitMessage(0);
return 0;
}
case WM_COMMAND: {
return SendMessage(reinterpret_cast<HWND>(lParam), WM_COMMAND, wParam, lParam);
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return TRUE;
}
Message Loop:
MSG message;
memset(&message, 0, sizeof(MSG));
while (true) {
if (PeekMessage(&message, 0, 0, 0, PM_REMOVE) != 0) {
if (message.message == WM_QUIT) {
break;
}
TranslateMessage(&message);
DispatchMessage(&message);
} else {
mainWindow.Update();
}
}
Related
I'm making a library with raw Win32 API. My problem is, when I make an object of my child window classes, I get an error that says:
Access violation reading location 0xFFFFFFFFFFFFFFFF
I have tried many solutions, but they didn't work for me.
Also, I know that I can't access my class members in the HandleMessage() function inside the Text class (one of the child classes) because they're filled with some random garbage. How can I fix this issue?
Here's the Text.h file:
class IText
{
protected:
virtual void onPaint(Event) = 0;
virtual void onClick(Event) = 0;
};
class Text : public Component, IText, IEventListener
{
public:
Text();
Text(const std::string& text, const Style& style, const handleWindow_t& parent);
operator const std::string () const;
public:
const std::wstring& getComponentClassName() const;
const handleWindow_t& getHandleWindow() const;
public:
void onPaint(Event e) override;
void onClick(Event e) override;
public:
void addEventListener(const std::string& eventType, const std::function<void(Event)>& callbackFn) override;
public:
static LRESULT CALLBACK HandleMsgSetup(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
Text* self = nullptr;
if (uMsg == WM_NCCREATE)
{
LPCREATESTRUCT pCreate = reinterpret_cast<LPCREATESTRUCT>(lParam);
self = static_cast<Text*>(pCreate->lpCreateParams);
self->m_Hwnd = hWnd;
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(self));
}
else
{
self = reinterpret_cast<Text*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
if (self)
{
return self->HandleMessage(uMsg, wParam, lParam);
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
Event e;
e.code = uMsg;
e.spec = wParam;
e.data = lParam;
e.component = m_Hwnd;
e.target = this;
switch (uMsg)
{
case WM_PAINT:
{
onPaint(e);
}
return 0;
case WM_LBUTTONDOWN:
{
onClick(e);
}
return 0;
}
return DefWindowProc(m_Hwnd, uMsg, wParam, lParam);
}
public:
handleWindow_t m_Hwnd;
private:
Style m_Style;
handleWindow_t m_Parent;
std::wstring m_Text, m_ClassName = make_ClassName(L"Text");
};
Here's the Text.cpp file:
Text::Text() : m_Style({0}), m_Text(L""), m_Parent(nullptr)
{
}
Text::Text(const std::string& text, const Style& style, const handleWindow_t& parent) : m_Text(to_wstring(text)), m_Style(style), m_Parent(parent)
{
WNDCLASS wc = { 0 };
wc.style = CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = HandleMsgSetup;
wc.hInstance = GetModuleHandle(nullptr);
wc.lpszClassName = m_ClassName.c_str();
if (!RegisterClass(&wc))
DWORD ret = GetLastError();
if (!(m_Hwnd = CreateWindowEx(
0,
m_ClassName.c_str(),
L"",
WS_CHILD | WS_VISIBLE,
m_Style.marginX, m_Style.marginY, m_Style.width, m_Style.height,
parent,
nullptr,
GetModuleHandle(nullptr), this
)))
printf("%s", "error\n");
}
Text::operator const std::string () const
{
return to_string(m_Text);
}
const std::wstring& Text::getComponentClassName() const
{
return m_ClassName;
}
const handleWindow_t& Text::getHandleWindow() const
{
return m_Hwnd;
}
void Text::onPaint(Event e)
{
PAINTSTRUCT ps;
handleDeviceContext_t hdc = BeginPaint(m_Hwnd, &ps);
FillRect(hdc, &ps.rcPaint, Brush(m_Style.backgroundColor));
HFONT font = CreateFontA(20, 0, 0, 0, FW_NORMAL, false, false, false, DEFAULT_CHARSET,
0, 0, 0,
0, "Sans Serif" // Or Segoe UI
);
SelectObject(hdc, font);
SetTextColor(hdc, m_Style.color);
if (m_Style.backgroundColor == transparent)
SetBkMode(hdc, TRANSPARENT);
else
SetBkColor(hdc, m_Style.backgroundColor);
DrawText(hdc, m_Text.c_str(), -1, &ps.rcPaint, DT_NOCLIP);
EndPaint(m_Hwnd, &ps);
}
void Text::addEventListener(const std::string& eventType, const std::function<void(Event)>& callbackFn)
{
if (eventType == "click")
m_ClickCallback = callbackFn;
}
void Text::onClick(Event e)
{
m_ClickCallback(e);
}
And here's the main.cpp:
void init(handleWindow_t window)
{
Style txtStyle = { 0 };
txtStyle.width = 100;
txtStyle.height = 22;
txtStyle.color = RGB(0, 100, 255);
txtStyle.backgroundColor = 0;
Text text("text", txtStyle, window);
text.addEventListener("click", [](Event e)
{
std::cout << "Clicked\n";
});
}
int main()
{
Style appStyle = { 0 };
appStyle.width = 800;
appStyle.height = 600;
appStyle.marginX = 100;
appStyle.marginY = 100;
appStyle.backgroundColor = 0xffffff;
Window window("Sandbox", appStyle);
window.show();
init(window);
while (window.running())
{
}
return 0;
}
The reason why I was getting that error was the that Text object was destructed when init function scope ends and the solution for that is to make pointer to that object so the Text object is not destroyed when the init function returns.
Here's the code
void init(handleWindow_t window)
{
Style txtStyle = { 0 };
txtStyle.width = 100;
txtStyle.height = 22;
txtStyle.color = RGB(0, 100, 255);
txtStyle.backgroundColor = 0;
Text* text = new Text("text", txtStyle, window);
text->addEventListener("click", [](Event e)
{
std::cout << "Clicked\n";
});
}
I have been able to download and build the sample projects from Microsoft. I can run the Win32 project and it displays a WebView2 object in the View and appears functional.
For my situation I want to use a CDialog as the parent for the WebView2 control and I can't work out how to do this. When I follow the instructions here it is based on a View style object. In the instructions it says:
Step 3 - Create a single WebView within the parent window
Add a WebView to the main window.
I get lost here and don't knwo how to add the control to my basic CDialog project.
Thank you for your direction on how to deal with this.
This alternative tutorial helped me. I downloaded the sample project, compiled it and it worked. Again, it was based on a application derived from a CView. However, I have managed to work out the principles required.
No doubt more will be involved as I continue to tweak the test application I am making.
Create a dialog based application using boiler place code.
Set the C++ Language Standard to ISO C++ 17.
Install two NuGet packages:
Microsoft.Web.WebView2 NuGet package
Microsoft.Windows.ImplementationLibrary NuGet package
Change InitInstance to use CoInitialize.
Add the ExitInstance handler and call UnCoinitialize.
For testing purposes I simply used the dimensions of my dialog. Begin by adding a variable to your dialog class:
std::unique_ptr<CWebBrowser> m_pWebBrowser;
Then, in OnInitDialog you can do something like this:
m_pWebBrowser = std::make_unique<CWebBrowser>();
if (m_pWebBrowser != nullptr)
{
CRect rectClient;
GetClientRect(rectClient);
m_pWebBrowser->CreateAsync(
WS_VISIBLE | WS_CHILD,
rectClient,
this,
1,
[this]() {
m_pWebBrowser->SetParent(this);
m_pWebBrowser->DisablePopups();
m_pWebBrowser->Navigate(L"https://jw.org", nullptr);
m_pWebBrowser->RegisterCallback(CWebBrowser::CallbackType::TitleChanged, [this]() {
CString title = m_pWebBrowser->GetTitle();
AfxGetMainWnd()->SetWindowText(title);
});
});
}
For the test, I also added the OnSize handler:
void CMFCTestWebView2Dlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
CRect rectClient;
if (m_pWebBrowser != nullptr)
{
m_staticBrowser.GetClientRect(rectClient);
m_staticBrowser.ClientToScreen(rectClient);
ScreenToClient(rectClient);
m_pWebBrowser->Resize(rectClient.Width(), rectClient.Height());
}
}
The heart of this is the CWebBrowser class. This is available in the linked tutorial and all I did was adjust the SetParentView method so that it accepted a CWnd pointer instead.
Oh, and you add DestroyWindow to the dialog class:
BOOL CMFCTestWebView2Dlg::DestroyWindow()
{
m_pWebBrowser.reset();
return CDialogEx::DestroyWindow();
}
CWebBrowser Header
#pragma once
#include <EventToken.h>
#include <functional>
#include <map>
struct ICoreWebView2Environment;
struct ICoreWebView2Controller;
struct CWebBrowserImpl;
class CView;
class CWebBrowser : public CWnd
{
public:
enum class CallbackType
{
CreationCompleted,
NavigationCompleted,
TitleChanged,
};
using CallbackFunc = std::function<void()>;
public:
CWebBrowser();
virtual ~CWebBrowser();
virtual BOOL Create(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID,
CCreateContext* = NULL) override;
BOOL CreateAsync(
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID,
CallbackFunc onCreated);
void RegisterCallback(CallbackType const type, CallbackFunc callback);
RECT GetBounds();
void SetBounds(LONG const width, LONG const height) { Resize(width, height); }
void Resize(LONG const width, LONG const height);
CString GetLocationURL();
void Navigate(CString const & url, CallbackFunc onComplete);
void NavigatePost(CString const& url, CString const& content, CString const& headers, CallbackFunc onComplete = nullptr);
void GoBack();
void GoForward();
void Reload();
void Stop();
bool IsNavigating() const { return m_isNavigating; }
void DisablePopups();
void PrintDocument();
CString GetTitle() const { return m_strTitle; }
void SetParent(CWnd* pParent) { m_pParent = pParent; }
bool IsWebViewCreated() const;
protected:
DECLARE_DYNCREATE(CWebBrowser)
DECLARE_MESSAGE_MAP()
private:
CWebBrowserImpl* m_pImpl;
std::map<CallbackType, CallbackFunc> m_callbacks;
EventRegistrationToken m_navigationCompletedToken = {};
EventRegistrationToken m_navigationStartingToken = {};
EventRegistrationToken m_documentTitleChangedToken = {};
bool m_isNavigating = false;
CWnd* m_pParent = nullptr;
CString m_strTitle;
private:
void RunAsync(CallbackFunc callback);
void CloseWebView();
void RegisterEventHandlers();
void ResizeToClientArea();
void NavigateTo(CString url);
CString NormalizeUrl(CString url);
static CString GetInstallPath();
static CString GetInstallPathFromRegistry(bool const searchWebView = true);
static CString GetInstallPathFromDisk(bool const searchWebView = true);
static CString GetUserDataFolder();
void InitializeWebView();
HRESULT OnCreateEnvironmentCompleted(HRESULT result, ICoreWebView2Environment* environment);
HRESULT OnCreateWebViewControllerCompleted(HRESULT result, ICoreWebView2Controller* controller);
static PCTSTR GetWindowClass();
static LRESULT CALLBACK WndProcStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
bool HandleWindowMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result);
BOOL CreateHostWindow(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID);
};
CWebBrowser Source
#include "pch.h"
#include <wrl.h>
#include "wil/com.h"
using namespace Microsoft::WRL;
#include "EdgeWebBrowser.h"
#include "Messages.h"
#include "WebView2.h"
#include <sstream>
#include <iomanip>
#include <shlwapi.h>
#pragma comment(lib,"shlwapi.lib")
#include "shlobj.h"
#pragma comment(lib,"Version.lib")
#include <string>
#define CHECK_FAILURE_STRINGIFY(arg) #arg
#define CHECK_FAILURE_FILE_LINE(file, line) ([](HRESULT hr){ CheckFailure(hr, L"Failure at " CHECK_FAILURE_STRINGIFY(file) L"(" CHECK_FAILURE_STRINGIFY(line) L")"); })
#define CHECK_FAILURE CHECK_FAILURE_FILE_LINE(__FILE__, __LINE__)
#define CHECK_FAILURE_BOOL(value) CHECK_FAILURE((value) ? S_OK : E_UNEXPECTED)
struct CWebBrowserImpl
{
wil::com_ptr<ICoreWebView2Environment> m_webViewEnvironment;
wil::com_ptr<ICoreWebView2Environment2> m_webViewEnvironment2;
wil::com_ptr<ICoreWebView2> m_webView;
wil::com_ptr<ICoreWebView2_2> m_webView2;
wil::com_ptr<ICoreWebView2Controller> m_webController;
wil::com_ptr<ICoreWebView2Settings> m_webSettings;
};
void ShowFailure(HRESULT hr, CString const & message)
{
CString text;
text.Format(L"%s (0x%08X)", (LPCTSTR)message, hr);
::MessageBox(nullptr, static_cast<LPCTSTR>(text), L"Failure", MB_OK);
}
void CheckFailure(HRESULT hr, CString const & message)
{
if (FAILED(hr))
{
CString text;
text.Format(L"%s : 0x%08X", (LPCTSTR)message, hr);
// TODO: log text
std::exit(hr);
}
}
/////////////////////////////////////////////////////////////////////////////
// CWebBrowser
IMPLEMENT_DYNCREATE(CWebBrowser, CWnd)
/////////////////////////////////////////////////////////////////////////////
// CWebBrowser properties
BEGIN_MESSAGE_MAP(CWebBrowser, CWnd)
END_MESSAGE_MAP()
CWebBrowser::CWebBrowser():m_pImpl(new CWebBrowserImpl())
{
m_callbacks[CallbackType::CreationCompleted] = nullptr;
m_callbacks[CallbackType::NavigationCompleted] = nullptr;
}
CWebBrowser::~CWebBrowser()
{
SetWindowLongPtr(m_hWnd, GWLP_USERDATA, 0);
CloseWebView();
delete m_pImpl;
}
BOOL CWebBrowser::CreateHostWindow(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID)
{
if (lpszClassName == nullptr)
lpszClassName = GetWindowClass();
if (!CWnd::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID))
return FALSE;
::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR)this);
return TRUE;
}
BOOL CWebBrowser::Create(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID,
CCreateContext*)
{
if (!CreateHostWindow(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID))
return FALSE;
InitializeWebView();
return TRUE;
}
BOOL CWebBrowser::CreateAsync(
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID,
CallbackFunc onCreated)
{
if (!CreateHostWindow(nullptr, nullptr, dwStyle, rect, pParentWnd, nID))
return FALSE;
m_callbacks[CallbackType::CreationCompleted] = onCreated;
InitializeWebView();
return TRUE;
}
void CWebBrowser::RegisterCallback(CallbackType const type, CallbackFunc callback)
{
m_callbacks[type] = callback;
}
void CWebBrowser::CloseWebView()
{
if (m_pImpl->m_webView)
{
m_pImpl->m_webView->remove_NavigationCompleted(m_navigationCompletedToken);
m_pImpl->m_webView->remove_NavigationStarting(m_navigationStartingToken);
m_pImpl->m_webView->remove_DocumentTitleChanged(m_documentTitleChangedToken);
m_pImpl->m_webController->Close();
m_pImpl->m_webController = nullptr;
m_pImpl->m_webView = nullptr;
m_pImpl->m_webView2 = nullptr;
m_pImpl->m_webSettings = nullptr;
}
m_pImpl->m_webViewEnvironment2 = nullptr;
m_pImpl->m_webViewEnvironment = nullptr;
}
void CWebBrowser::InitializeWebView()
{
CloseWebView();
CString subFolder = GetInstallPath();
CString appData = GetUserDataFolder();
ICoreWebView2EnvironmentOptions* options = nullptr;
HRESULT hr = CreateCoreWebView2EnvironmentWithOptions(
subFolder,
appData,
options,
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
this,
&CWebBrowser::OnCreateEnvironmentCompleted).Get());
if (!SUCCEEDED(hr))
{
CString text;
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
{
text = L"Cannot found the WebView2 component.";
}
else
{
text = L"Cannot create the webview environment.";
}
ShowFailure(hr, text);
}
}
HRESULT CWebBrowser::OnCreateEnvironmentCompleted(
HRESULT result,
ICoreWebView2Environment* environment)
{
CHECK_FAILURE(result);
if (!environment)
return E_FAIL;
CHECK_FAILURE(environment->QueryInterface(IID_PPV_ARGS(&m_pImpl->m_webViewEnvironment)));
CHECK_FAILURE(environment->QueryInterface(IID_PPV_ARGS(&m_pImpl->m_webViewEnvironment2)));
CHECK_FAILURE(m_pImpl->m_webViewEnvironment->CreateCoreWebView2Controller(
m_hWnd,
Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
this,
&CWebBrowser::OnCreateWebViewControllerCompleted).Get()));
return S_OK;
}
HRESULT CWebBrowser::OnCreateWebViewControllerCompleted(
HRESULT result,
ICoreWebView2Controller* controller)
{
if (result == S_OK)
{
if (controller != nullptr)
{
m_pImpl->m_webController = controller;
CHECK_FAILURE(controller->get_CoreWebView2(&m_pImpl->m_webView));
if (!m_pImpl->m_webView)
return E_FAIL;
CHECK_FAILURE(m_pImpl->m_webView->QueryInterface(IID_PPV_ARGS(&m_pImpl->m_webView2)));
CHECK_FAILURE(m_pImpl->m_webView->get_Settings(&m_pImpl->m_webSettings));
RegisterEventHandlers();
ResizeToClientArea();
}
auto callback = m_callbacks[CallbackType::CreationCompleted];
if (callback != nullptr)
RunAsync(callback);
}
else
{
ShowFailure(result, L"Cannot create webview environment.");
}
return S_OK;
}
void CWebBrowser::RegisterEventHandlers()
{
// NavigationCompleted handler
CHECK_FAILURE(m_pImpl->m_webView->add_NavigationCompleted(
Callback<ICoreWebView2NavigationCompletedEventHandler>(
[this](
ICoreWebView2*,
ICoreWebView2NavigationCompletedEventArgs* args) -> HRESULT
{
m_isNavigating = false;
BOOL success;
CHECK_FAILURE(args->get_IsSuccess(&success));
if (!success)
{
COREWEBVIEW2_WEB_ERROR_STATUS webErrorStatus{};
CHECK_FAILURE(args->get_WebErrorStatus(&webErrorStatus));
if (webErrorStatus == COREWEBVIEW2_WEB_ERROR_STATUS_DISCONNECTED)
{
// Do something here if you want to handle a specific error case.
// In most cases this isn't necessary, because the WebView will
// display its own error page automatically.
}
}
wil::unique_cotaskmem_string uri;
m_pImpl->m_webView->get_Source(&uri);
if (wcscmp(uri.get(), L"about:blank") == 0)
{
uri = wil::make_cotaskmem_string(L"");
}
auto callback = m_callbacks[CallbackType::NavigationCompleted];
if (callback != nullptr)
RunAsync(callback);
return S_OK;
})
.Get(),
&m_navigationCompletedToken));
// NavigationStarting handler
CHECK_FAILURE(m_pImpl->m_webView->add_NavigationStarting(
Callback<ICoreWebView2NavigationStartingEventHandler>(
[this](
ICoreWebView2*,
ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT
{
wil::unique_cotaskmem_string uri;
CHECK_FAILURE(args->get_Uri(&uri));
m_isNavigating = true;
return S_OK;
}).Get(), &m_navigationStartingToken));
// DocumentTitleChanged handler
CHECK_FAILURE(m_pImpl->m_webView->add_DocumentTitleChanged(
Callback<ICoreWebView2DocumentTitleChangedEventHandler>(
[this](ICoreWebView2* sender, IUnknown* args) -> HRESULT {
wil::unique_cotaskmem_string title;
CHECK_FAILURE(sender->get_DocumentTitle(&title));
m_strTitle = title.get();
auto callback = m_callbacks[CallbackType::TitleChanged];
if (callback != nullptr)
RunAsync(callback);
return S_OK;
})
.Get(), &m_documentTitleChangedToken));
}
void CWebBrowser::ResizeToClientArea()
{
if (m_pImpl->m_webController)
{
RECT bounds;
GetClientRect(&bounds);
m_pImpl->m_webController->put_Bounds(bounds);
}
}
RECT CWebBrowser::GetBounds()
{
RECT rc{0,0,0,0};
if (m_pImpl->m_webController)
{
m_pImpl->m_webController->get_Bounds(&rc);
}
return rc;
}
void CWebBrowser::Resize(LONG const width, LONG const height)
{
SetWindowPos(nullptr, 0, 0, width, height, SWP_NOMOVE| SWP_NOREPOSITION);
}
CString CWebBrowser::GetLocationURL()
{
CString url;
if (m_pImpl->m_webView)
{
wil::unique_cotaskmem_string uri;
m_pImpl->m_webView->get_Source(&uri);
if (wcscmp(uri.get(), L"about:blank") == 0)
{
uri = wil::make_cotaskmem_string(L"");
}
url = uri.get();
}
return url;
}
CString CWebBrowser::NormalizeUrl(CString url)
{
if (url.Find(_T("://")) < 0)
{
if (url.GetLength() > 1 && url[1] == ':')
url = _T("file://") + url;
else
url = _T("http://") + url;
}
return url;
}
void CWebBrowser::NavigateTo(CString url)
{
m_pImpl->m_webView->Navigate(NormalizeUrl(url));
}
void CWebBrowser::Navigate(CString const & url, CallbackFunc onComplete)
{
if (m_pImpl->m_webView)
{
m_callbacks[CallbackType::NavigationCompleted] = onComplete;
NavigateTo(url);
}
}
// The raw request header string delimited by CRLF(optional in last header).
void CWebBrowser::NavigatePost(CString const& url, CString const& content, CString const& headers, std::function<void()> onComplete)
{
if (!m_pImpl->m_webView) return;
CString normalizedUrl{ NormalizeUrl(url) };
m_callbacks[CallbackType::NavigationCompleted] = onComplete;
wil::com_ptr<ICoreWebView2WebResourceRequest> webResourceRequest;
wil::com_ptr<IStream> postDataStream = SHCreateMemStream(
reinterpret_cast<const BYTE*>(static_cast<LPCTSTR>(content)),
content.GetLength() + 1);
CHECK_FAILURE(m_pImpl->m_webViewEnvironment2->CreateWebResourceRequest(
CT2W(normalizedUrl),
L"POST",
postDataStream.get(),
CT2W(headers),
&webResourceRequest));
CHECK_FAILURE(m_pImpl->m_webView2->NavigateWithWebResourceRequest(webResourceRequest.get()));
}
void CWebBrowser::PrintDocument()
{
if (m_pImpl->m_webView)
{
m_pImpl->m_webView->ExecuteScript(L"window.print();", nullptr);
}
}
void CWebBrowser::Stop()
{
if (m_pImpl->m_webView)
{
m_pImpl->m_webView->Stop();
}
}
void CWebBrowser::Reload()
{
if (m_pImpl->m_webView)
{
m_pImpl->m_webView->Reload();
}
}
void CWebBrowser::GoBack()
{
if (m_pImpl->m_webView)
{
BOOL possible = FALSE;
m_pImpl->m_webView->get_CanGoBack(&possible);
if(possible)
m_pImpl->m_webView->GoBack();
}
}
void CWebBrowser::GoForward()
{
if (m_pImpl->m_webView)
{
BOOL possible = FALSE;
m_pImpl->m_webView->get_CanGoForward(&possible);
if (possible)
m_pImpl->m_webView->GoForward();
}
}
void CWebBrowser::DisablePopups()
{
if (m_pImpl->m_webSettings)
{
m_pImpl->m_webSettings->put_AreDefaultScriptDialogsEnabled(FALSE);
}
}
PCTSTR CWebBrowser::GetWindowClass()
{
static PCTSTR windowClass = []
{
static TCHAR const * className = L"EdgeBrowserHost";
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProcStatic;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = AfxGetInstanceHandle();
wcex.hIcon = nullptr;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = className;
wcex.hIconSm = nullptr;
ATOM result = RegisterClassEx(&wcex);
if (result == 0)
{
[[maybe_unused]] DWORD lastError = ::GetLastError();
}
return className;
}();
return windowClass;
}
LRESULT CALLBACK CWebBrowser::WndProcStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (auto app = (CWebBrowser*)::GetWindowLongPtr(hWnd, GWLP_USERDATA))
{
LRESULT result = 0;
if (app->HandleWindowMessage(hWnd, message, wParam, lParam, &result))
{
return result;
}
}
return ::DefWindowProc(hWnd, message, wParam, lParam);
}
bool CWebBrowser::HandleWindowMessage(
HWND, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result)
{
*result = 0;
switch (message)
{
case WM_SIZE:
{
if (lParam != 0)
{
ResizeToClientArea();
return true;
}
}
break;
case MSG_RUN_ASYNC_CALLBACK:
{
auto* task = reinterpret_cast<CallbackFunc*>(wParam);
(*task)();
delete task;
return true;
}
break;
}
return false;
}
void CWebBrowser::RunAsync(CallbackFunc callback)
{
auto* task = new CallbackFunc(callback);
PostMessage(MSG_RUN_ASYNC_CALLBACK, reinterpret_cast<WPARAM>(task), 0);
}
bool CWebBrowser::IsWebViewCreated() const
{
return m_pImpl->m_webView != nullptr;
}
CString CWebBrowser::GetInstallPath()
{
static CString path = []
{
auto installPath = GetInstallPathFromRegistry(); // check registry for WebView2
if (installPath.IsEmpty())
installPath = GetInstallPathFromDisk(); // check disk for WebView2
if (installPath.IsEmpty())
installPath = GetInstallPathFromRegistry(false);// check registry for Edge
if (installPath.IsEmpty())
installPath = GetInstallPathFromDisk(false); // check disk for Edge
return installPath;
}();
return path;
}
CString CWebBrowser::GetInstallPathFromRegistry(bool const searchWebView)
{
CString path;
HKEY handle = nullptr;
LSTATUS result = ERROR_FILE_NOT_FOUND;
if (searchWebView)
{
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft EdgeWebView)",
0,
KEY_READ,
&handle);
if (result != ERROR_SUCCESS)
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
LR"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft EdgeWebView)",
0,
KEY_READ,
&handle);
}
else
{
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge)",
0,
KEY_READ,
&handle);
if (result != ERROR_SUCCESS)
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
LR"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge)",
0,
KEY_READ,
&handle);
}
if (result == ERROR_SUCCESS)
{
TCHAR buffer[MAX_PATH + 1]{ 0 };
DWORD type = REG_SZ;
DWORD size = MAX_PATH;
result = RegQueryValueEx(handle, L"InstallLocation", 0, &type, reinterpret_cast<LPBYTE>(buffer), &size);
if (result == ERROR_SUCCESS)
path += CString{ buffer };
TCHAR version[100]{ 0 };
size = 100;
result = RegQueryValueEx(handle, L"Version", 0, &type, reinterpret_cast<LPBYTE>(version), &size);
if (result == ERROR_SUCCESS)
{
if (path.GetAt(path.GetLength() - 1) != L'\\')
path += L"\\";
path += CString{ version };
}
else
path.Empty();
RegCloseKey(handle);
}
return path;
}
CString CWebBrowser::GetInstallPathFromDisk(bool const searchWebView)
{
CString path =
searchWebView ?
LR"(c:\Program Files (x86)\Microsoft\EdgeWebView\Application\)" :
LR"(c:\Program Files (x86)\Microsoft\Edge\Application\)";
CString pattern = path + L"*";
WIN32_FIND_DATA ffd{ 0 };
HANDLE hFind = FindFirstFile(pattern, &ffd);
if (hFind == INVALID_HANDLE_VALUE)
{
[[maybe_unused]] DWORD error = ::GetLastError();
return {};
}
do
{
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
CString name{ ffd.cFileName };
int a, b, c, d;
if (4 == swscanf_s(ffd.cFileName, L"%d.%d.%d.%d", &a, &b, &c, &d))
{
FindClose(hFind);
return path + name;
}
}
} while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
return {};
}
CString CWebBrowser::GetUserDataFolder()
{
TCHAR szPath[MAX_PATH]{ 0 };
::SHGetFolderPath(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, szPath);
::PathAppend(szPath, LR"(\Demos\)");
::PathAppend(szPath, L"MfcEdgeDemo");
return CString{ szPath };
}
It works. One final comment, atleast for current ooperating systems... Make sure you have installed the WebView2 Runtime.
This question already has an answer here:
Win32 API - RegisterClassEx errors
(1 answer)
Closed 2 years ago.
Hi I am an Beginner learning DirectX 11 Api I have seen tutorial on Youtube and implemented this Wrapper for Windows But the Window is lot Loading The only debug string I get is just the Register Class Failed string in the INIT_WINDOw method . Nothing else Pops up . PLease help me with this .
DX_Wrapper.cpp
#include "Headers/Dx_Wrapper.h"
//Global One For Defining __stdcall for WindowProc
namespace
{
Wrapper* g_wrapper = nullptr;
}
//Main WINDOW Message processing Window Procedure
LRESULT CALLBACK WINDOWPROC(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
if (g_wrapper) return g_wrapper->MessageProcedure(hwnd, msg, wparam, lparam);
else return DefWindowProc(hwnd, msg, wparam, lparam);
}
//Constructor For the Instance
Wrapper::Wrapper(HINSTANCE hinstance)
{
m_hinstance = hinstance;
m_hwnd = NULL;
m_winheight = 600;
m_winwidth = 800;
m_wintitle = L"DirectX";
m_winstyles = WS_OVERLAPPEDWINDOW;
g_wrapper = this;
}
Wrapper::~Wrapper()
{
}
//Window Loop Manager Attached to the WINDOWPROC function
int32_t Wrapper::Run()
{
MSG message = { 0 };
while (WM_QUIT != message.message)
{
//Message Check If Quit
if (PeekMessage(&message, NULL, NULL, NULL, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
//If Not Quit
else
{
UPDATE_FRM(5.5f);
}
}
return static_cast<int>(message.wParam);
}
bool Wrapper::Init()
{
if (!INIT_WINDOW() == true)
{
return false;
}
return true;
}
LRESULT Wrapper::MessageProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
}
void Wrapper::UPDATE_FRM(float delta_time)
{
}
void Wrapper::RENDER_FRM(float delta_time)
{
}
//Registering Classical Windows Class Function
bool Wrapper::INIT_WINDOW()
{
//THE CLASS REGISTERING STUFF
WNDCLASSEX win_class;
win_class.cbClsExtra = 0;
win_class.hInstance = m_hinstance;
win_class.cbWndExtra = 0;
win_class.style = CS_HREDRAW | CS_VREDRAW;
win_class.cbSize = sizeof(WNDCLASSEX);
win_class.lpfnWndProc = WINDOWPROC;
win_class.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
win_class.lpszMenuName = NULL;
win_class.lpszClassName = L"dx_main_class";
win_class.hCursor = 0;
RegisterClassEx(&win_class);
if (!RegisterClassEx(&win_class))
{
OutputDebugString(L"Registering Class Failed");
return false;
}
RECT r = {0,0,m_winwidth,m_winheight};
AdjustWindowRect(&r, m_winstyles, FALSE);
uint_fast16_t f_width = r.right - r.left;
uint_fast16_t f_height = r.bottom - r.top;
uint_fast16_t f_width_final = uint_fast16_t(GetSystemMetrics(SM_CXSCREEN) / 2 - f_width / 2);
uint_fast16_t f_height_final = uint_fast16_t(GetSystemMetrics(SM_CYSCREEN) / 2 - f_height / 2);
m_hwnd = CreateWindow(L"dx_main_class", m_wintitle, m_winstyles, f_width_final, f_height_final, f_width, f_height, NULL, NULL, m_hinstance, NULL);
if (!m_hwnd)
{
OutputDebugString(L"Window Could Not be Created ");
return false;
}
ShowWindow(m_hwnd, SW_SHOW);
return true;
}
DX_Wrapper.h
#include <Windows.h>
#include <stdint.h>
#include <string>
#define WIN32_LEAN_AND_MEAN
class Wrapper
{
public:
Wrapper(HINSTANCE hinstance);
virtual ~Wrapper();
int32_t Run();
virtual bool Init();
virtual LRESULT MessageProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
virtual void UPDATE_FRM(float delta_time) = 0;
virtual void RENDER_FRM(float delta_time) = 0;
protected:
HWND m_hwnd = nullptr;
HINSTANCE m_hinstance = nullptr;
uint_fast16_t m_winwidth = 0;
uint_fast16_t m_winheight = 0;
LPCWSTR m_wintitle = L"";
DWORD m_winstyles = 0;
protected:
bool INIT_WINDOW();
};
This is the Main Application CPP file
#include "Headers/Dx_Wrapper.h"
class Application : public Wrapper
{
public:
Application(HINSTANCE hinstance);
bool Init() override;
void UPDATE_FRM(float delta_time) override;
void RENDER_FRM(float delta_time) override;
};
Application::Application(HINSTANCE hinstance) : Wrapper(hinstance)
{
}
bool Application::Init()
{
return Wrapper::Init();
}
void Application::UPDATE_FRM(float delta_time)
{
}
void Application::RENDER_FRM(float delta_time)
{
}
int32_t WINAPI WinMain(__in HINSTANCE hinstance, __in_opt HINSTANCE hprevinstance, __in LPSTR cmdline, __in int cmdshow)
{
Application m_app(hinstance);
if (!m_app.Init()) return 1;
return m_app.Run();
}
My Problem is The OutputDebugString Just shows The Registering class failed string from the INIT_WINDOW function . I have no clue why it is wrong . The Window is not running .
First you called twice RegisterClassEx function, so you need to delete the first call:
//RegisterClassEx(&win_class);
if (!RegisterClassEx(&win_class))
{
OutputDebugString(L"Registering Class Failed");
return false;
}
Then you need to set each member of WNDCLASSEX.
So you can refer to the following code:
WNDCLASSEX win_class;
win_class.cbSize = sizeof(WNDCLASSEX);
win_class.style = CS_HREDRAW | CS_VREDRAW;
win_class.lpfnWndProc = WINDOWPROC;
win_class.cbClsExtra = 0;
win_class.cbWndExtra = 0;
win_class.hInstance = m_hinstance;
win_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
win_class.hCursor = 0;
win_class.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
win_class.lpszMenuName = NULL;
win_class.lpszClassName = L"dx_main_class";
win_class.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
I'm trying to figure out how I can find where to either download the file set or get rid of this error. This elusive 'basewin.h' is nowhere to be found. So I don't know if I'm missing a library or what. I've also tried switching around the 'windows.h' and 'basewin.h' statements. I've tried disabling precompiled headers. Apparently this file holds the information for the "basewindow" class.
I'm using visual studio 2013 and I'm using a win32 project.I'm trying to run the example from the microsoft tutorial website http://msdn.microsoft.com/en-us/library/windows/desktop/ff684181(v=vs.85).aspx. Any help would be very much appreciated. Here is the code:
#include <windows.h>
#include <d2d1.h>
#pragma comment(lib, "d2d1")
#include "basewin.h"
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
class MainWindow : public BaseWindow<MainWindow>
{
ID2D1Factory *pFactory;
ID2D1HwndRenderTarget *pRenderTarget;
ID2D1SolidColorBrush *pBrush;
D2D1_ELLIPSE ellipse;
void CalculateLayout();
HRESULT CreateGraphicsResources();
void DiscardGraphicsResources();
void OnPaint();
void Resize();
public:
MainWindow() : pFactory(NULL), pRenderTarget(NULL), pBrush(NULL)
{
}
PCWSTR ClassName() const { return L"Circle Window Class"; }
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
};
// Recalculate drawing layout when the size of the window changes.
void MainWindow::CalculateLayout()
{
if (pRenderTarget != NULL)
{
D2D1_SIZE_F size = pRenderTarget->GetSize();
const float x = size.width / 2;
const float y = size.height / 2;
const float radius = min(x, y);
ellipse = D2D1::Ellipse(D2D1::Point2F(x, y), radius, radius);
}
}
HRESULT MainWindow::CreateGraphicsResources()
{
HRESULT hr = S_OK;
if (pRenderTarget == NULL)
{
RECT rc;
GetClientRect(m_hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right, rc.bottom);
hr = pFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(m_hwnd, size),
&pRenderTarget);
if (SUCCEEDED(hr))
{
const D2D1_COLOR_F color = D2D1::ColorF(1.0f, 1.0f, 0);
hr = pRenderTarget->CreateSolidColorBrush(color, &pBrush);
if (SUCCEEDED(hr))
{
CalculateLayout();
}
}
}
return hr;
}
void MainWindow::DiscardGraphicsResources()
{
SafeRelease(&pRenderTarget);
SafeRelease(&pBrush);
}
void MainWindow::OnPaint()
{
HRESULT hr = CreateGraphicsResources();
if (SUCCEEDED(hr))
{
PAINTSTRUCT ps;
BeginPaint(m_hwnd, &ps);
pRenderTarget->BeginDraw();
pRenderTarget->Clear( D2D1::ColorF(D2D1::ColorF::SkyBlue) );
pRenderTarget->FillEllipse(ellipse, pBrush);
hr = pRenderTarget->EndDraw();
if (FAILED(hr) || hr == D2DERR_RECREATE_TARGET)
{
DiscardGraphicsResources();
}
EndPaint(m_hwnd, &ps);
}
}
void MainWindow::Resize()
{
if (pRenderTarget != NULL)
{
RECT rc;
GetClientRect(m_hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right, rc.bottom);
pRenderTarget->Resize(size);
CalculateLayout();
InvalidateRect(m_hwnd, NULL, FALSE);
}
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow)
{
MainWindow win;
if (!win.Create(L"Circle", WS_OVERLAPPEDWINDOW))
{
return 0;
}
ShowWindow(win.Window(), nCmdShow);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
if (FAILED(D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED, &pFactory)))
{
return -1; // Fail CreateWindowEx.
}
return 0;
case WM_DESTROY:
DiscardGraphicsResources();
SafeRelease(&pFactory);
PostQuitMessage(0);
return 0;
case WM_PAINT:
OnPaint();
return 0;
case WM_SIZE:
Resize();
return 0;
}
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}
The page you linked says: "The program re-uses the BaseWindow class that was defined in the topic Managing Application State".
That link contains the code for the BaseWindow class, which you should put in basewin.h.
I have this class:
WNDCLASSEX ActionButton::m_wndClass = CreateWndClass();
ActionButton::ActionButton() :
m_function(NULL), m_parameters(NULL), m_window()
{}
ActionButton::~ActionButton()
{
DestroyWindow(m_window);
}
bool ActionButton::DestroyButton()
{
return DestroyWindow(m_window);
}
bool ActionButton::Create(HWND parent, int x, int y, int heigth, int width)
{
HWND m_window = CreateWindowEx(0, L"Action button", NULL, WS_CHILD | WS_VISIBLE,
x, y, width, heigth, parent, NULL, NULL, NULL);
if (m_window == NULL)
return false;
SetWindowLongPtr(m_window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
return true;
}
void ActionButton::SetFuncionLeftButtonDown(CallbackFunction f)
{
m_function = f;
}
void ActionButton::SetParametersLeftButtonDown(void* param)
{
m_parameters = param;
}
WNDCLASSEX ActionButton::CreateWndClass()
{
WNDCLASSEX m_wndClass = {0};
if (m_wndClass.cbSize == 0)
{
m_wndClass.cbSize = sizeof(WNDCLASSEX);
m_wndClass.style = CS_NOCLOSE;
m_wndClass.lpfnWndProc = WndProc;
m_wndClass.cbClsExtra = 0;
m_wndClass.cbWndExtra = 0;
m_wndClass.hInstance = GetModuleHandle(NULL);
m_wndClass.hIcon = NULL;
m_wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
m_wndClass.hbrBackground = HBRUSH(COLOR_BACKGROUND);
m_wndClass.lpszMenuName = NULL;
m_wndClass.lpszClassName = L"Action button";
m_wndClass.hIconSm = NULL;
}
RegisterClassEx(&m_wndClass);
return m_wndClass;
}
LRESULT __stdcall ActionButton::WndProc (HWND window, UINT msg, WPARAM wp, LPARAM lp)
{
ActionButton* classInfo = reinterpret_cast<ActionButton *>(GetWindowLongPtr(window, GWLP_USERDATA));
switch(msg)
{
case WM_LBUTTONDOWN:
{
(classInfo->m_function)(classInfo->m_parameters, classInfo);
classInfo->DestroyButton();
break;
}
case WM_DESTROY:
{
break;
}
default:
return DefWindowProc(window, msg, wp, lp);
}
return 0;
}
I have found problem, that it do not destroy window, what is more I check with debugger that m_window in destructor and Destroy() methods is NULL.
My code of using this class:
void Function(void* input, ActionButton*)
{
std::cout << "Works :)\n";
}
//....
ActionButton button;
button.Create(Form.Get(), 150,150, 50,50);
button.SetFuncionLeftButtonDown(Function);
HWND m_window
That declares a local variable which hides the class member variable. Your compiler should be warning you about this. Pay attention to the warnings.