From 33bc8d9036f23d3e006a4c2eb55d32ca750a1b0c Mon Sep 17 00:00:00 2001 From: momo5502 Date: Tue, 10 May 2022 20:07:58 +0200 Subject: [PATCH] Prepare adding the launcher --- src/runner/CMakeLists.txt | 4 +- .../launcher/html/doc_host_ui_handler.cpp | 115 ++++ .../launcher/html/doc_host_ui_handler.hpp | 47 ++ src/runner/launcher/html/html_argument.cpp | 51 ++ src/runner/launcher/html/html_argument.hpp | 20 + src/runner/launcher/html/html_dispatch.cpp | 67 ++ src/runner/launcher/html/html_dispatch.hpp | 24 + src/runner/launcher/html/html_frame.cpp | 298 +++++++++ src/runner/launcher/html/html_frame.hpp | 67 ++ src/runner/launcher/html/html_window.cpp | 29 + src/runner/launcher/html/html_window.hpp | 15 + src/runner/launcher/html/ole_client_site.cpp | 77 +++ src/runner/launcher/html/ole_client_site.hpp | 24 + .../launcher/html/ole_in_place_frame.cpp | 82 +++ .../launcher/html/ole_in_place_frame.hpp | 30 + .../launcher/html/ole_in_place_site.cpp | 105 ++++ .../launcher/html/ole_in_place_site.hpp | 32 + src/runner/launcher/launcher.cpp | 33 + src/runner/launcher/launcher.hpp | 15 + src/runner/launcher/window.cpp | 208 +++++++ src/runner/launcher/window.hpp | 44 ++ src/runner/main.cpp | 4 + src/runner/resource.hpp | 1 + src/runner/resource.rc | 1 + src/runner/resources/main.html | 574 ++++++++++++++++++ src/runner/std_include.hpp | 6 + src/runner/utils/nt.cpp | 254 ++++++++ src/runner/utils/nt.hpp | 105 ++++ 28 files changed, 2330 insertions(+), 2 deletions(-) create mode 100644 src/runner/launcher/html/doc_host_ui_handler.cpp create mode 100644 src/runner/launcher/html/doc_host_ui_handler.hpp create mode 100644 src/runner/launcher/html/html_argument.cpp create mode 100644 src/runner/launcher/html/html_argument.hpp create mode 100644 src/runner/launcher/html/html_dispatch.cpp create mode 100644 src/runner/launcher/html/html_dispatch.hpp create mode 100644 src/runner/launcher/html/html_frame.cpp create mode 100644 src/runner/launcher/html/html_frame.hpp create mode 100644 src/runner/launcher/html/html_window.cpp create mode 100644 src/runner/launcher/html/html_window.hpp create mode 100644 src/runner/launcher/html/ole_client_site.cpp create mode 100644 src/runner/launcher/html/ole_client_site.hpp create mode 100644 src/runner/launcher/html/ole_in_place_frame.cpp create mode 100644 src/runner/launcher/html/ole_in_place_frame.hpp create mode 100644 src/runner/launcher/html/ole_in_place_site.cpp create mode 100644 src/runner/launcher/html/ole_in_place_site.hpp create mode 100644 src/runner/launcher/launcher.cpp create mode 100644 src/runner/launcher/launcher.hpp create mode 100644 src/runner/launcher/window.cpp create mode 100644 src/runner/launcher/window.hpp create mode 100644 src/runner/resources/main.html create mode 100644 src/runner/utils/nt.cpp create mode 100644 src/runner/utils/nt.hpp diff --git a/src/runner/CMakeLists.txt b/src/runner/CMakeLists.txt index 2af10c6..e56684b 100644 --- a/src/runner/CMakeLists.txt +++ b/src/runner/CMakeLists.txt @@ -1,5 +1,5 @@ -file(GLOB runner_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) -file(GLOB runner_headers ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) +file(GLOB_RECURSE runner_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +file(GLOB_RECURSE runner_headers ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) add_executable(runner ${runner_sources} diff --git a/src/runner/launcher/html/doc_host_ui_handler.cpp b/src/runner/launcher/html/doc_host_ui_handler.cpp new file mode 100644 index 0000000..0c1cc86 --- /dev/null +++ b/src/runner/launcher/html/doc_host_ui_handler.cpp @@ -0,0 +1,115 @@ +#include "../../std_include.hpp" +#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 = this->frame_->get_html_dispatch(); + return (*ppDispatch) ? S_OK : 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/runner/launcher/html/doc_host_ui_handler.hpp b/src/runner/launcher/html/doc_host_ui_handler.hpp new file mode 100644 index 0000000..0b538b4 --- /dev/null +++ b/src/runner/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/runner/launcher/html/html_argument.cpp b/src/runner/launcher/html/html_argument.cpp new file mode 100644 index 0000000..72798b8 --- /dev/null +++ b/src/runner/launcher/html/html_argument.cpp @@ -0,0 +1,51 @@ +#include "../../std_include.hpp" +#include "html_argument.hpp" + +html_argument::html_argument(VARIANT* val) : value_(val) +{ +} + +bool html_argument::is_empty() const +{ + return this->value_ == nullptr || this->value_->vt == VT_EMPTY; +} + +bool html_argument::is_string() const +{ + if (this->is_empty()) return false; + return this->value_->vt == VT_BSTR; +} + +bool html_argument::is_number() const +{ + if (this->is_empty()) return false; + return this->value_->vt == VT_I4; +} + +bool html_argument::is_bool() const +{ + if (this->is_empty()) return false; + return this->value_->vt == VT_BOOL; +} + +std::string html_argument::get_string() const +{ + if (!this->is_string()) return {}; + std::wstring wide_string(this->value_->bstrVal); +#pragma warning(push) +#pragma warning(disable: 4244) + return std::string(wide_string.begin(), wide_string.end()); +#pragma warning(pop) +} + +int html_argument::get_number() const +{ + if (!this->is_number()) return 0; + return this->value_->intVal; +} + +bool html_argument::get_bool() const +{ + if (!this->is_bool()) return false; + return this->value_->boolVal != FALSE; +} diff --git a/src/runner/launcher/html/html_argument.hpp b/src/runner/launcher/html/html_argument.hpp new file mode 100644 index 0000000..6e460e2 --- /dev/null +++ b/src/runner/launcher/html/html_argument.hpp @@ -0,0 +1,20 @@ +#pragma once + +class html_argument final +{ +public: + html_argument(VARIANT* val); + + bool is_empty() const; + + bool is_string() const; + bool is_number() const; + bool is_bool() const; + + std::string get_string() const; + int get_number() const; + bool get_bool() const; + +private: + VARIANT* value_; +}; diff --git a/src/runner/launcher/html/html_dispatch.cpp b/src/runner/launcher/html/html_dispatch.cpp new file mode 100644 index 0000000..bbc008c --- /dev/null +++ b/src/runner/launcher/html/html_dispatch.cpp @@ -0,0 +1,67 @@ +#include "../../std_include.hpp" +#include "html_frame.hpp" + +html_dispatch::html_dispatch(html_frame* frame) : frame_(frame) +{ +} + +HRESULT html_dispatch::QueryInterface(const IID& riid, LPVOID* ppvObj) +{ + if (!memcmp(&riid, &IID_IUnknown, sizeof(GUID)) || + !memcmp(&riid, &IID_IDispatch, sizeof(GUID))) + { + *ppvObj = this; + this->AddRef(); + return S_OK; + } + + *ppvObj = nullptr; + return E_NOINTERFACE; +} + +ULONG html_dispatch::AddRef() +{ + return 1; +} + +ULONG html_dispatch::Release() +{ + return 1; +} + +HRESULT html_dispatch::GetTypeInfoCount(UINT* /*pctinfo*/) +{ + return S_FALSE; +} + +HRESULT html_dispatch::GetTypeInfo(UINT /*iTInfo*/, LCID /*lcid*/, ITypeInfo** /*ppTInfo*/) +{ + return S_FALSE; +} + +HRESULT html_dispatch::GetIDsOfNames(const IID& /*riid*/, LPOLESTR* rgszNames, UINT cNames, LCID /*lcid*/, + DISPID* rgDispId) +{ + for (unsigned int i = 0; i < cNames; ++i) + { + std::wstring wide_name(rgszNames[i]); +#pragma warning(push) +#pragma warning(disable: 4244) + std::string name(wide_name.begin(), wide_name.end()); +#pragma warning(pop) + + + rgDispId[i] = this->frame_->get_callback_id(name); + } + + return S_OK; +} + +HRESULT html_dispatch::Invoke(DISPID dispIdMember, const IID& /*riid*/, LCID /*lcid*/, WORD /*wFlags*/, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, EXCEPINFO* /*pExcepInfo*/, UINT* /*puArgErr*/) +{ + html_frame::callback_params params(pDispParams, pVarResult); + this->frame_->invoke_callback(dispIdMember, ¶ms); + return S_OK; +} diff --git a/src/runner/launcher/html/html_dispatch.hpp b/src/runner/launcher/html/html_dispatch.hpp new file mode 100644 index 0000000..869af7a --- /dev/null +++ b/src/runner/launcher/html/html_dispatch.hpp @@ -0,0 +1,24 @@ +#pragma once + +class html_frame; + +class html_dispatch final : public IDispatch +{ +public: + html_dispatch(html_frame* frame); + virtual ~html_dispatch() = default; + +private: + html_frame* frame_; + +public: // IDispatch interface + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID FAR* ppvObj) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT* pctinfo) override; + HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) override; + HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) + override; + HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, + VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) override; +}; diff --git a/src/runner/launcher/html/html_frame.cpp b/src/runner/launcher/html/html_frame.cpp new file mode 100644 index 0000000..cd638fb --- /dev/null +++ b/src/runner/launcher/html/html_frame.cpp @@ -0,0 +1,298 @@ +#include "../../std_include.hpp" +#include "../../finally.hpp" + +#include "../../utils/nt.hpp" + +#include "html_frame.hpp" + + +std::atomic html_frame::frame_count_ = 0; + +html_frame::callback_params::callback_params(DISPPARAMS* params, VARIANT* res) : result(res) +{ + for (auto i = params->cArgs; i > 0; --i) + { + auto* param = ¶ms->rgvarg[i - 1]; + this->arguments.emplace_back(param); + } +} + +html_frame::html_frame() + : in_place_frame_(this) + , in_place_site_(this) + , ui_handler_(this) + , client_site_(this) + , html_dispatch_(this) +{ + if (frame_count_++ == 0 && OleInitialize(nullptr) != S_OK) + { + throw std::runtime_error("Unable to initialize the OLE library"); + } + + auto needs_restart = false; + needs_restart |= set_browser_feature("FEATURE_BROWSER_EMULATION", 11000); + needs_restart |= set_browser_feature("FEATURE_GPU_RENDERING", 1); + + if (needs_restart) + { + //utils::nt::relaunch_self(); + //utils::nt::terminate(0); + } +} + +html_frame::~html_frame() +{ + if (--frame_count_ <= 0) + { + frame_count_ = 0; + OleUninitialize(); + } +} + +void html_frame::object_deleter(IUnknown* object) +{ + if (object) + { + object->Release(); + } +} + +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_; +} + +html_dispatch* html_frame::get_html_dispatch() +{ + return &this->html_dispatch_; +} + +std::shared_ptr html_frame::get_web_browser() const +{ + if (!this->browser_object_) return {}; + + IWebBrowser2* web_browser = nullptr; + if (FAILED(this->browser_object_->QueryInterface(IID_IWebBrowser2, reinterpret_cast(&web_browser))) + || !web_browser) + return {}; + + return std::shared_ptr(web_browser, object_deleter); +} + +std::shared_ptr html_frame::get_dispatch() const +{ + const auto web_browser = this->get_web_browser(); + if (!web_browser) return {}; + + IDispatch* dispatch = nullptr; + if (FAILED(web_browser->get_Document(&dispatch)) || !dispatch) return {}; + + return std::shared_ptr(dispatch, object_deleter); +} + +std::shared_ptr html_frame::get_document() const +{ + const auto dispatch = this->get_dispatch(); + if (!dispatch) return {}; + + IHTMLDocument2* document = nullptr; + if (FAILED(dispatch->QueryInterface(IID_IHTMLDocument2, reinterpret_cast(&document))) + || !document) + return {}; + + return std::shared_ptr(document, object_deleter); +} + +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 (FAILED( + 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); + object_deleter(browser_object); + } + }); +} + +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); +} + +bool html_frame::set_browser_feature(const std::string& feature, DWORD value) +{ + const auto registry_path = R"(SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\)" + feature; + + HKEY key = nullptr; + if (RegCreateKeyA(HKEY_CURRENT_USER, registry_path.data(), &key) == ERROR_SUCCESS) + { + RegCloseKey(key); + } + + key = nullptr; + if (RegOpenKeyExA( + HKEY_CURRENT_USER, registry_path.data(), 0, + KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) + { + return false; // Error :( + } + + const utils::nt::library self; + const auto name = self.get_name(); + + DWORD type{}; + auto is_new = true; + if (RegQueryValueExA(key, name.data(), nullptr, &type, nullptr, nullptr) == ERROR_SUCCESS) + { + is_new = false; + } + + RegSetValueExA(key, name.data(), 0, REG_DWORD, reinterpret_cast(&value), sizeof(value)); + RegCloseKey(key); + + return is_new; +} + +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); + } +} + +bool html_frame::load_url(const std::string& url) const +{ + auto web_browser = this->get_web_browser(); + if (!web_browser) return false; + + 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()); + + const auto _ = utils::finally([&my_url]() { VariantClear(&my_url); }); + if (!my_url.bstrVal) return false; + + return SUCCEEDED(web_browser->Navigate2(&my_url, nullptr, nullptr, nullptr, nullptr)); +} + +bool html_frame::load_html(const std::string& html) const +{ + if (!this->load_url("about:blank")) return false; + + const auto document = this->get_document(); + if (!document) return false; + + SAFEARRAYBOUND safe_array_bound = {1, 0}; + auto safe_array = SafeArrayCreate(VT_VARIANT, 1, &safe_array_bound); + if (!safe_array) return false; + + const auto _ = utils::finally([safe_array]() { SafeArrayDestroy(safe_array); }); + + VARIANT* variant = nullptr; + if (FAILED(SafeArrayAccessData(safe_array, reinterpret_cast(&variant))) || !variant) return false; + + std::wstring wide_html(html.begin(), html.end()); + + variant->vt = VT_BSTR; + variant->bstrVal = SysAllocString(wide_html.data()); + if (!variant->bstrVal) return false; + + document->write(safe_array); + document->close(); + + return true; +} + +int html_frame::get_callback_id(const std::string& name) +{ + for (auto i = 0u; i < this->callbacks_.size(); ++i) + { + if (this->callbacks_[i].first == name) + { + return i; + } + } + + return -1; +} + +void html_frame::invoke_callback(const int id, callback_params* params) +{ + if (id >= 0 && static_cast(id) < this->callbacks_.size()) + { + this->callbacks_[id].second(params); + } +} + +void html_frame::register_callback(const std::string& name, const std::function& callback) +{ + this->callbacks_.emplace_back(name, callback); +} diff --git a/src/runner/launcher/html/html_frame.hpp b/src/runner/launcher/html/html_frame.hpp new file mode 100644 index 0000000..04d8108 --- /dev/null +++ b/src/runner/launcher/html/html_frame.hpp @@ -0,0 +1,67 @@ +#pragma once +#include "ole_in_place_frame.hpp" +#include "ole_in_place_site.hpp" +#include "doc_host_ui_handler.hpp" +#include "ole_client_site.hpp" +#include "html_dispatch.hpp" +#include "html_argument.hpp" + +class html_frame +{ +public: + class callback_params final + { + public: + callback_params(DISPPARAMS* params, VARIANT* res); + + std::vector arguments; + html_argument result; + }; + + html_frame(); + virtual ~html_frame(); + + void initialize(HWND window); + + void resize(DWORD width, DWORD height) const; + bool load_url(const std::string& url) const; + bool load_html(const std::string& html) const; + + HWND get_window() const; + + std::shared_ptr get_browser_object() const; + std::shared_ptr get_web_browser() const; + std::shared_ptr get_dispatch() const; + std::shared_ptr get_document() 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(); + html_dispatch* get_html_dispatch(); + + int get_callback_id(const std::string& name); + void invoke_callback(int id, callback_params* params); + + void register_callback(const std::string& name, const std::function& callback); + +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_; + html_dispatch html_dispatch_; + + std::vector>> callbacks_; + + void create_browser(); + void initialize_browser(); + + static bool set_browser_feature(const std::string& feature, DWORD value); + static void object_deleter(IUnknown* object); + + static std::atomic frame_count_; +}; diff --git a/src/runner/launcher/html/html_window.cpp b/src/runner/launcher/html/html_window.cpp new file mode 100644 index 0000000..ed6d4c2 --- /dev/null +++ b/src/runner/launcher/html/html_window.cpp @@ -0,0 +1,29 @@ +#include "../../std_include.hpp" +#include "html_window.hpp" + +window* html_window::get_window() +{ + return this; +} + +html_frame* html_window::get_html_frame() +{ + return this; +} + +LRESULT html_window::processor(const UINT message, const WPARAM w_param, const LPARAM l_param) +{ + if (message == WM_SIZE) + { + this->resize(LOWORD(l_param), HIWORD(l_param)); + return 0; + } + + if (message == WM_CREATE) + { + this->initialize(*this); + return 0; + } + + return window::processor(message, w_param, l_param); +} diff --git a/src/runner/launcher/html/html_window.hpp b/src/runner/launcher/html/html_window.hpp new file mode 100644 index 0000000..5b9f25b --- /dev/null +++ b/src/runner/launcher/html/html_window.hpp @@ -0,0 +1,15 @@ +#pragma once +#include "../window.hpp" +#include "html_frame.hpp" + +class html_window final : public window, public html_frame +{ +public: + ~html_window() = default; + + window* get_window(); + html_frame* get_html_frame(); + +private: + LRESULT processor(UINT message, WPARAM w_param, LPARAM l_param) override; +}; diff --git a/src/runner/launcher/html/ole_client_site.cpp b/src/runner/launcher/html/ole_client_site.cpp new file mode 100644 index 0000000..1a624de --- /dev/null +++ b/src/runner/launcher/html/ole_client_site.cpp @@ -0,0 +1,77 @@ +#include "../../std_include.hpp" +#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/runner/launcher/html/ole_client_site.hpp b/src/runner/launcher/html/ole_client_site.hpp new file mode 100644 index 0000000..d0adc80 --- /dev/null +++ b/src/runner/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/runner/launcher/html/ole_in_place_frame.cpp b/src/runner/launcher/html/ole_in_place_frame.cpp new file mode 100644 index 0000000..aa71a01 --- /dev/null +++ b/src/runner/launcher/html/ole_in_place_frame.cpp @@ -0,0 +1,82 @@ +#include "../../std_include.hpp" +#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/runner/launcher/html/ole_in_place_frame.hpp b/src/runner/launcher/html/ole_in_place_frame.hpp new file mode 100644 index 0000000..4a39d7f --- /dev/null +++ b/src/runner/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/runner/launcher/html/ole_in_place_site.cpp b/src/runner/launcher/html/ole_in_place_site.cpp new file mode 100644 index 0000000..8fc7ead --- /dev/null +++ b/src/runner/launcher/html/ole_in_place_site.cpp @@ -0,0 +1,105 @@ +#include "../../std_include.hpp" +#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/runner/launcher/html/ole_in_place_site.hpp b/src/runner/launcher/html/ole_in_place_site.hpp new file mode 100644 index 0000000..3dad18c --- /dev/null +++ b/src/runner/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/runner/launcher/launcher.cpp b/src/runner/launcher/launcher.cpp new file mode 100644 index 0000000..f00d830 --- /dev/null +++ b/src/runner/launcher/launcher.cpp @@ -0,0 +1,33 @@ +#include "../std_include.hpp" +#include "../resource.hpp" +#include "launcher.hpp" + +#include "../utils/nt.hpp" + +launcher::launcher() +{ + this->create_main_menu(); +} + +void launcher::create_main_menu() +{ + this->main_window_.set_callback( + [](window* window, const UINT message, const WPARAM w_param, const LPARAM l_param) -> LRESULT + { + if (message == WM_CLOSE) + { + window::close_all(); + } + + return DefWindowProcA(*window, message, w_param, l_param); + }); + + this->main_window_.create("S1x", 750, 420); + this->main_window_.load_html(utils::nt::load_resource(MAIN_MENU)); + this->main_window_.show(); +} + +void launcher::run() const +{ + window::run(); +} diff --git a/src/runner/launcher/launcher.hpp b/src/runner/launcher/launcher.hpp new file mode 100644 index 0000000..f1753c8 --- /dev/null +++ b/src/runner/launcher/launcher.hpp @@ -0,0 +1,15 @@ +#pragma once +#include "html/html_window.hpp" + +class launcher final +{ +public: + launcher(); + + void run() const; + +private: + html_window main_window_; + + void create_main_menu(); +}; diff --git a/src/runner/launcher/window.cpp b/src/runner/launcher/window.cpp new file mode 100644 index 0000000..76b1082 --- /dev/null +++ b/src/runner/launcher/window.cpp @@ -0,0 +1,208 @@ +#include "../std_include.hpp" +#include "window.hpp" + +#include "../utils/nt.hpp" + +std::mutex window::mutex_; +std::vector window::windows_; + +window::window() +{ + ZeroMemory(&this->wc_, sizeof(this->wc_)); + + 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 = GetModuleHandle(nullptr); + this->wc_.hCursor = LoadCursor(nullptr, IDC_ARROW); + this->wc_.hIcon = LoadIcon(this->wc_.hInstance, MAKEINTRESOURCE(102)); + this->wc_.hIconSm = this->wc_.hIcon; + 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 long flags) +{ + { + std::lock_guard _(mutex_); + windows_.push_back(this); + } + + 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(), flags, x, y, width, height, nullptr, + nullptr, this->wc_.hInstance, this); + + SendMessageA(this->handle_, WM_DPICHANGED, 0, 0); +} + +window::~window() +{ + this->close(); + UnregisterClass(this->wc_.lpszClassName, this->wc_.hInstance); +} + +void window::close() +{ + if (!this->handle_) return; + + SendMessageA(this->handle_, WM_KILL_WINDOW, NULL, NULL); + this->handle_ = nullptr; +} + +void window::run() +{ + MSG msg; + while (GetMessage(&msg, nullptr, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +void window::close_all() +{ + std::unique_lock lock(mutex_); + auto window_list = windows_; + lock.unlock(); + + const auto current_thread_id = GetCurrentThreadId(); + for (auto& window : window_list) + { + const auto thread_id = GetWindowThreadProcessId(*window, nullptr); + + if (thread_id == current_thread_id) + { + window->close(); + } + } +} + +void window::remove_window(const window* window) +{ + std::lock_guard _(mutex_); + + for (auto i = windows_.begin(); i != windows_.end(); ++i) + { + if (*i == window) + { + windows_.erase(i); + break; + } + } +} + +int window::get_window_count() +{ + std::lock_guard _(mutex_); + + auto count = 0; + const auto current_thread_id = GetCurrentThreadId(); + + for (const auto& window : windows_) + { + const auto thread_id = GetWindowThreadProcessId(*window, nullptr); + + if (thread_id == current_thread_id) + { + ++count; + } + } + + return count; +} + +void window::show() const +{ + ShowWindow(this->handle_, SW_SHOW); + UpdateWindow(this->handle_); +} + +void window::hide() const +{ + ShowWindow(this->handle_, SW_HIDE); + UpdateWindow(this->handle_); +} + +void window::set_callback(const std::function& callback) +{ + this->callback_ = callback; +} + +LRESULT window::processor(const UINT message, const WPARAM w_param, const LPARAM l_param) +{ + if (message == WM_DPICHANGED) + { + const utils::nt::library user32{"user32.dll"}; + const auto get_dpi = user32 ? user32.get_proc("GetDpiForWindow") : nullptr; + + if (get_dpi) + { + const auto dpi = get_dpi(*this); + if (dpi != this->last_dpi_) + { + RECT rect; + GetWindowRect(*this, &rect); + + const auto scale = dpi * 1.0 / this->last_dpi_; + this->last_dpi_ = dpi; + + const auto width = rect.right - rect.left; + const auto height = rect.bottom - rect.top; + + MoveWindow(*this, rect.left, rect.top, int(width * scale), int(height * scale), TRUE); + } + } + } + + if (message == WM_DESTROY) + { + remove_window(this); + + if (get_window_count() == 0) + { + PostQuitMessage(0); + } + + return TRUE; + } + + if (message == WM_KILL_WINDOW) + { + DestroyWindow(*this); + return 0; + } + + if (this->callback_) + { + return this->callback_(this, message, w_param, l_param); + } + + return DefWindowProc(*this, message, w_param, l_param); +} + +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)); + + static_cast(data->lpCreateParams)->handle_ = hwnd; + } + + const auto self = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); + if (self) return self->processor(message, w_param, l_param); + + return DefWindowProc(hwnd, message, w_param, l_param); +} + + +window::operator HWND() const +{ + return this->handle_; +} diff --git a/src/runner/launcher/window.hpp b/src/runner/launcher/window.hpp new file mode 100644 index 0000000..4dea45d --- /dev/null +++ b/src/runner/launcher/window.hpp @@ -0,0 +1,44 @@ +#pragma once + +#define WM_KILL_WINDOW (WM_USER+0) + +class window +{ +public: + window(); + virtual ~window(); + + void create(const std::string& title, int width, int height, + long flags = (WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX))); + + void close(); + + void show() const; + void hide() const; + + void set_callback(const std::function& callback); + + operator HWND() const; + + static void run(); + static void close_all(); + +protected: + virtual LRESULT processor(UINT message, WPARAM w_param, LPARAM l_param); + +private: + uint32_t last_dpi_ = 96; + + WNDCLASSEX wc_{}; + HWND handle_ = nullptr; + std::string classname_; + std::function callback_; + + static LRESULT CALLBACK static_processor(HWND hwnd, UINT message, WPARAM w_param, LPARAM l_param); + + static std::mutex mutex_; + static std::vector windows_; + + static void remove_window(const window* window); + static int get_window_count(); +}; diff --git a/src/runner/main.cpp b/src/runner/main.cpp index 1980cf8..0b791c7 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -11,6 +11,8 @@ #include "resource.hpp" +#include "launcher/launcher.hpp" + #pragma comment(lib, "Shlwapi.lib") void patch_data(const driver_device& driver_device, const uint32_t pid, const uint64_t addr, const uint8_t* buffer, @@ -76,6 +78,8 @@ std::filesystem::path extract_driver() void unsafe_main(const int /*argc*/, char* /*argv*/[]) { + //launcher().run(); + const auto driver_file = extract_driver(); driver driver{driver_file, "MomoLul"}; diff --git a/src/runner/resource.hpp b/src/runner/resource.hpp index b08cce0..dde0c73 100644 --- a/src/runner/resource.hpp +++ b/src/runner/resource.hpp @@ -1,3 +1,4 @@ #pragma once #define DRIVER_BINARY 300 +#define MAIN_MENU 301 diff --git a/src/runner/resource.rc b/src/runner/resource.rc index 7145013..98eab2c 100644 --- a/src/runner/resource.rc +++ b/src/runner/resource.rc @@ -85,6 +85,7 @@ END 102 ICON "resources/icon.ico" DRIVER_BINARY RCDATA DRIVER_FILE +MAIN_MENU RCDATA "resources/main.html" diff --git a/src/runner/resources/main.html b/src/runner/resources/main.html new file mode 100644 index 0000000..54ef7dc --- /dev/null +++ b/src/runner/resources/main.html @@ -0,0 +1,574 @@ + + + + + + + S1x + + + + + + +
+
+ + + + +
+
+ + + + + diff --git a/src/runner/std_include.hpp b/src/runner/std_include.hpp index 8a2f48d..b7af216 100644 --- a/src/runner/std_include.hpp +++ b/src/runner/std_include.hpp @@ -2,9 +2,15 @@ #include #include +#include #include +#include #include #include +#include + +#include +#include #pragma comment(lib, "Shlwapi.lib") diff --git a/src/runner/utils/nt.cpp b/src/runner/utils/nt.cpp new file mode 100644 index 0000000..5fc5273 --- /dev/null +++ b/src/runner/utils/nt.cpp @@ -0,0 +1,254 @@ +#include "nt.hpp" + +namespace utils::nt +{ + library library::load(const std::string& name) + { + return library(LoadLibraryA(name.data())); + } + + library library::load(const std::filesystem::path& path) + { + return library::load(path.generic_string()); + } + + library library::get_by_address(void* address) + { + HMODULE handle = nullptr; + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, static_cast(address), &handle); + return library(handle); + } + + library::library() + { + this->module_ = GetModuleHandleA(nullptr); + } + + library::library(const std::string& name) + { + this->module_ = GetModuleHandleA(name.data()); + } + + library::library(const HMODULE handle) + { + this->module_ = handle; + } + + bool library::operator==(const library& obj) const + { + return this->module_ == obj.module_; + } + + library::operator bool() const + { + return this->is_valid(); + } + + library::operator HMODULE() const + { + return this->get_handle(); + } + + PIMAGE_NT_HEADERS library::get_nt_headers() const + { + if (!this->is_valid()) return nullptr; + return reinterpret_cast(this->get_ptr() + this->get_dos_header()->e_lfanew); + } + + PIMAGE_DOS_HEADER library::get_dos_header() const + { + return reinterpret_cast(this->get_ptr()); + } + + PIMAGE_OPTIONAL_HEADER library::get_optional_header() const + { + if (!this->is_valid()) return nullptr; + return &this->get_nt_headers()->OptionalHeader; + } + + std::vector library::get_section_headers() const + { + std::vector headers; + + auto nt_headers = this->get_nt_headers(); + auto section = IMAGE_FIRST_SECTION(nt_headers); + + for (uint16_t i = 0; i < nt_headers->FileHeader.NumberOfSections; ++i, ++section) + { + if (section) headers.push_back(section); + else OutputDebugStringA("There was an invalid section :O"); + } + + return headers; + } + + std::uint8_t* library::get_ptr() const + { + return reinterpret_cast(this->module_); + } + + void library::unprotect() const + { + if (!this->is_valid()) return; + + DWORD protection; + VirtualProtect(this->get_ptr(), this->get_optional_header()->SizeOfImage, PAGE_EXECUTE_READWRITE, + &protection); + } + + size_t library::get_relative_entry_point() const + { + if (!this->is_valid()) return 0; + return this->get_nt_headers()->OptionalHeader.AddressOfEntryPoint; + } + + void* library::get_entry_point() const + { + if (!this->is_valid()) return nullptr; + return this->get_ptr() + this->get_relative_entry_point(); + } + + bool library::is_valid() const + { + return this->module_ != nullptr && this->get_dos_header()->e_magic == IMAGE_DOS_SIGNATURE; + } + + std::string library::get_name() const + { + if (!this->is_valid()) return ""; + + auto path = this->get_path(); + const auto pos = path.find_last_of("/\\"); + if (pos == std::string::npos) return path; + + return path.substr(pos + 1); + } + + std::string library::get_path() const + { + if (!this->is_valid()) return ""; + + char name[MAX_PATH] = {0}; + GetModuleFileNameA(this->module_, name, sizeof name); + + return name; + } + + std::string library::get_folder() const + { + if (!this->is_valid()) return ""; + + const auto path = std::filesystem::path(this->get_path()); + return path.parent_path().generic_string(); + } + + void library::free() + { + if (this->is_valid()) + { + FreeLibrary(this->module_); + this->module_ = nullptr; + } + } + + HMODULE library::get_handle() const + { + return this->module_; + } + + void** library::get_iat_entry(const std::string& module_name, const std::string& proc_name) const + { + if (!this->is_valid()) return nullptr; + + const library other_module(module_name); + if (!other_module.is_valid()) return nullptr; + + auto* const target_function = other_module.get_proc(proc_name); + if (!target_function) return nullptr; + + auto* header = this->get_optional_header(); + if (!header) return nullptr; + + auto* import_descriptor = reinterpret_cast(this->get_ptr() + header->DataDirectory + [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + + while (import_descriptor->Name) + { + if (!_stricmp(reinterpret_cast(this->get_ptr() + import_descriptor->Name), module_name.data())) + { + auto* original_thunk_data = reinterpret_cast(import_descriptor-> + OriginalFirstThunk + this->get_ptr()); + auto* thunk_data = reinterpret_cast(import_descriptor->FirstThunk + this-> + get_ptr()); + + while (original_thunk_data->u1.AddressOfData) + { + const size_t ordinal_number = original_thunk_data->u1.AddressOfData & 0xFFFFFFF; + + if (ordinal_number > 0xFFFF) continue; + + if (GetProcAddress(other_module.module_, reinterpret_cast(ordinal_number)) == + target_function) + { + return reinterpret_cast(&thunk_data->u1.Function); + } + + ++original_thunk_data; + ++thunk_data; + } + + //break; + } + + ++import_descriptor; + } + + return nullptr; + } + + void raise_hard_exception() + { + int data = false; + const library ntdll("ntdll.dll"); + ntdll.invoke_pascal("RtlAdjustPrivilege", 19, true, false, &data); + ntdll.invoke_pascal("NtRaiseHardError", 0xC000007B, 0, nullptr, nullptr, 6, &data); + } + + std::string load_resource(const int id) + { + auto* const res = FindResource(library(), MAKEINTRESOURCE(id), RT_RCDATA); + if (!res) return {}; + + auto* const handle = LoadResource(nullptr, res); + if (!handle) return {}; + + return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, res)); + } + + void relaunch_self() + { + const utils::nt::library self; + + STARTUPINFOA startup_info; + PROCESS_INFORMATION process_info; + + ZeroMemory(&startup_info, sizeof(startup_info)); + ZeroMemory(&process_info, sizeof(process_info)); + startup_info.cb = sizeof(startup_info); + + char current_dir[MAX_PATH]; + GetCurrentDirectoryA(sizeof(current_dir), current_dir); + auto* const command_line = GetCommandLineA(); + + CreateProcessA(self.get_path().data(), command_line, nullptr, nullptr, false, NULL, nullptr, current_dir, + &startup_info, &process_info); + + if (process_info.hThread && process_info.hThread != INVALID_HANDLE_VALUE) CloseHandle(process_info.hThread); + if (process_info.hProcess && process_info.hProcess != INVALID_HANDLE_VALUE) CloseHandle(process_info.hProcess); + } + + void terminate(const uint32_t code) + { + TerminateProcess(GetCurrentProcess(), code); + } +} diff --git a/src/runner/utils/nt.hpp b/src/runner/utils/nt.hpp new file mode 100644 index 0000000..dc12cbe --- /dev/null +++ b/src/runner/utils/nt.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include "../std_include.hpp" + +// min and max is required by gdi, therefore NOMINMAX won't work +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +namespace utils::nt +{ + class library final + { + public: + static library load(const std::string& name); + static library load(const std::filesystem::path& path); + static library get_by_address(void* address); + + library(); + explicit library(const std::string& name); + explicit library(HMODULE handle); + + library(const library& a) : module_(a.module_) + { + } + + bool operator!=(const library& obj) const { return !(*this == obj); }; + bool operator==(const library& obj) const; + + operator bool() const; + operator HMODULE() const; + + void unprotect() const; + void* get_entry_point() const; + size_t get_relative_entry_point() const; + + bool is_valid() const; + std::string get_name() const; + std::string get_path() const; + std::string get_folder() const; + std::uint8_t* get_ptr() const; + void free(); + + HMODULE get_handle() const; + + template + T get_proc(const std::string& process) const + { + if (!this->is_valid()) T{}; + return reinterpret_cast(GetProcAddress(this->module_, process.data())); + } + + template + std::function get(const std::string& process) const + { + if (!this->is_valid()) return std::function(); + return static_cast(this->get_proc(process)); + } + + template + T invoke(const std::string& process, Args ... args) const + { + auto method = this->get(process); + if (method) return method(args...); + return T(); + } + + template + T invoke_pascal(const std::string& process, Args ... args) const + { + auto method = this->get(process); + if (method) return method(args...); + return T(); + } + + template + T invoke_this(const std::string& process, void* this_ptr, Args ... args) const + { + auto method = this->get(this_ptr, process); + if (method) return method(args...); + return T(); + } + + std::vector get_section_headers() const; + + PIMAGE_NT_HEADERS get_nt_headers() const; + PIMAGE_DOS_HEADER get_dos_header() const; + PIMAGE_OPTIONAL_HEADER get_optional_header() const; + + void** get_iat_entry(const std::string& module_name, const std::string& proc_name) const; + + private: + HMODULE module_; + }; + + __declspec(noreturn) void raise_hard_exception(); + std::string load_resource(int id); + + void relaunch_self(); + __declspec(noreturn) void terminate(uint32_t code = 0); +}