How do I add a WebView2 control to a CDialog resource in a MFC project? - mfc

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.

Related

"Access violation reading location 0xFFFFFFFFFFFFFFFF" Why can't I make an object of my child classes in functions?

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";
});
}

MSVC ignores throw statement

At the current moment I am trying to test IAutoComplete COM interface, which requires me to make use of MSVC cpp compiler. I am faced with a problem I never faced in MingGW. MSVC sees my throw statement and completely ignores it. So I don't know if there is a setting I didn't set.
Alright I got a down vote which I probably deserve for giving a bad example. I thought that it failed to throw because it was in a struct but tries with a fresh struct proved me wrong. I might not be able to offer a reproducible example but I will try.
Prepare for alot of code.
Jav/AutoObject.h
#ifndef JAV_WIN32_AUTO_OBJECT_HPP
#define JAV_WIN32_AUTO_OBJECT_HPP
#include <Jav/config.h>
namespace Jav
{
struct AutoObject
{
virtual ~AutoObject(){}
void* operator new(size_t sz);
};
AutoObject* shallowCopy(AutoObject*);
AutoObject* deleteObject(AutoObject*);
}
#endif // JAV_WIN32_AUTO_OBJECT_HPP
Jav/win32/widgets/widgets_ex.h
#ifndef JAV_WIN32_GUI_WIDGETS_EX_HPP
#define JAV_WIN32_GUI_WIDGETS_EX_HPP
#include <Jav/win32/widgets/widgets.h>
namespace Jav { namespace win32 {
class Widget
{
public:
Widget(HWND widget=nullptr) : widget(widget){}
operator HWND() { return widget; }
public:
void setId(int id) { SetWindowLong(widget,GWLP_ID,id); }
void destroy() { DestroyWindow(widget); widget = NULL; }
void show() { ShowWindow(widget,SW_SHOW); }
void hide() { ShowWindow(widget,SW_HIDE); }
protected:
HWND widget;
};
}}
#endif // JAV_WIN32_GUI_WIDGETS_EX_HPP
Jav/win32/window/Window.h
#include <Jav/win32/widgets/widgets_ex.h>
using Window = Widget;
Jav/win32/window/SimpleWindow.h
#ifndef JAV_WIN32_WINDOW_SIMPLE_WINDOW_HPP
#define JAV_WIN32_WINDOW_SIMPLE_WINDOW_HPP
#include <Jav/AutoObject.h>
#include <Jav/win32/window/Window.h>
namespace Jav { namespace win32 {
struct Content__ : AutoObject
{
virtual void onCreate(HWND,CREATESTRUCTA*) {}
virtual void onDestroy() {}
virtual void onSize(int w,int h) {}
virtual void onDraw(Canvas&) {}
};
class SimpleWindow : public Window
{
public:
SimpleWindow(Content__*, const char *title, size_t w,size_t h,
int style=WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
int ex_style=0);
public:
bool isOpen();
void close();
const MSG& getMessage();
const MSG& peekMessage();
void dispatchMessage(const MSG&);
private:
static ATOM registerWindow();
static LRESULT CALLBACK windowProc(HWND,UINT,WPARAM,LPARAM);
static HWND createWindow(Content__*, const char *,HMENU, size_t,size_t, int,int);
private:
MSG msg;
};
}}
#endif // JAV_WIN32_WINDOW_SIMPLE_WINDOW_HPP
AutoObject.cpp
#include <Jav/AutoObject.h>
#include <unordered_map>
#include <objbase.h>
namespace {
struct AutoObjectsMap : std::unordered_map<Jav::AutoObject*,size_t>
{
~AutoObjectsMap() { for(auto &obj : *this) CoTaskMemFree(obj.first); }
};
AutoObjectsMap *alloc_objects_ptr;
struct AutoObjectsMapMgr
{
~AutoObjectsMapMgr() { delete alloc_objects_ptr; };
};
}
#define alloc_objects (*alloc_objects_ptr)
namespace Jav
{
void* AutoObject::operator new(size_t sz)
{
auto mem = CoTaskMemAlloc(sz);
if(!alloc_objects_ptr) alloc_objects_ptr = new AutoObjectsMap();
alloc_objects[(AutoObject*)mem] = 1;
return mem;
}
AutoObject* shallowCopy(AutoObject *r)
{
auto elem = alloc_objects.find(r);
if( elem != alloc_objects.end() ) ++alloc_objects[r];
return r;
}
AutoObject* deleteObject(AutoObject *mem)
{
auto elem = alloc_objects.find(mem);
if( elem != alloc_objects.end() )
{
if(--elem->second == 0)
{
mem->~AutoObject();
CoTaskMemFree(mem);
alloc_objects.erase(elem);
return nullptr;
}
}
return mem;
}
}
SimpleWindow.cpp
#include <Jav/win32/window/SimpleWindow.h>
namespace Jav { namespace win32 {
namespace {
LRESULT CALLBACK SimpleWindow::windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
Content__ *obj = (Content__*)GetWindowLongPtrA(hwnd,GWLP_USERDATA);
auto getWidth = [lParam](){ return LOWORD(lParam); };
auto getHeight = [lParam](){ return HIWORD(lParam); };
auto getX = [lParam](){ return LOWORD(lParam); };
auto getY = [lParam](){ return HIWORD(lParam); };
auto getWidget = [lParam](){ return (HWND)lParam; };
auto widgetId = [wParam](){ return LOWORD(wParam); };
auto widgetEvent = [wParam](){ return HIWORD(wParam); };
auto menuType = [wParam](){ return LOWORD(wParam); };
auto eventId = [wParam](){ return LOWORD(wParam); };
switch (uMsg)
{
case WM_CREATE:
{
CREATESTRUCT *cs = ((CREATESTRUCTA*)lParam);
obj = (Content__*)cs->lpCreateParams;
SetWindowLongPtr(hwnd,GWLP_USERDATA,(LONG_PTR)obj);
obj->onCreate(hwnd,cs);
return 0;
}
case WM_DESTROY:
obj->onDestroy();
deleteObject(obj);
return 0;
case WM_SIZE:
obj->onSize(getWidth(),getHeight());
return 0;
default: return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
ATOM SimpleWindow::registerWindow()
{
WNDCLASSEX wincl = {};
wincl.cbSize = sizeof(WNDCLASSEX);
wincl.lpszClassName = "JavWin32WindowSimpleWindow";
wincl.lpfnWndProc = SimpleWindow::windowProc;
wincl.style = CS_HREDRAW|CS_VREDRAW;
wincl.hIcon = LoadIcon(NULL,IDI_APPLICATION);;
wincl.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
wincl.hCursor = LoadCursor(NULL,IDC_ARROW);
wincl.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wincl.cbWndExtra = WINDOW_WND_EXTRA_BYTES;
return RegisterClassEx(&wincl);
}
SimpleWindow::SimpleWindow(Content__ *obj,const char *title, size_t w,size_t h, int style,int ex_style)
: Window( createWindow(obj,title,NULL, w,h, style,ex_style) ),
app_ctx(*this)
{
msg.message = 0;
show();
}
bool SimpleWindow::isOpen()
{
return msg.message != WM_QUIT;
}
void SimpleWindow::close()
{
DestroyWindow(*this);
}
const MSG& SimpleWindow::getMessage()
{
GetMessageA(&msg,*this,0,0);
return msg;
}
const MSG& SimpleWindow::peekMessage()
{
PeekMessageA(&msg,*this,0,0,PM_REMOVE);
return msg;
}
void SimpleWindow::dispatchMessage(const MSG &msg)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}}
main.cpp
#include <Jav/AutoObject.h>
#include <Jav/win32/window/SimpleWindow.h>
using namespace Jav::win32;
class Content : public Content__
{
IAutoComplete *pac = NULL;
IAutoComplete2 *pac2 = NULL;
//IUnknown *punkSource;
//ColumnList columnList;
public:
~Content()
{
if(pac) pac->Release();
if(pac2) pac2->Release();
}
void onCreate(HWND hwnd,CREATESTRUCTA*)override
{
throw; //doesn't throw or terminate
HWND textBox; // pretend this is an edit control
attachAutoCompleteObject(textBox); //doesn't throw either
}
void attachAutoCompleteObject(HWND hwnd)
{
HRESULT hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pac));
if(FAILED(hr)) throw "Failed to create AutoComplete Object\n";
hr = pac->Init(hwnd, &columnList, NULL, NULL);
if(FAILED(hr)) throw "Failed to attach auto complete object\n";
hr = pac->QueryInterface(IID_PPV_ARGS(&pac2)); rep(pac2);
if (FAILED(hr)) throw "Failed to set autocomplete options\n";
hr = pac2->SetOptions(ACO_AUTOSUGGEST);
}
};
int main()
{
Content content;
SimpleWindow win(&content,"",640,480);
while(win.isOpen())
{
auto &msg = win.getMessage();
win.dispatchMessage(msg);
}
}
So you appear to have throw "literal" instead of just throw; you originally posted. Then it indeed should throw. The problem here is that you attempt to throw exception that goes through Windows DLLs, like GetMessage/DispatchMessage fuctions or COM method dispatch, which is not guarrantied to work.

Subclassin button WinApi

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();
}
}

Win32 PlaySound overlapping audio

I have a C++ Win32 app that needs to be able to play an external .wav file every time a certain event is triggered. I currently have code that looks like this:
void CALLBACK timerCall(HWND hwnd, UINT msg, UINT timer, DWORD time)
{
if(/*some condition is met*/)
{
std::cout << "Detected event" << std::endl;
PlaySound("file.wav", NULL, SND_FILENAME | SND_ASYNC);
}
}
The problem is, the .wav file is several seconds long and I want each invocation of the event to play a new instance of that sound. Without SND_ASYNC there it wouldn't trigger the event until the sound had finished playing; this was resolved by adding SND_ASYNC. However, now if the event is triggered again while the sound is already playing, it interrupts the playing and simply starts over instead of overlapping the sounds.
How do I prevent the new call to PlaySound from interrupting the previous and force the sounds to overlap?
"waveaudio" device (which is used by PlaySound) does not support playing several files simultaneously. Try to use an .mp3 file. Example below.
#include <Windows.h>
#include <stdio.h>
#include <stdexcept>
#ifdef _UNICODE
#define stprintf_s swprintf_s
#else
#define stprintf_s sprintf_s
#endif
class Player {
public:
Player(LPCTSTR lpFileName) {
MCI_OPEN_PARMS openp;
MCI_SET_PARMS setp;
openp.dwCallback = NULL;
openp.lpstrDeviceType = reinterpret_cast<LPCTSTR>(MCI_ALL_DEVICE_ID);
openp.lpstrElementName = lpFileName;
TCHAR name[32];
static int alias = 0;
stprintf_s(name, TEXT("alias%08d"), alias++);
openp.lpstrAlias = name;
checkerror(mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_ELEMENT | MCI_OPEN_SHAREABLE | MCI_OPEN_ALIAS, reinterpret_cast<DWORD_PTR>(&openp)));
_device = openp.wDeviceID;
setp.dwCallback = NULL;
setp.dwTimeFormat = 0;
if (mciSendCommand(openp.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, reinterpret_cast<DWORD_PTR>(&setp)) == DWORD(-1)) {
close();
throw std::runtime_error("Can't open MCI device");
}
}
Player(Player const&) = delete;
Player(Player&& other) {
_device = other._device;
other._device = 0;
}
~Player() {
if (_device != 0) {
close();
}
}
void play(HWND hWndNotify) {
MCI_PLAY_PARMS params;
params.dwCallback = reinterpret_cast<DWORD_PTR>(hWndNotify);
params.dwFrom = NULL;
params.dwTo = NULL;
checkerror(mciSendCommand(_device, MCI_PLAY, (hWndNotify != 0) ? MCI_NOTIFY : 0, reinterpret_cast<DWORD_PTR>(&params)));
}
void rewind() {
MCI_SEEK_PARMS params;
checkerror(mciSendCommand(_device, MCI_SEEK, MCI_WAIT | MCI_SEEK_TO_START, reinterpret_cast<DWORD_PTR>(&params)));
}
void pause() {
MCI_GENERIC_PARMS params;
params.dwCallback = NULL;
checkerror(mciSendCommand(_device, MCI_PAUSE, MCI_WAIT, reinterpret_cast<DWORD_PTR>(&params)));
}
void stop() {
MCI_GENERIC_PARMS params;
params.dwCallback = NULL;
checkerror(mciSendCommand(_device, MCI_STOP, MCI_WAIT, reinterpret_cast<DWORD_PTR>(&params)));
}
MCIDEVICEID device() const { return _device; }
private:
MCIDEVICEID _device;
static void checkerror(MCIERROR code) {
if (code != 0) {
char buffer[260];
mciGetErrorStringA(code, buffer, sizeof(buffer) - 1);
throw std::runtime_error(buffer);
}
}
void close() {
MCI_GENERIC_PARMS params;
params.dwCallback = NULL;
checkerror(mciSendCommand(_device, MCI_CLOSE, MCI_WAIT, reinterpret_cast<DWORD_PTR>(&params)));
}
};
#include <map>
#include <string>
#include <mutex>
#ifdef _UNICODE
typedef std::wstring tstring;
#else
typedef std::string tstring;
#endif
class Repeater {
public:
Repeater(LPCTSTR fn) : fn(fn) {
hWnd = CreateWindowEx(0, TEXT("STATIC"), NULL, 0, 0, 0, 0, 0, HWND_DESKTOP, NULL, GetModuleHandle(NULL), 0);
if (hWnd == NULL)
throw std::runtime_error("Can't create window");
oldproc = reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG>(this));
SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG>(&myproc));
}
~Repeater() {
DestroyWindow(hWnd);
}
void play() {
Player player(fn.c_str());
std::lock_guard<std::recursive_mutex> lock(devmap_mutex);
player.play(hWnd);
devmap.insert(decltype(devmap)::value_type(player.device(), std::move(player)));
}
HWND wnd() const { return hWnd; }
void stop() {
std::lock_guard<std::recursive_mutex> lock(devmap_mutex);
devmap.clear();
}
private:
HWND hWnd;
tstring fn;
std::recursive_mutex devmap_mutex;
std::map<MCIDEVICEID, Player> devmap;
WNDPROC oldproc;
static LRESULT CALLBACK myproc(_In_ HWND hWnd, _In_ UINT Msg, _In_ WPARAM wParam, _In_ LPARAM lParam) {
auto self = reinterpret_cast<Repeater*>(GetWindowLong(hWnd, GWLP_USERDATA));
switch (Msg) {
case MM_MCINOTIFY: {
// see https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd757358(v=vs.85).aspx
std::lock_guard<std::recursive_mutex> lock(self->devmap_mutex);
self->devmap.erase(static_cast<MCIDEVICEID>(lParam));
return 0;
}
case WM_DESTROY: {
SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG>(self->oldproc));
}
default:
break;
}
return CallWindowProc(self->oldproc, hWnd, Msg, wParam, lParam);
}
};
int main() {
// USE MP3. Forget about WAV.
LPCTSTR filename = TEXT("c:\\Users\\Vyacheslav\\Music\\Ori\\soundtrack\\Racing the Lava.mp3");
#if 0
// without notifications
Player dev1(filename), dev2(filename);
dev1.play();
Sleep(1000);
dev2.play();
Sleep(10000);
#else
// with notifications
{
Repeater rep(filename);
std::thread thread([&rep] {
for (int i = 0; i < 5; ++i) {
rep.play();
Sleep(1000);
}
Sleep(1000);
rep.stop(); // .stop() MUST be called from the same thread as ALL .play() !!!
PostMessage(rep.wnd(), WM_QUIT, 0, 0); // interrupt message processing queue
});
MSG msg;
while (GetMessage(&msg, 0, 0, 0)) {
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
thread.join();
}
Sleep(10000); // silence
#endif
return 0;
}

Cannot open include file: 'basewin.h': No such file or directory

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.