diff --git a/src/launcher/html/doc_host_ui_handler.cpp b/src/launcher/html/doc_host_ui_handler.cpp
new file mode 100644
index 0000000..ceab8d9
--- /dev/null
+++ b/src/launcher/html/doc_host_ui_handler.cpp
@@ -0,0 +1,115 @@
+#include
+#include "../html_frame.hpp"
+
+doc_host_ui_handler::doc_host_ui_handler(html_frame* frame): frame_(frame)
+{
+}
+
+HRESULT doc_host_ui_handler::QueryInterface(REFIID riid, LPVOID* ppvObj)
+{
+ auto client_site = this->frame_->get_client_site();
+ if (client_site)
+ {
+ return client_site->QueryInterface(riid, ppvObj);
+ }
+
+ return E_NOINTERFACE;
+}
+
+ULONG doc_host_ui_handler::AddRef()
+{
+ return 1;
+}
+
+ULONG doc_host_ui_handler::Release()
+{
+ return 1;
+}
+
+HRESULT doc_host_ui_handler::ShowContextMenu(DWORD /*dwID*/, POINT* /*ppt*/, IUnknown* /*pcmdtReserved*/,
+ IDispatch* /*pdispReserved*/)
+{
+ return S_OK;
+}
+
+HRESULT doc_host_ui_handler::ShowUI(DWORD /*dwID*/, IOleInPlaceActiveObject* /*pActiveObject*/,
+ IOleCommandTarget* /*pCommandTarget*/,
+ IOleInPlaceFrame* /*pFrame*/, IOleInPlaceUIWindow* /*pDoc*/)
+{
+ return S_OK;
+}
+
+HRESULT doc_host_ui_handler::HideUI()
+{
+ return S_OK;
+}
+
+HRESULT doc_host_ui_handler::UpdateUI()
+{
+ return S_OK;
+}
+
+HRESULT doc_host_ui_handler::EnableModeless(BOOL /*fEnable*/)
+{
+ return S_OK;
+}
+
+HRESULT doc_host_ui_handler::OnDocWindowActivate(BOOL /*fActivate*/)
+{
+ return S_OK;
+}
+
+HRESULT doc_host_ui_handler::OnFrameWindowActivate(BOOL /*fActivate*/)
+{
+ return S_OK;
+}
+
+HRESULT doc_host_ui_handler::ResizeBorder(LPCRECT /*prcBorder*/, IOleInPlaceUIWindow* /*pUIWindow*/,
+ BOOL /*fRameWindow*/)
+{
+ return S_OK;
+}
+
+HRESULT doc_host_ui_handler::TranslateAcceleratorA(LPMSG /*lpMsg*/, const GUID* pguidCmdGroup, DWORD /*nCmdID*/)
+{
+ pguidCmdGroup = nullptr;
+ return S_FALSE;
+}
+
+HRESULT doc_host_ui_handler::GetOptionKeyPath(LPOLESTR* /*pchKey*/, DWORD /*dw*/)
+{
+ return S_FALSE;
+}
+
+HRESULT doc_host_ui_handler::GetDropTarget(IDropTarget* /*pDropTarget*/, IDropTarget** /*ppDropTarget*/)
+{
+ return S_FALSE;
+}
+
+HRESULT doc_host_ui_handler::GetExternal(IDispatch** ppDispatch)
+{
+ *ppDispatch = nullptr;
+ return S_FALSE;
+}
+
+HRESULT doc_host_ui_handler::FilterDataObject(IDataObject* /*pDO*/, IDataObject** ppDORet)
+{
+ *ppDORet = nullptr;
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE doc_host_ui_handler::TranslateUrl(DWORD /*dwTranslate*/, OLECHAR __RPC_FAR* /*pchURLIn*/,
+ OLECHAR __RPC_FAR* __RPC_FAR* ppchURLOut)
+{
+ *ppchURLOut = nullptr;
+ return S_FALSE;
+}
+
+HRESULT doc_host_ui_handler::GetHostInfo(DOCHOSTUIINFO __RPC_FAR * pInfo)
+{
+ pInfo->cbSize = sizeof(DOCHOSTUIINFO);
+ pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIFLAG_DPI_AWARE | DOCHOSTUIFLAG_SCROLL_NO;
+ pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;
+
+ return S_OK;
+}
diff --git a/src/launcher/html/doc_host_ui_handler.hpp b/src/launcher/html/doc_host_ui_handler.hpp
new file mode 100644
index 0000000..0b538b4
--- /dev/null
+++ b/src/launcher/html/doc_host_ui_handler.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+class html_frame;
+
+class doc_host_ui_handler final : public IDocHostUIHandler
+{
+public:
+ doc_host_ui_handler(html_frame* frame);
+ virtual ~doc_host_ui_handler() = default;
+
+private:
+ html_frame* frame_;
+
+public: // IDocHostUIHandler interface
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppvObj) override;
+ ULONG STDMETHODCALLTYPE AddRef() override;
+ ULONG STDMETHODCALLTYPE Release() override;
+ HRESULT STDMETHODCALLTYPE ShowContextMenu(
+ DWORD dwID,
+ POINT __RPC_FAR * ppt,
+ IUnknown __RPC_FAR * pcmdtReserved,
+ IDispatch __RPC_FAR * pdispReserved) override;
+ HRESULT STDMETHODCALLTYPE ShowUI(
+ DWORD dwID,
+ IOleInPlaceActiveObject __RPC_FAR * pActiveObject,
+ IOleCommandTarget __RPC_FAR * pCommandTarget,
+ IOleInPlaceFrame __RPC_FAR * pFrame,
+ IOleInPlaceUIWindow __RPC_FAR * pDoc) override;
+ HRESULT STDMETHODCALLTYPE GetHostInfo(DOCHOSTUIINFO __RPC_FAR * pInfo) override;
+ HRESULT STDMETHODCALLTYPE HideUI() override;
+ HRESULT STDMETHODCALLTYPE UpdateUI() override;
+ HRESULT STDMETHODCALLTYPE EnableModeless(BOOL fEnable) override;
+ HRESULT STDMETHODCALLTYPE OnDocWindowActivate(BOOL fActivate) override;
+ HRESULT STDMETHODCALLTYPE OnFrameWindowActivate(BOOL fActivate) override;
+ HRESULT STDMETHODCALLTYPE ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow __RPC_FAR * pUIWindow,
+ BOOL fRameWindow) override;
+ HRESULT STDMETHODCALLTYPE TranslateAccelerator(LPMSG lpMsg, const GUID __RPC_FAR * pguidCmdGroup, DWORD nCmdID)
+ override;
+ HRESULT STDMETHODCALLTYPE GetOptionKeyPath(LPOLESTR __RPC_FAR * pchKey, DWORD dw) override;
+ HRESULT STDMETHODCALLTYPE GetDropTarget(IDropTarget __RPC_FAR * pDropTarget,
+ IDropTarget __RPC_FAR *__RPC_FAR * ppDropTarget) override;
+ HRESULT STDMETHODCALLTYPE GetExternal(IDispatch __RPC_FAR *__RPC_FAR * ppDispatch) override;
+ HRESULT STDMETHODCALLTYPE TranslateUrl(DWORD dwTranslate, OLECHAR __RPC_FAR * pchURLIn,
+ OLECHAR __RPC_FAR *__RPC_FAR * ppchURLOut) override;
+ HRESULT STDMETHODCALLTYPE FilterDataObject(IDataObject __RPC_FAR * pDO, IDataObject __RPC_FAR *__RPC_FAR * ppDORet)
+ override;
+};
diff --git a/src/launcher/html/ole_client_site.cpp b/src/launcher/html/ole_client_site.cpp
new file mode 100644
index 0000000..2bc2b11
--- /dev/null
+++ b/src/launcher/html/ole_client_site.cpp
@@ -0,0 +1,77 @@
+#include
+#include "../html_frame.hpp"
+
+ole_client_site::ole_client_site(html_frame* frame): frame_(frame)
+{
+}
+
+HRESULT ole_client_site::QueryInterface(REFIID riid, LPVOID* ppvObject)
+{
+ if (!memcmp(&riid, &IID_IUnknown, sizeof(GUID)) ||
+ !memcmp(&riid, &IID_IOleClientSite, sizeof(GUID)))
+ {
+ *ppvObject = this;
+ this->AddRef();
+ return S_OK;
+ }
+
+ if (!memcmp(&riid, &IID_IOleInPlaceSite, sizeof(GUID)))
+ {
+ auto in_place_site = this->frame_->get_in_place_site();
+ in_place_site->AddRef();
+ *ppvObject = in_place_site;
+ return S_OK;
+ }
+
+ if (!memcmp(&riid, &IID_IDocHostUIHandler, sizeof(GUID)))
+ {
+ auto ui_handler = this->frame_->get_ui_handler();
+ ui_handler->AddRef();
+ *ppvObject = ui_handler;
+ return S_OK;
+ }
+
+ *ppvObject = nullptr;
+ return E_NOINTERFACE;
+}
+
+ULONG ole_client_site::AddRef()
+{
+ return 1;
+}
+
+ULONG ole_client_site::Release()
+{
+ return 1;
+}
+
+HRESULT ole_client_site::SaveObject()
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ole_client_site::GetMoniker(DWORD /*dwAssign*/, DWORD /*dwWhichMoniker*/, IMoniker** /*ppmk*/)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ole_client_site::GetContainer(LPOLECONTAINER* ppContainer)
+{
+ *ppContainer = nullptr;
+ return E_NOINTERFACE;
+}
+
+HRESULT ole_client_site::ShowObject()
+{
+ return NOERROR;
+}
+
+HRESULT ole_client_site::OnShowWindow(BOOL /*fShow*/)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ole_client_site::RequestNewObjectLayout()
+{
+ return E_NOTIMPL;
+}
diff --git a/src/launcher/html/ole_client_site.hpp b/src/launcher/html/ole_client_site.hpp
new file mode 100644
index 0000000..d0adc80
--- /dev/null
+++ b/src/launcher/html/ole_client_site.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+class html_frame;
+
+class ole_client_site final : public IOleClientSite
+{
+public:
+ ole_client_site(html_frame* frame);
+ virtual ~ole_client_site() = default;
+
+private:
+ html_frame* frame_;
+
+public:
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppvObject) override;
+ ULONG STDMETHODCALLTYPE AddRef() override;
+ ULONG STDMETHODCALLTYPE Release() override;
+ HRESULT STDMETHODCALLTYPE SaveObject() override;
+ HRESULT STDMETHODCALLTYPE GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk) override;
+ HRESULT STDMETHODCALLTYPE GetContainer(LPOLECONTAINER FAR* ppContainer) override;
+ HRESULT STDMETHODCALLTYPE ShowObject() override;
+ HRESULT STDMETHODCALLTYPE OnShowWindow(BOOL fShow) override;
+ HRESULT STDMETHODCALLTYPE RequestNewObjectLayout() override;
+};
diff --git a/src/launcher/html/ole_in_place_frame.cpp b/src/launcher/html/ole_in_place_frame.cpp
new file mode 100644
index 0000000..b9612d5
--- /dev/null
+++ b/src/launcher/html/ole_in_place_frame.cpp
@@ -0,0 +1,82 @@
+#include
+#include "../html_frame.hpp"
+
+ole_in_place_frame::ole_in_place_frame(html_frame* frame): frame_(frame)
+{
+}
+
+HRESULT ole_in_place_frame::QueryInterface(REFIID /*riid*/, LPVOID* /*ppvObj*/)
+{
+ return E_NOTIMPL;
+}
+
+ULONG ole_in_place_frame::AddRef()
+{
+ return 1;
+}
+
+ULONG ole_in_place_frame::Release()
+{
+ return 1;
+}
+
+HRESULT ole_in_place_frame::GetWindow(HWND* lphwnd)
+{
+ *lphwnd = this->frame_->get_window();
+ return S_OK;
+}
+
+HRESULT ole_in_place_frame::ContextSensitiveHelp(BOOL /*fEnterMode*/)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ole_in_place_frame::GetBorder(LPRECT /*lprectBorder*/)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ole_in_place_frame::RequestBorderSpace(LPCBORDERWIDTHS /*pborderwidths*/)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ole_in_place_frame::SetBorderSpace(LPCBORDERWIDTHS /*pborderwidths*/)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ole_in_place_frame::SetActiveObject(IOleInPlaceActiveObject* /*pActiveObject*/, LPCOLESTR /*pszObjName*/)
+{
+ return S_OK;
+}
+
+HRESULT ole_in_place_frame::InsertMenus(HMENU /*hmenuShared*/, LPOLEMENUGROUPWIDTHS /*lpMenuWidths*/)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ole_in_place_frame::SetMenu(HMENU /*hmenuShared*/, HOLEMENU /*holemenu*/, HWND /*hwndActiveObject*/)
+{
+ return S_OK;
+}
+
+HRESULT ole_in_place_frame::RemoveMenus(HMENU /*hmenuShared*/)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ole_in_place_frame::SetStatusText(LPCOLESTR /*pszStatusText*/)
+{
+ return S_OK;
+}
+
+HRESULT ole_in_place_frame::EnableModeless(BOOL /*fEnable*/)
+{
+ return S_OK;
+}
+
+HRESULT ole_in_place_frame::TranslateAcceleratorA(LPMSG /*lpmsg*/, WORD /*wID*/)
+{
+ return E_NOTIMPL;
+}
diff --git a/src/launcher/html/ole_in_place_frame.hpp b/src/launcher/html/ole_in_place_frame.hpp
new file mode 100644
index 0000000..4a39d7f
--- /dev/null
+++ b/src/launcher/html/ole_in_place_frame.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+class html_frame;
+
+class ole_in_place_frame final : public IOleInPlaceFrame
+{
+public:
+ ole_in_place_frame(html_frame* frame);
+ virtual ~ole_in_place_frame() = default;
+
+private:
+ html_frame* frame_;
+
+public: // IOleInPlaceFrame interface
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID FAR* ppvObj) override;
+ ULONG STDMETHODCALLTYPE AddRef() override;
+ ULONG STDMETHODCALLTYPE Release() override;
+ HRESULT STDMETHODCALLTYPE GetWindow(HWND FAR* lphwnd) override;
+ HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode) override;
+ HRESULT STDMETHODCALLTYPE GetBorder(LPRECT lprectBorder) override;
+ HRESULT STDMETHODCALLTYPE RequestBorderSpace(LPCBORDERWIDTHS pborderwidths) override;
+ HRESULT STDMETHODCALLTYPE SetBorderSpace(LPCBORDERWIDTHS pborderwidths) override;
+ HRESULT STDMETHODCALLTYPE SetActiveObject(IOleInPlaceActiveObject* pActiveObject, LPCOLESTR pszObjName) override;
+ HRESULT STDMETHODCALLTYPE InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths) override;
+ HRESULT STDMETHODCALLTYPE SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject) override;
+ HRESULT STDMETHODCALLTYPE RemoveMenus(HMENU hmenuShared) override;
+ HRESULT STDMETHODCALLTYPE SetStatusText(LPCOLESTR pszStatusText) override;
+ HRESULT STDMETHODCALLTYPE EnableModeless(BOOL fEnable) override;
+ HRESULT STDMETHODCALLTYPE TranslateAccelerator(LPMSG lpmsg, WORD wID) override;
+};
diff --git a/src/launcher/html/ole_in_place_site.cpp b/src/launcher/html/ole_in_place_site.cpp
new file mode 100644
index 0000000..0623eb2
--- /dev/null
+++ b/src/launcher/html/ole_in_place_site.cpp
@@ -0,0 +1,105 @@
+#include
+#include "../html_frame.hpp"
+
+ole_in_place_site::ole_in_place_site(html_frame* frame) : frame_(frame)
+{
+}
+
+HRESULT ole_in_place_site::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
+{
+ auto client_site = this->frame_->get_client_site();
+ if (client_site)
+ {
+ return client_site->QueryInterface(riid, ppvObj);
+ }
+
+ return E_NOINTERFACE;
+}
+
+ULONG ole_in_place_site::AddRef()
+{
+ return 1;
+}
+
+ULONG ole_in_place_site::Release()
+{
+ return 1;
+}
+
+HRESULT ole_in_place_site::GetWindow(HWND* lphwnd)
+{
+ *lphwnd = this->frame_->get_window();
+ return S_OK;
+}
+
+HRESULT ole_in_place_site::ContextSensitiveHelp(BOOL /*fEnterMode*/)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ole_in_place_site::CanInPlaceActivate()
+{
+ return S_OK;
+}
+
+HRESULT ole_in_place_site::OnInPlaceActivate()
+{
+ return S_OK;
+}
+
+HRESULT ole_in_place_site::OnUIActivate()
+{
+ return S_OK;
+}
+
+HRESULT ole_in_place_site::GetWindowContext(LPOLEINPLACEFRAME* lplpFrame, LPOLEINPLACEUIWINDOW* lplpDoc,
+ LPRECT /*lprcPosRect*/, LPRECT /*lprcClipRect*/,
+ LPOLEINPLACEFRAMEINFO lpFrameInfo)
+{
+ *lplpFrame = this->frame_->get_in_place_frame();
+ *lplpDoc = nullptr;
+
+ lpFrameInfo->fMDIApp = FALSE;
+ lpFrameInfo->hwndFrame = this->frame_->get_window();
+ lpFrameInfo->haccel = nullptr;
+ lpFrameInfo->cAccelEntries = 0;
+
+ return S_OK;
+}
+
+HRESULT ole_in_place_site::Scroll(SIZE /*scrollExtent*/)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ole_in_place_site::OnUIDeactivate(BOOL /*fUndoable*/)
+{
+ return S_OK;
+}
+
+HRESULT ole_in_place_site::OnInPlaceDeactivate()
+{
+ return S_OK;
+}
+
+HRESULT ole_in_place_site::DiscardUndoState()
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ole_in_place_site::DeactivateAndUndo()
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ole_in_place_site::OnPosRectChange(LPCRECT lprcPosRect)
+{
+ IOleInPlaceObject* in_place = nullptr;
+ if (!this->frame_->get_browser_object()->QueryInterface(IID_IOleInPlaceObject, reinterpret_cast(&in_place)))
+ {
+ in_place->SetObjectRects(lprcPosRect, lprcPosRect);
+ in_place->Release();
+ }
+
+ return S_OK;
+}
diff --git a/src/launcher/html/ole_in_place_site.hpp b/src/launcher/html/ole_in_place_site.hpp
new file mode 100644
index 0000000..3dad18c
--- /dev/null
+++ b/src/launcher/html/ole_in_place_site.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+class html_frame;
+
+class ole_in_place_site final : public IOleInPlaceSite
+{
+public:
+ ole_in_place_site(html_frame* frame);
+ virtual ~ole_in_place_site() = default;
+
+private:
+ html_frame* frame_;
+
+public: // IOleInPlaceSite interface
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID FAR* ppvObj) override;
+ ULONG STDMETHODCALLTYPE AddRef() override;
+ ULONG STDMETHODCALLTYPE Release() override;
+ HRESULT STDMETHODCALLTYPE GetWindow(HWND FAR* lphwnd) override;
+ HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode) override;
+ HRESULT STDMETHODCALLTYPE CanInPlaceActivate() override;
+ HRESULT STDMETHODCALLTYPE OnInPlaceActivate() override;
+ HRESULT STDMETHODCALLTYPE OnUIActivate() override;
+ HRESULT STDMETHODCALLTYPE GetWindowContext(LPOLEINPLACEFRAME FAR* lplpFrame, LPOLEINPLACEUIWINDOW FAR* lplpDoc,
+ LPRECT lprcPosRect, LPRECT lprcClipRect,
+ LPOLEINPLACEFRAMEINFO lpFrameInfo) override;
+ HRESULT STDMETHODCALLTYPE Scroll(SIZE scrollExtent) override;
+ HRESULT STDMETHODCALLTYPE OnUIDeactivate(BOOL fUndoable) override;
+ HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate() override;
+ HRESULT STDMETHODCALLTYPE DiscardUndoState() override;
+ HRESULT STDMETHODCALLTYPE DeactivateAndUndo() override;
+ HRESULT STDMETHODCALLTYPE OnPosRectChange(LPCRECT lprcPosRect) override;
+};
diff --git a/src/launcher/html_frame.cpp b/src/launcher/html_frame.cpp
new file mode 100644
index 0000000..a35e42d
--- /dev/null
+++ b/src/launcher/html_frame.cpp
@@ -0,0 +1,142 @@
+#include
+#include "html_frame.hpp"
+
+html_frame::html_frame() : in_place_frame_(this), in_place_site_(this), ui_handler_(this), client_site_(this)
+{
+ if (OleInitialize(nullptr) != S_OK)
+ {
+ throw std::runtime_error("Unable to initialize the OLE library");
+ }
+}
+
+html_frame::~html_frame()
+{
+ OleUninitialize();
+}
+
+HWND html_frame::get_window() const
+{
+ return this->window_;
+}
+
+std::shared_ptr html_frame::get_browser_object() const
+{
+ return this->browser_object_;
+}
+
+ole_in_place_frame* html_frame::get_in_place_frame()
+{
+ return &this->in_place_frame_;
+}
+
+ole_in_place_site* html_frame::get_in_place_site()
+{
+ return &this->in_place_site_;
+}
+
+doc_host_ui_handler* html_frame::get_ui_handler()
+{
+ return &this->ui_handler_;
+}
+
+ole_client_site* html_frame::get_client_site()
+{
+ return &this->client_site_;
+}
+
+std::shared_ptr html_frame::get_web_browser() const
+{
+ IWebBrowser2* web_browser;
+ if (!this->browser_object_->QueryInterface(IID_IWebBrowser2, reinterpret_cast(&web_browser)))
+ {
+ return std::shared_ptr(web_browser, [](IWebBrowser2* web_browser)
+ {
+ if (web_browser)
+ {
+ web_browser->Release();
+ }
+ });
+ }
+
+ return {};
+}
+
+void html_frame::initialize(const HWND window)
+{
+ if (this->window_) return;
+ this->window_ = window;
+
+ this->create_browser();
+ this->initialize_browser();
+}
+
+void html_frame::create_browser()
+{
+ LPCLASSFACTORY class_factory = nullptr;
+ if (CoGetClassObject(CLSID_WebBrowser, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, nullptr, IID_IClassFactory,
+ reinterpret_cast(&class_factory)) || !class_factory)
+ {
+ throw std::runtime_error("Unable to get the class factory");
+ }
+
+ IOleObject* browser_object = nullptr;
+ class_factory->CreateInstance(nullptr, IID_IOleObject, reinterpret_cast(&browser_object));
+ class_factory->Release();
+
+ if (!browser_object)
+ {
+ throw std::runtime_error("Unable to create browser object");
+ }
+
+ this->browser_object_ = std::shared_ptr(browser_object, [](IOleObject* browser_object)
+ {
+ if (browser_object)
+ {
+ browser_object->Close(OLECLOSE_NOSAVE);
+ browser_object->Release();
+ }
+ });
+}
+
+void html_frame::initialize_browser()
+{
+ this->browser_object_->SetClientSite(this->get_client_site());
+ this->browser_object_->SetHostNames(L"Hostname", nullptr);
+
+ RECT rect;
+ GetClientRect(this->get_window(), &rect);
+ OleSetContainedObject(this->browser_object_.get(), TRUE);
+
+ this->browser_object_->DoVerb(OLEIVERB_SHOW, nullptr, this->get_client_site(), -1, this->get_window(), &rect);
+ this->resize(rect.right, rect.bottom);
+}
+
+void html_frame::resize(const DWORD width, const DWORD height) const
+{
+ auto web_browser = this->get_web_browser();
+ if (web_browser)
+ {
+ web_browser->put_Left(0);
+ web_browser->put_Top(0);
+ web_browser->put_Width(width);
+ web_browser->put_Height(height);
+ }
+}
+
+void html_frame::load_url(std::string url) const
+{
+ auto web_browser = this->get_web_browser();
+ if (web_browser)
+ {
+ std::wstring wide_url(url.begin(), url.end());
+
+ VARIANT my_url;
+ VariantInit(&my_url);
+ my_url.vt = VT_BSTR;
+ my_url.bstrVal = SysAllocString(wide_url.data());
+
+ web_browser->Navigate2(&my_url, nullptr, nullptr, nullptr, nullptr);
+
+ VariantClear(&my_url);
+ }
+}
diff --git a/src/launcher/html_frame.hpp b/src/launcher/html_frame.hpp
new file mode 100644
index 0000000..aaee1b7
--- /dev/null
+++ b/src/launcher/html_frame.hpp
@@ -0,0 +1,40 @@
+#pragma once
+#include "window.hpp"
+#include "html/ole_in_place_frame.hpp"
+#include "html/ole_in_place_site.hpp"
+#include "html/doc_host_ui_handler.hpp"
+#include "html/ole_client_site.hpp"
+
+class html_frame final
+{
+public:
+ html_frame();
+ ~html_frame();
+
+ void initialize(const HWND window);
+
+ void resize(DWORD width, DWORD height) const;
+ void load_url(std::string url) const;
+
+ HWND get_window() const;
+
+ std::shared_ptr get_browser_object() const;
+ std::shared_ptr get_web_browser() const;
+
+ ole_in_place_frame* get_in_place_frame();
+ ole_in_place_site* get_in_place_site();
+ doc_host_ui_handler* get_ui_handler();
+ ole_client_site* get_client_site();
+
+private:
+ HWND window_ = nullptr;
+ std::shared_ptr browser_object_;
+
+ ole_in_place_frame in_place_frame_;
+ ole_in_place_site in_place_site_;
+ doc_host_ui_handler ui_handler_;
+ ole_client_site client_site_;
+
+ void create_browser();
+ void initialize_browser();
+};
diff --git a/src/launcher/image.cpp b/src/launcher/image.cpp
deleted file mode 100644
index afcf3c3..0000000
--- a/src/launcher/image.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-#include
-#include "image.hpp"
-
-image::image(const size_t resource)
-{
- this->bitmap_ = LoadBitmapA(GetModuleHandleA(nullptr), MAKEINTRESOURCEA(resource));
-}
-
-image::~image()
-{
- DeleteBitmap(this->bitmap_);
-}
-
-void image::set_position(const POINT& position)
-{
- this->position_ = position;
-}
-
-void image::set_size(const POINT& size)
-{
- this->size_ = size;
-}
-
-bool image::is_hovered(const POINT& mouse) const
-{
- return mouse.x >= this->position_.x && mouse.x < (this->position_.x + this->size_.x)
- && mouse.y >= this->position_.y && mouse.y < (this->position_.y + this->size_.y);
-}
-
-void image::paint(const HDC hdc, const POINT& mouse) const
-{
- BITMAP bitmap;
- GetObject(this->bitmap_, sizeof(bitmap), &bitmap);
-
- const auto dc = CreateCompatibleDC(hdc);
- SelectObject(dc, this->bitmap_);
-
- const LONG modifier = 2;
- LONG size_offset = 0;
- LONG position_offset = 0;
- if (this->is_hovered(mouse))
- {
- size_offset = modifier * 2;
- position_offset = -modifier;
- }
-
- SetStretchBltMode(hdc, HALFTONE);
- StretchBlt(hdc, this->position_.x + position_offset, this->position_.y + position_offset,
- this->size_.x + size_offset, this->size_.y + size_offset, dc, 0, 0, bitmap.bmWidth,
- bitmap.bmHeight, SRCCOPY);
-
- DeleteDC(dc);
-}
-
-void image::set_click_listener(std::function callback)
-{
- this->callback_ = callback;
-}
-
-void image::click(const POINT& mouse, const bool down)
-{
- if (down)
- {
- this->down_handled_ = this->is_hovered(mouse);
- }
- else
- {
- if (this->down_handled_ && this->is_hovered(mouse) && this->callback_)
- {
- this->callback_();
- }
-
- this->down_handled_ = false;
- }
-}
diff --git a/src/launcher/image.hpp b/src/launcher/image.hpp
deleted file mode 100644
index ff54759..0000000
--- a/src/launcher/image.hpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-class image final
-{
-public:
- image(size_t resource);
- ~image();
-
- void set_position(const POINT& position);
- void set_size(const POINT& size);
-
- bool is_hovered(const POINT& mouse) const;
-
- void paint(HDC hdc, const POINT& mouse) const;
-
- void set_click_listener(std::function callback);
-
- void click(const POINT& mouse, bool down);
-
-private:
- POINT size_{};
- POINT position_{};
- HBITMAP bitmap_;
-
- bool down_handled_ = false;
- std::function callback_;
-};
diff --git a/src/launcher/launcher.cpp b/src/launcher/launcher.cpp
index ea66228..34b402f 100644
--- a/src/launcher/launcher.cpp
+++ b/src/launcher/launcher.cpp
@@ -1,19 +1,14 @@
#include
#include "launcher.hpp"
+#include "html_frame.hpp"
-launcher::launcher() : window_("Open-IW5", 615, 300), image_sp_(IMAGE_SP), image_mp_(IMAGE_MP)
+launcher::launcher()
{
- this->image_sp_.set_position({100, 90});
- this->image_mp_.set_position({401, 90});
-
- this->image_sp_.set_size({100, 100});
- this->image_mp_.set_size({100, 100});
-
- this->image_sp_.set_click_listener(std::bind(&launcher::select_mode, this, mode::singleplayer));
- this->image_mp_.set_click_listener(std::bind(&launcher::select_mode, this, mode::multiplayer));
-
this->window_.set_callback(std::bind(&launcher::handler, this, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3));
+
+ this->window_.create("Open-IW5", 615, 300);
+ this->html_frame_.load_url("https://google.com");
}
launcher::mode launcher::run() const
@@ -25,27 +20,16 @@ launcher::mode launcher::run() const
LRESULT launcher::handler(const UINT message, const WPARAM w_param, const LPARAM l_param)
{
- if (message == WM_PAINT)
+ if (message == WM_SIZE)
{
- this->paint();
+ this->html_frame_.resize(LOWORD(l_param), HIWORD(l_param));
+ return 0;
}
- else if (message == WM_MOUSEMOVE)
+
+ if (message == WM_CREATE)
{
- this->mouse_move(l_param);
- }
- else if (message == WM_LBUTTONDOWN)
- {
- this->image_sp_.click(this->mouse_, true);
- this->image_mp_.click(this->mouse_, true);
- }
- else if (message == WM_LBUTTONUP)
- {
- this->image_sp_.click(this->mouse_, false);
- this->image_mp_.click(this->mouse_, false);
- }
- else if (message == WM_ERASEBKGND)
- {
- return TRUE;
+ this->html_frame_.initialize(this->window_);
+ return 0;
}
return DefWindowProc(this->window_, message, w_param, l_param);
@@ -56,65 +40,3 @@ void launcher::select_mode(const mode mode)
this->mode_ = mode;
this->window_.close();
}
-
-void launcher::draw_text(const HDC hdc)
-{
- Gdiplus::Graphics graphics(hdc);
- Gdiplus::SolidBrush color(Gdiplus::Color(255, 150, 150, 150));
- Gdiplus::FontFamily font_family(L"Segoe UI");
- Gdiplus::Font font(&font_family, 20, Gdiplus::FontStyleRegular, Gdiplus::UnitPixel);
- const auto stringformat = Gdiplus::StringFormat::GenericTypographic();
-
- std::wstring sp(L"Singleplayer");
- std::wstring mp(L"Multiplayer");
-
- Gdiplus::RectF rect{};
- graphics.MeasureString(sp.data(), -1, &font, rect, stringformat, &rect);
-
- const Gdiplus::PointF pos{150 - (rect.Width / 2 + 2), 45};
- graphics.DrawString(sp.data(), -1, &font, pos, &color);
-
- rect = {};
- graphics.MeasureString(mp.data(), -1, &font, rect, stringformat, &rect);
- graphics.DrawString(mp.data(), -1, &font, {451 - (rect.Width / 2 + 4), 45}, &color);
-
- Gdiplus::Pen pen(Gdiplus::Color(50, 255, 255, 255), 1);
- graphics.DrawLine(&pen, 300, 0, 300, 600);
-}
-
-void launcher::paint() const
-{
- RECT rect;
- GetClientRect(this->window_, &rect);
- const int width = rect.right - rect.left;
- const int height = rect.bottom + rect.left;
-
- PAINTSTRUCT ps;
- const auto hdc = BeginPaint(this->window_, &ps);
-
- const auto hdc_copy = CreateCompatibleDC(hdc);
- const auto bitmap = CreateCompatibleBitmap(hdc, width, height);
- SelectObject(hdc_copy, bitmap);
-
- this->window_.clear(hdc_copy);
-
- this->draw_text(hdc_copy);
-
- this->image_sp_.paint(hdc_copy, this->mouse_);
- this->image_mp_.paint(hdc_copy, this->mouse_);
-
- BitBlt(hdc, 0, 0, width, height, hdc_copy, 0, 0, SRCCOPY);
-
- DeleteObject(bitmap);
- DeleteDC(hdc_copy);
-
- EndPaint(this->window_, &ps);
-}
-
-void launcher::mouse_move(const LPARAM l_param)
-{
- this->mouse_.x = GET_X_LPARAM(l_param);
- this->mouse_.y = GET_Y_LPARAM(l_param);
-
- InvalidateRect(this->window_, nullptr, FALSE);
-}
diff --git a/src/launcher/launcher.hpp b/src/launcher/launcher.hpp
index ff25d08..8b757fe 100644
--- a/src/launcher/launcher.hpp
+++ b/src/launcher/launcher.hpp
@@ -1,6 +1,6 @@
#pragma once
#include "window.hpp"
-#include "image.hpp"
+#include "html_frame.hpp"
class launcher final
{
@@ -21,17 +21,8 @@ private:
mode mode_ = none;
window window_;
-
- image image_sp_;
- image image_mp_;
-
- POINT mouse_{};
+ html_frame html_frame_;
LRESULT handler(const UINT message, const WPARAM w_param, const LPARAM l_param);
-
void select_mode(mode mode);
-
- static void draw_text(const HDC hdc);
- void paint() const;
- void mouse_move(LPARAM l_param);
};
diff --git a/src/launcher/window.cpp b/src/launcher/window.cpp
index adfd7cf..f2c1224 100644
--- a/src/launcher/window.cpp
+++ b/src/launcher/window.cpp
@@ -1,36 +1,35 @@
#include
#include "window.hpp"
-window::window(const std::string& title, const int width, const int height)
+window::window()
{
- Gdiplus::GdiplusStartupInput input;
- GdiplusStartup(&this->token_, &input, nullptr);
-
- const auto handle = GetModuleHandle(nullptr);
-
ZeroMemory(&this->wc_, sizeof(this->wc_));
- this->classname = "window-base-" + std::to_string(time(nullptr));
+ this->classname_ = "window-base-" + std::to_string(time(nullptr));
this->wc_.cbSize = sizeof(this->wc_);
this->wc_.style = CS_HREDRAW | CS_VREDRAW;
this->wc_.lpfnWndProc = static_processor;
- this->wc_.hInstance = handle;
+ this->wc_.hInstance = GetModuleHandle(nullptr);
this->wc_.hCursor = LoadCursor(nullptr, IDC_ARROW);
- this->wc_.hIcon = LoadIcon(handle, MAKEINTRESOURCE(102));
+ this->wc_.hIcon = LoadIcon(this->wc_.hInstance, MAKEINTRESOURCE(102));
this->wc_.hIconSm = this->wc_.hIcon;
- this->wc_.hbrBackground = CreateSolidBrush(RGB(35, 35, 35));
- this->wc_.lpszClassName = this->classname.data();
+ this->wc_.hbrBackground = HBRUSH(COLOR_WINDOW);
+ this->wc_.lpszClassName = this->classname_.data();
RegisterClassEx(&this->wc_);
+}
+void window::create(const std::string& title, const int width, const int height)
+{
const auto x = (GetSystemMetrics(SM_CXSCREEN) - width) / 2;
const auto y = (GetSystemMetrics(SM_CYSCREEN) - height) / 2;
this->handle_ = CreateWindowExA(NULL, this->wc_.lpszClassName, title.data(),
(WS_OVERLAPPEDWINDOW | WS_VISIBLE) & ~(WS_THICKFRAME | WS_MAXIMIZEBOX), x, y, width,
- height, nullptr, nullptr, handle, nullptr);
+ height, nullptr, nullptr, this->wc_.hInstance, this);
- SetWindowLongPtrA(*this, GWLP_USERDATA, LONG_PTR(this));
+ ShowWindow(this->handle_, SW_SHOW);
+ UpdateWindow(this->handle_);
}
window::~window()
@@ -38,8 +37,6 @@ window::~window()
this->close();
UnregisterClass(this->wc_.lpszClassName, this->wc_.hInstance);
DeleteObject(this->wc_.hbrBackground);
-
- Gdiplus::GdiplusShutdown(this->token_);
}
void window::close()
@@ -53,27 +50,13 @@ void window::close()
void window::run() const
{
MSG msg;
- while (this->handle_ && IsWindow(*this))
+ while (this->handle_ && IsWindow(*this) && GetMessage(&msg, nullptr, 0, 0))
{
- if (PeekMessageA(&msg, nullptr, NULL, NULL, PM_REMOVE))
- {
- TranslateMessage(&msg);
- DispatchMessageA(&msg);
- }
- else
- {
- std::this_thread::sleep_for(1ms);
- }
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
}
}
-void window::clear(const HDC hdc) const
-{
- RECT rc;
- GetClientRect(*this, &rc);
- FillRect(hdc, &rc, this->wc_.hbrBackground);
-}
-
void window::set_callback(const std::function& callback)
{
this->callback_ = callback;
@@ -81,6 +64,12 @@ void window::set_callback(const std::function& ca
LRESULT CALLBACK window::processor(const UINT message, const WPARAM w_param, const LPARAM l_param) const
{
+ if (message == WM_DESTROY)
+ {
+ PostQuitMessage(0);
+ return TRUE;
+ }
+
if (message == WM_KILL_WINDOW)
{
DestroyWindow(*this);
@@ -97,6 +86,14 @@ LRESULT CALLBACK window::processor(const UINT message, const WPARAM w_param, con
LRESULT CALLBACK window::static_processor(HWND hwnd, UINT message, WPARAM w_param, LPARAM l_param)
{
+ if (message == WM_CREATE)
+ {
+ auto data = reinterpret_cast(l_param);
+ SetWindowLongPtrA(hwnd, GWLP_USERDATA, LONG_PTR(data->lpCreateParams));
+
+ reinterpret_cast(data->lpCreateParams)->handle_ = hwnd;
+ }
+
const auto self = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (self) return self->processor(message, w_param, l_param);
diff --git a/src/launcher/window.hpp b/src/launcher/window.hpp
index ef6327a..3bbc1d2 100644
--- a/src/launcher/window.hpp
+++ b/src/launcher/window.hpp
@@ -5,23 +5,22 @@
class window final
{
public:
- window(const std::string& title, int width, int height);
+ window();
~window();
+ void create(const std::string& title, int width, int height);
+
void close();
void run() const;
- void clear(const HDC hdc) const;
-
void set_callback(const std::function& callback);
operator HWND() const;
private:
- ULONG_PTR token_;
WNDCLASSEX wc_{};
HWND handle_ = nullptr;
- std::string classname;
+ std::string classname_;
std::function callback_;
LRESULT CALLBACK processor(UINT message, WPARAM w_param, LPARAM l_param) const;
diff --git a/src/module/command.cpp b/src/module/command.cpp
index c40447a..dc5f40d 100644
--- a/src/module/command.cpp
+++ b/src/module/command.cpp
@@ -23,7 +23,10 @@ void command::add(const std::string& name, const std::function
-#include
-#include
-#include
+#include
+#include
+#include
#include
#include
#include