I can't get Awesomium 1.7.0 to play an html5 video (webm format), even with the sample projects coming along with the SDK (sample_gdi).
sample page: http://www.webmfiles.org/demo-files/
It looks like the video frames are correctly loaded, but the player is stuck on the first frame. Although if I move the progress bar manually, I can browse through the video frames...
I tried with both webViewType window and offscreen, and with enable_gpu_acceleration and enable_web_gl enabled, but every time without success...
My specs: VS2010, windows 7
Any ideas? Thanks!!
Code from the sample project "Sample_gdi" installed automatically by the Awesomium 1.7.0 installer, available in C:\Users[user]\Documents\Visual Studio 2010\Projects\Awesomium\1.7.0.5\BuildSamples\BuildSamples.sln :
Main.cc
#include "../common/application.h"
#include "../common/view.h"
#include <Awesomium/WebCore.h>
#include <Awesomium/STLHelpers.h>
#ifdef _WIN32
#include <Windows.h>
#endif
using namespace Awesomium;
class GDISample : public Application::Listener {
Application* app_;
View* view_;
public:
GDISample()
: app_(Application::Create()),
view_(0) {
app_->set_listener(this);
}
virtual ~GDISample() {
if (view_)
app_->DestroyView(view_);
if (app_)
delete app_;
}
void Run() {
app_->Run();
}
// Inherited from Application::Listener
virtual void OnLoaded() {
view_ = View::Create(800, 600);
view_->web_view()->LoadURL(WebURL(WSLit("http://www.google.com")));
}
// Inherited from Application::Listener
virtual void OnUpdate() {
}
// Inherited from Application::Listener
virtual void OnShutdown() {
}
};
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, wchar_t*,
int nCmdShow) {
GDISample sample;
sample.Run();
return 0;
}
application_win.cc
#include "application.h"
#include "view.h"
#include <Awesomium/WebCore.h>
#include <Awesomium/STLHelpers.h>
#include <string>
using namespace Awesomium;
class ApplicationWin : public Application {
bool is_running_;
public:
ApplicationWin() {
is_running_ = true;
listener_ = NULL;
web_core_ = NULL;
}
virtual ~ApplicationWin() {
if (listener())
listener()->OnShutdown();
if (web_core_)
web_core_->Shutdown();
}
virtual void Run() {
Load();
// Main message loop:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) && is_running_) {
web_core_->Update();
TranslateMessage(&msg);
DispatchMessage(&msg);
if (listener())
listener()->OnUpdate();
}
}
virtual void Quit() {
is_running_ = false;
}
virtual void Load() {
WebConfig config;
web_core_ = WebCore::Initialize(config);
if (listener())
listener()->OnLoaded();
}
virtual View* CreateView(int width, int height) {
return View::Create(width, height);
}
virtual void DestroyView(View* view) {
delete view;
}
virtual void ShowMessage(const char* message) {
std::wstring message_str(message, message + strlen(message));
MessageBox(0, message_str.c_str(), message_str.c_str(), NULL);
}
};
Application* Application::Create() {
return new ApplicationWin();
}
view_win.cc
#include "view.h"
#include <Awesomium/WebCore.h>
#include <Awesomium/STLHelpers.h>
#include <vector>
class ViewWin;
static bool g_is_initialized = false;
static std::vector<ViewWin*> g_active_views_;
const wchar_t szWindowClass[] = L"ViewWinClass";
const wchar_t szTitle[] = L"Application";
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
using namespace Awesomium;
class ViewWin : public View,
public WebViewListener::View {
public:
ViewWin(int width, int height) {
PlatformInit();
WebPreferences webPref;
WebSession *session = WebCore::instance()->CreateWebSession(ToWebString(""),webPref);
web_view_ = WebCore::instance()->CreateWebView(width,
height,
session,
Awesomium::kWebViewType_Window);
web_view_->set_view_listener(this);
// Create our WinAPI Window
HINSTANCE hInstance = GetModuleHandle(0);
hwnd_ = CreateWindow(szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
width + 20,
height + 40,
NULL,
NULL,
hInstance,
NULL);
if (!hwnd_)
exit(-1);
web_view_->set_parent_window(hwnd_);
ShowWindow(hwnd_, SW_SHOWNORMAL);
UpdateWindow(hwnd_);
SetTimer (hwnd_, 0, 15, NULL );
g_active_views_.push_back(this);
}
virtual ~ViewWin() {
for (std::vector<ViewWin*>::iterator i = g_active_views_.begin();
i != g_active_views_.end(); i++) {
if (*i == this) {
g_active_views_.erase(i);
break;
}
}
web_view_->Destroy();
}
HWND hwnd() { return hwnd_; }
static void PlatformInit() {
if (g_is_initialized)
return;
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(0);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = szWindowClass;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc)) {
exit(-1);
}
g_is_initialized = true;
}
static ViewWin* GetFromHandle(HWND handle) {
for (std::vector<ViewWin*>::iterator i = g_active_views_.begin();
i != g_active_views_.end(); i++) {
if ((*i)->hwnd() == handle) {
return *i;
}
}
return NULL;
}
// Following methods are inherited from WebViewListener::View
virtual void OnChangeTitle(Awesomium::WebView* caller,
const Awesomium::WebString& title) {
std::string title_utf8(ToString(title));
std::wstring title_wide(title_utf8.begin(), title_utf8.end());
SetWindowText(hwnd_, title_wide.c_str());
}
virtual void OnChangeAddressBar(Awesomium::WebView* caller,
const Awesomium::WebURL& url) { }
virtual void OnChangeTooltip(Awesomium::WebView* caller,
const Awesomium::WebString& tooltip) { }
virtual void OnChangeTargetURL(Awesomium::WebView* caller,
const Awesomium::WebURL& url) { }
virtual void OnChangeCursor(Awesomium::WebView* caller,
Awesomium::Cursor cursor) { }
virtual void OnChangeFocus(Awesomium::WebView* caller,
Awesomium::FocusedElementType focused_type) { }
virtual void OnShowCreatedWebView(Awesomium::WebView* caller,
Awesomium::WebView* new_view,
const Awesomium::WebURL& opener_url,
const Awesomium::WebURL& target_url,
const Awesomium::Rect& initial_pos,
bool is_popup) { }
virtual void OnAddConsoleMessage(Awesomium::WebView* caller,
const Awesomium::WebString& message,
int line_number,
const Awesomium::WebString& source) { }
protected:
HWND hwnd_;
};
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
ViewWin* view = ViewWin::GetFromHandle(hWnd);
switch (message) {
case WM_COMMAND:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
case WM_TIMER:
break;
case WM_SIZE:
view->web_view()->Resize(LOWORD(lParam), HIWORD(lParam));
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_QUIT:
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
View* View::Create(int width, int height) {
return new ViewWin(width, height);
}
../common/application.h
#ifndef COMMON_APPLICATION_H_
#define COMMON_APPLICATION_H_
class View;
namespace Awesomium {
class WebCore;
}
// Common class that sets up an application, creates the WebCore, handles
// the Run loop, and abstracts platform-specific details.
class Application {
public:
// Listener interface to be used to handle various application events.
class Listener {
public:
virtual ~Listener() {}
// Event is fired when app (and WebCore) have been loaded.
virtual void OnLoaded() = 0;
// Event is fired for each iteration of the Run loop.
virtual void OnUpdate() = 0;
// Event is fired when the app is shutting down.
virtual void OnShutdown() = 0;
};
virtual ~Application() {}
// Platform-specific factory constructor
static Application* Create();
// Begin the Run loop.
virtual void Run() = 0;
// Ends the Run loop.
virtual void Quit() = 0;
// Create a platform-specific, windowed View
virtual View* CreateView(int width, int height) = 0;
// Destroy a View
virtual void DestroyView(View* view) = 0;
// Show a modal message box
virtual void ShowMessage(const char* message) = 0;
// Get the WebCore
virtual Awesomium::WebCore* web_core() { return web_core_; }
// Get the Listener.
Listener* listener() { return listener_; }
// Set the Listener for various app events.
void set_listener(Listener* listener) { listener_ = listener; }
protected:
Application() { }
virtual void Load() = 0;
Listener* listener_;
Awesomium::WebCore* web_core_;
};
#endif // COMMON_APPLICATION_H_
../common/view.h
#ifndef COMMON_VIEW_H_
#define COMMON_VIEW_H_
namespace Awesomium {
class WebView;
}
// Common class that implements a windowed WebView, handles all input/display,
// and abstracts away all the platform-specific details.
class View {
public:
virtual ~View() {}
// Platform-specific constructor
static View* Create(int width, int height);
// Get the associated WebView
Awesomium::WebView* web_view() { return web_view_; }
protected:
View() { }
Awesomium::WebView* web_view_;
};
#endif // COMMON_VIEW_H_
Looks like I wasn't able to reproduce the bug when deployed on other computers.
There should be something broken on my computer that prevents awesomium to play an html5 video correctly...
I spent a day on this issue and for me it ended up being an audio connection issue. I had to use an HDMI cable or plug speakers into my computer. Once I did that, video did not freeze.
My issues were caused by using a DVI cable and not having anything plugged in to my audio out.
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";
});
}
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.
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();
}
}
If you look at CONTROL.CPP, you'll see CONTROL::CONTROLNATIVEWINDOW::WndProc(). Here, for a test, I am outputting the CONTROL name. I have multiple derived classes of CONTROL that override the GetName() method. However, the only derived class it ever prints is WINDOW, even though it should output TEXTBOX as well.
What I can't tell is that if the reinterpret_cast in the LRESULT CALLBACK InternalWinProc() is casting in incorrectly:
LRESULT CALLBACK InternalWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
NativeWindow* window = reinterpret_cast<NativeWindow*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
if (window) {
Message msg = Message::Create(hWnd, message, IntPtr((void*)wParam), IntPtr((void*)lParam));
window->WndProc(&msg);
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
Or, the line
window = new ControlNativeWindow(this);
in the base class CONTROL constructor is not saving the derived type this correctly.
When running through the debugger and looking at the line above, the this has a vptr pointing all to CONTROL::... methods instead of the respective derived class. Which makes me think that you can't save the this pointer of a derived class in the base class like I'm doing.
And then when I go to cast it back to NativeWindow* in the InternalWndProc function, it is giving a bad cast so when the line
control->GetName();
executes in the
Control::ControlNativeWindow::WndProc(Message * msg)
method it is incorrectly calling
Window::GetName();
Here is the rest of the code:
CONTROL.H
#ifndef CONTROL_H
#define CONTROL_H
#include "IntPtr.h"
#include "CreateParams.h"
#include "Point.h"
#include "Size.h"
#include "NativeWindow.h"
class Control
{
public:
Control();
~Control();
virtual void CreateControl();
Control* GetParent() { return parent; }
void SetParent(Control* parent) { this->parent = parent; }
IntPtr GetHandle() { return window->GetHandle(); }
Point GetLocation() { return Point(x, y); }
void SetLocation(Point point);
Size GetSize() { return Size(width, height); }
void SetSize(Size size);
std::string GetText();
void SetText(std::string text);
void Show();
bool IsHandledCreated();
class ControlNativeWindow : public NativeWindow
{
public:
ControlNativeWindow(Control* control);
virtual std::string GetName() { return "CONTROLNATIVEWindow\n"; }
protected:
virtual void WndProc(Message* msg);
private:
Control* control;
};
virtual std::string GetName() { return "Control\n"; }
protected:
virtual void OnPaint();
virtual void OnTextChanged();
virtual void DefWndProc(Message* msg);
virtual void WndProc(Message* msg);
virtual void CreateHandle();
virtual CreateParams GetCreateParams();
private:
Control* parent;
ControlNativeWindow* window;
int x;
int y;
int width;
int height;
std::string text;
bool visible;
};
#endif // CONTROL_H
CONTROL.CPP
#include "Control.h"
#include "Utility.h"
#include <string>
#include "Message.h"
using namespace std;
Control::Control()
: x(0), y(0),
width(200), height(20), // fix this
visible(false)
{
window = new ControlNativeWindow(this);
}
Control::~Control()
{
}
void Control::CreateControl()
{
CreateHandle();
}
void Control::SetLocation(Point point)
{
x = point.X;
y = point.Y;
}
void Control::SetSize(Size size)
{
width = size.Width;
height = size.Height;
}
std::string Control::GetText()
{
if (IsHandledCreated())
{
HWND hWnd = (HWND)GetHandle().ToPointer();
int len = GetWindowTextLength(hWnd);
wchar_t* str = new wchar_t[len + 1]; // fix
GetWindowText(hWnd, str, len);
return string(WStringToAnsi(str));
}
return text;
}
void Control::SetText(string text)
{
if (IsHandledCreated())
{
wstring str = AnsiToWString(text);
SetWindowText((HWND)GetHandle().ToPointer(), str.c_str());
}
this->text = text;
}
void Control::Show()
{
visible = true;
}
bool Control::IsHandledCreated()
{
return window->GetHandle() == IntPtr::Zero;
}
void Control::OnPaint()
{
}
void Control::OnTextChanged()
{
}
void Control::DefWndProc(Message * msg)
{
window->DefWndProc(msg);
}
void Control::WndProc(Message * msg)
{
switch (msg->Msg)
{
case WM_PAINT:
OnPaint();
break;
case WM_DESTROY:
PostQuitMessage(0);
default:
DefWndProc(msg);
};
}
void Control::CreateHandle()
{
CreateParams cp = GetCreateParams();
SetLocation(Point(cp.GetX(), cp.GetY()));
window->CreateHandle(&cp);
}
CreateParams Control::GetCreateParams()
{
CreateParams cp;
cp.SetParent(parent->GetHandle());
cp.SetCaption(text);
return cp;
}
Control::ControlNativeWindow::ControlNativeWindow(Control* control)
{
wstring ws = AnsiToWString(control->GetName());
OutputDebugString(ws.c_str());
this->control = static_cast<Control*>(control);
ws = AnsiToWString(this->control->GetName());
OutputDebugString(ws.c_str());
}
void Control::ControlNativeWindow::WndProc(Message * msg)
{
// HERE IS THE ISSUE
// IT IS OUTPUTTING WINDOW ALWAYS
// HOWEVER, IT SHOULD OUTPUT THE CORRECT DERIVED CLASS
wstring ws = AnsiToWString(control->GetName());
OutputDebugString(ws.c_str());
control->WndProc(msg);
}
NATIVEWINDOW.H
#ifndef NATIVE_WINDOW_H
#define NATIVE_WINDOW_H
#include <string>
#include "IntPtr.h"
class CreateParams;
class Message;
class NativeWindow
{
public:
NativeWindow();
~NativeWindow();
virtual void CreateHandle(CreateParams* cp);
IntPtr GetHandle() { return handle; }
virtual void DefWndProc(Message* msg);
virtual void WndProc(Message* msg);
virtual std::string GetName() { return "NATIVEWindow\n"; }
private:
IntPtr handle;
class WindowClass
{
public:
WindowClass(std::string className, int classStyle);
std::string GetClsName() { return className; }
int GetClassStyle() { return classStyle; }
private:
void registerClass();
friend NativeWindow;
NativeWindow* targetWindow;
std::string className;
int classStyle;
};
};
#endif // NATIVE_WINDOW_H
NATIVEWINDOW.CPP
#include "NativeWindow.h"
#include <Windows.h>
#include <string>
#include "CreateParams.h"
#include "Message.h"
#include "Utility.h"
using namespace std;
LRESULT CALLBACK InternalWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
NativeWindow::NativeWindow() : handle(IntPtr::Zero)
{
}
NativeWindow::~NativeWindow()
{
}
void NativeWindow::CreateHandle(CreateParams* cp)
{
WindowClass windowClass(cp->GetClsName(), cp->GetClassStyle());
wstring wideClassName = AnsiToWString(windowClass.GetClsName());
wstring wideCaption = AnsiToWString(cp->GetCaption());
HWND hWnd = CreateWindowEx(
cp->GetExStyle(),
wideClassName.c_str(),
wideCaption.c_str(),
cp->GetStyle(),
cp->GetX(), cp->GetY(),
cp->GetWidth(), cp->GetHeight(),
(HWND)cp->GetParent().ToPointer(),
nullptr,
nullptr,
nullptr
);
if (!hWnd)
{
MessageBox(nullptr, L"Call to CreateWindow Failed", L"FAIL", MB_OK);
return;
}
handle = hWnd;
windowClass.targetWindow = this;
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)this);
}
void NativeWindow::WndProc(Message* msg)
{
DefWndProc(msg);
}
void NativeWindow::DefWndProc(Message * msg)
{
DefWindowProc((HWND)msg->HWnd.ToPointer(), (LRESULT)msg->Result.ToPointer(), (WPARAM)msg->WParam.ToPointer(), (LPARAM)msg->LParam.ToPointer());
}
NativeWindow::WindowClass::WindowClass(std::string className, int classStyle)
{
this->className = className;
this->classStyle = classStyle;
registerClass();
}
void NativeWindow::WindowClass::registerClass()
{
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = classStyle;
wndclass.lpfnWndProc = InternalWndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = nullptr;
wndclass.hIcon = LoadIcon(nullptr, MAKEINTRESOURCE(IDI_APPLICATION));
wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszMenuName = nullptr;
wstring ws = AnsiToWString(className);
wndclass.lpszClassName = ws.c_str();
wndclass.hIconSm = LoadIcon(nullptr, MAKEINTRESOURCE(IDI_APPLICATION));
WNDCLASSEX wcex;
wcex.lpszClassName = ws.c_str();
bool found = GetClassInfoEx(nullptr, ws.c_str(), &wcex);
if (found)
return;
if (!RegisterClassEx(&wndclass))
{
DWORD dw = GetLastError();
if (dw == ERROR_CLASS_ALREADY_EXISTS)
{
MessageBox(nullptr, L"Class already exists", L"SUCCESS", MB_OK);
}
else
{
MessageBox(nullptr, L"Call to RegisterClassEx Failed", L"FAIL", MB_OK);
}
}
}
LRESULT CALLBACK InternalWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
NativeWindow* window = reinterpret_cast<NativeWindow*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
if (window) {
Message msg = Message::Create(hWnd, message, IntPtr((void*)wParam), IntPtr((void*)lParam));
window->WndProc(&msg);
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
WINDOW.H
#ifndef WINDOW_H
#define WINDOW_H
#include <Windows.h>
#include "Control.h"
#include "ControlCollection.h"
class Window : public Control
{
public:
Window();
~Window();
void Show();
virtual std::string GetName() { return "Window\n"; }
protected:
virtual CreateParams GetCreateParams();
private:
ControlCollection controls;
};
#endif // WINDOW_H
TEXTBOX.H
#ifndef TEXTBOX_H
#define TEXTBOX_H
#include "Control.h"
class TextBox : public Control
{
public:
TextBox();
~TextBox();
virtual std::string GetName() { return "TextBox\n"; }
protected:
virtual void WndProc(Message* msg);
virtual void OnTextChanged();
virtual CreateParams GetCreateParams();
private:
void reflectCommand(Message* msg);
};
#endif // TEXTBOX_H
A C++ object doesn't become a member of a derived class until after the base class' constructor finishes. In short, the object is not textbox type until after the control constructor leaves.
Consider an object with a vtable:
class A { public: A() x() { doit(); } virtual void doit(); private: int x; }
and a derived class:
class B { public: virtual void doit(); private: std::string myname; }
A B object upon entering A's constructor body is like this:
+--------------------+
| A vtable | // A vtable
+--------------------+
| int x | // A's member (0 from initializer list)
+--------------------+
| std::string myname | // B's member (uninit)
+--------------------+
Note that if B::doit() executes, it will access the uninitialized myname.
B's constructor will reassign the vtable pointer to B vtable and run myname's constructor, but that's after we've already executed the A constructor body. Java does this differently, among others, since reflection requires the object not to change type at runtime.
Calling one of the object's virtual methods therefore doesn't refer to the derived type's override yet.
Often, objects have an init method of some kind that the user is required to use after construction to allow derived classes to participate in initialization.
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>(¶ms)));
}
void rewind() {
MCI_SEEK_PARMS params;
checkerror(mciSendCommand(_device, MCI_SEEK, MCI_WAIT | MCI_SEEK_TO_START, reinterpret_cast<DWORD_PTR>(¶ms)));
}
void pause() {
MCI_GENERIC_PARMS params;
params.dwCallback = NULL;
checkerror(mciSendCommand(_device, MCI_PAUSE, MCI_WAIT, reinterpret_cast<DWORD_PTR>(¶ms)));
}
void stop() {
MCI_GENERIC_PARMS params;
params.dwCallback = NULL;
checkerror(mciSendCommand(_device, MCI_STOP, MCI_WAIT, reinterpret_cast<DWORD_PTR>(¶ms)));
}
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>(¶ms)));
}
};
#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;
}