diff --git a/real-app/real-app.vcxproj b/real-app/real-app.vcxproj
index e114276..cc3665d 100644
--- a/real-app/real-app.vcxproj
+++ b/real-app/real-app.vcxproj
@@ -18,9 +18,15 @@
+
+
+
+
+
+
@@ -30,9 +36,15 @@
+
+
+
+
+
+
@@ -101,7 +113,7 @@
MultiThreadedDLL
- NotSet
+ Windows
libcurl_a.lib;Ws2_32.lib;crypt32.lib;Wldap32.lib;Normaliz.lib;%(AdditionalDependencies)
@@ -123,7 +135,7 @@
true
true
- NotSet
+ Windows
libcurl_a.lib;Ws2_32.lib;crypt32.lib;Wldap32.lib;Normaliz.lib;%(AdditionalDependencies)
diff --git a/real-app/src/OStreamSink.cpp b/real-app/src/OStreamSink.cpp
new file mode 100644
index 0000000..849e873
--- /dev/null
+++ b/real-app/src/OStreamSink.cpp
@@ -0,0 +1,23 @@
+#include "OStreamSink.h"
+
+using namespace miniant::Spdlog;
+
+OStreamSink::OStreamSink(std::shared_ptr outputStream, bool forceFlush):
+ m_outputStream(std::move(outputStream)),
+ m_forceFlush(forceFlush) {}
+
+std::mutex& OStreamSink::GetMutex() {
+ return mutex_;
+}
+
+void OStreamSink::sink_it_(const spdlog::details::log_msg& message) {
+ fmt::memory_buffer formattedMessage;
+ sink::formatter_->format(message, formattedMessage);
+ m_outputStream->write(formattedMessage.data(), static_cast(formattedMessage.size()));
+ if (m_forceFlush)
+ m_outputStream->flush();
+}
+
+void OStreamSink::flush_() {
+ m_outputStream->flush();
+}
diff --git a/real-app/src/OStreamSink.h b/real-app/src/OStreamSink.h
new file mode 100644
index 0000000..da05edb
--- /dev/null
+++ b/real-app/src/OStreamSink.h
@@ -0,0 +1,27 @@
+#pragma once
+
+// Work-around for compilation error caused by spdlog using Windows headers
+#include
+
+#include
+
+#include
+
+namespace miniant::Spdlog {
+
+class OStreamSink : public spdlog::sinks::base_sink {
+public:
+ explicit OStreamSink(std::shared_ptr outputStream, bool forceFlush=false);
+
+ std::mutex& GetMutex();
+
+protected:
+ void sink_it_(const spdlog::details::log_msg& message) override;
+ void flush_() override;
+
+private:
+ std::shared_ptr m_outputStream;
+ bool m_forceFlush;
+};
+
+}
diff --git a/real-app/src/Windows/Console.cpp b/real-app/src/Windows/Console.cpp
new file mode 100644
index 0000000..6ecf7b3
--- /dev/null
+++ b/real-app/src/Windows/Console.cpp
@@ -0,0 +1,26 @@
+#include "Console.h"
+
+#include
+
+#include
+
+using namespace miniant::Windows;
+
+Console::Console(std::function onShow):
+ m_onShow(std::move(onShow)) {}
+
+void Console::Open() {
+ ::AllocConsole();
+
+ FILE* dummy;
+ freopen_s(&dummy, "conout$", "w", stdout);
+ std::cout.clear();
+
+ if (m_onShow)
+ m_onShow();
+}
+
+void Console::Close() {
+ ::SendMessage(::GetConsoleWindow(), WM_CLOSE, 0, 0);
+ ::FreeConsole();
+}
diff --git a/real-app/src/Windows/Console.h b/real-app/src/Windows/Console.h
new file mode 100644
index 0000000..8ac9296
--- /dev/null
+++ b/real-app/src/Windows/Console.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include
+
+namespace miniant::Windows {
+
+class Console {
+public:
+ Console(std::function onShow);
+
+ void Open();
+ void Close();
+
+private:
+ std::function m_onShow;
+};
+
+}
diff --git a/real-app/src/Windows/Exception.cpp b/real-app/src/Windows/Exception.cpp
new file mode 100644
index 0000000..e58dfbf
--- /dev/null
+++ b/real-app/src/Windows/Exception.cpp
@@ -0,0 +1,14 @@
+#include "Exception.h"
+
+#include
+
+#include
+
+using namespace miniant::Windows;
+
+std::string GetErrorMessage() noexcept {
+ DWORD lastError = ::GetLastError();
+ return "Last error: " + std::to_string(lastError);
+}
+
+Exception::Exception() noexcept: std::runtime_error(GetErrorMessage()) {}
diff --git a/real-app/src/Windows/Exception.h b/real-app/src/Windows/Exception.h
new file mode 100644
index 0000000..82d5d8e
--- /dev/null
+++ b/real-app/src/Windows/Exception.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include
+
+namespace miniant::Windows {
+
+class Exception : std::runtime_error {
+public:
+ Exception() noexcept;
+};
+
+}
diff --git a/real-app/src/Windows/GlobalWindowProcedure.cpp b/real-app/src/Windows/GlobalWindowProcedure.cpp
new file mode 100644
index 0000000..b32fa2a
--- /dev/null
+++ b/real-app/src/Windows/GlobalWindowProcedure.cpp
@@ -0,0 +1,46 @@
+#include "GlobalWindowProcedure.h"
+
+#include "Exception.h"
+
+using namespace miniant::Windows;
+
+std::unordered_map GlobalWindowProcedure::s_windowProcedureMap;
+
+UINT GlobalWindowProcedure::GetFreeEventId() noexcept {
+ static UINT nextFreeEventId = WM_USER;
+ return nextFreeEventId++;
+}
+
+WNDCLASS GlobalWindowProcedure::RegisterWindowClass(LPCTSTR lpszClassName) {
+ WNDCLASS wc = {};
+ wc.lpszClassName = lpszClassName;
+ wc.lpfnWndProc = &WndProc;
+ if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(&WndProc), &wc.hInstance))
+ throw Exception();
+
+ if (::RegisterClass(&wc) == 0)
+ throw Exception();
+
+ return wc;
+}
+
+void GlobalWindowProcedure::SetWindowProcedure(HWND hWnd, WindowProcedure procedure) {
+ s_windowProcedureMap[hWnd] = procedure;
+}
+
+LRESULT CALLBACK GlobalWindowProcedure::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ WindowProcedure* windowProcedure;
+ try {
+ windowProcedure = &s_windowProcedureMap.at(hWnd);
+ } catch (std::out_of_range&) {
+ return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
+ }
+
+ if (*windowProcedure) {
+ std::optional lResult = (*windowProcedure)(hWnd, uMsg, wParam, lParam);
+ if (lResult)
+ return lResult.value();
+ }
+
+ return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
diff --git a/real-app/src/Windows/GlobalWindowProcedure.h b/real-app/src/Windows/GlobalWindowProcedure.h
new file mode 100644
index 0000000..3c07ad3
--- /dev/null
+++ b/real-app/src/Windows/GlobalWindowProcedure.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include
+
+#include
+#include