From c6d53cd13cbdc3dc9900c01320e343fdbc9a599c Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Wed, 24 Aug 2016 17:45:10 +0200 Subject: [PATCH] eap::monitor_ui class to prevent multiple launches introduced --- lib/EAPBase_UI/include/EAP_UI.h | 60 ++++++++++ lib/EAPBase_UI/src/EAP_UI.cpp | 193 ++++++++++++++++++++++++++++++++ lib/WinStd | 2 +- 3 files changed, 254 insertions(+), 1 deletion(-) diff --git a/lib/EAPBase_UI/include/EAP_UI.h b/lib/EAPBase_UI/include/EAP_UI.h index d20ea9c..f362f29 100644 --- a/lib/EAPBase_UI/include/EAP_UI.h +++ b/lib/EAPBase_UI/include/EAP_UI.h @@ -100,6 +100,14 @@ inline bool wxSetIconFromResource(wxStaticBitmap *bmp, wxIcon &icon, HINSTANCE h /// inline wxString wxEAPGetProviderName(const std::wstring &id); +namespace eap +{ + /// + /// Base class to prevent multiple instances of the same dialog + /// + class monitor_ui; +} + #pragma once #include // Fixes `CreateDialog` name collision @@ -808,3 +816,55 @@ inline wxString wxEAPGetProviderName(const std::wstring &id) return !id.empty() ? id : _(""); } + + +namespace eap +{ + class monitor_ui + { + public: + monitor_ui(_In_ HINSTANCE module, _In_ const GUID &guid); + virtual ~monitor_ui(); + + void set_popup(_In_ HWND hwnd); + void release_slaves(_In_bytecount_(size) const void *data, _In_ size_t size) const; + + inline bool is_master() const + { + return m_is_master; + } + + inline bool is_slave() const + { + return !is_master(); + } + + inline const std::vector& master_data() const + { + return m_data; + } + + protected: + virtual LRESULT winproc( + _In_ UINT msg, + _In_ WPARAM wparam, + _In_ LPARAM lparam); + + static LRESULT CALLBACK winproc( + _In_ HWND hwnd, + _In_ UINT msg, + _In_ WPARAM wparam, + _In_ LPARAM lparam); + + protected: + bool m_is_master; ///< Is this monitor master? + HWND m_hwnd; ///< Message window handle + std::list m_slaves; ///< List of slaves to notify on finish + HWND m_hwnd_popup; ///< Pop-up window handle + std::vector m_data; ///< Data master sent + + // Custom window messages + static const UINT s_msg_attach; ///< Slave sends this message to attach to master + static const UINT s_msg_finish; ///< Master sends this message to slaves to notify them it has finished (wparam has size, lparam has data) + }; +} diff --git a/lib/EAPBase_UI/src/EAP_UI.cpp b/lib/EAPBase_UI/src/EAP_UI.cpp index 53943e9..f7f54a6 100644 --- a/lib/EAPBase_UI/src/EAP_UI.cpp +++ b/lib/EAPBase_UI/src/EAP_UI.cpp @@ -345,3 +345,196 @@ wxEAPConfigProvider::wxEAPConfigProvider(eap::config_provider &prov, wxWindow *p m_identity->m_provider_name->SetFocusFromKbd(); } + + +using namespace std; +using namespace winstd; + +////////////////////////////////////////////////////////////////////// +// eap::monitor_ui +////////////////////////////////////////////////////////////////////// + +eap::monitor_ui::monitor_ui(_In_ HINSTANCE module, _In_ const GUID &guid) : + m_hwnd_popup(NULL) +{ + // Verify if the monitor is already running. + const WNDCLASSEX wnd_class_desc = { + sizeof(WNDCLASSEX), // cbSize + 0, // style + winproc, // lpfnWndProc + 0, // cbClsExtra + 0, // cbWndExtra + module, // hInstance + NULL, // hIcon + NULL, // hCursor + NULL, // hbrBackground + NULL, // lpszMenuName + _T(__FUNCTION__), // lpszClassName + NULL // hIconSm + }; + ATOM wnd_class = RegisterClassEx(&wnd_class_desc); + if (!wnd_class) + throw win_runtime_error(__FUNCTION__ " Error registering master monitor window class."); + tstring_guid guid_str(guid); + HWND hwnd_master = FindWindowEx(HWND_MESSAGE, NULL, (LPCTSTR)wnd_class, guid_str.c_str()); + if (hwnd_master) { + // Another monitor is already running. + m_is_master = false; + + // Register slave windows class slightly different, not to include slaves in FindWindowEx(). + const WNDCLASSEX wnd_class_desc = { + sizeof(WNDCLASSEX), // cbSize + 0, // style + winproc, // lpfnWndProc + 0, // cbClsExtra + 0, // cbWndExtra + module, // hInstance + NULL, // hIcon + NULL, // hCursor + NULL, // hbrBackground + NULL, // lpszMenuName + _T(__FUNCTION__) _T("-Slave"), // lpszClassName + NULL // hIconSm + }; + wnd_class = RegisterClassEx(&wnd_class_desc); + if (!wnd_class) + throw win_runtime_error(__FUNCTION__ " Error registering slave monitor window class."); + } else { + // This is a fresh monitor. + m_is_master = true; + } + + m_hwnd = CreateWindowEx( + 0, // dwExStyle + (LPCTSTR)wnd_class, // lpClassName + guid_str.c_str(), // lpWindowName + 0, // dwStyle + 0, // x + 0, // y + 0, // nWidth + 0, // nHeight + HWND_MESSAGE, // hWndParent + NULL, // hMenu + module, // hInstance + this); // lpParam + + if (!m_is_master) { + // Notify master we are waiting him. + SendMessage(hwnd_master, s_msg_attach, 0, (LPARAM)m_hwnd); + + // Slaves must pump message queue until finished. + MSG msg; + while (GetMessage(&msg, NULL, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + + +eap::monitor_ui::~monitor_ui() +{ + if (m_hwnd) + DestroyWindow(m_hwnd); +} + + +void eap::monitor_ui::set_popup(_In_ HWND hwnd) +{ + m_hwnd_popup = hwnd; +} + + +void eap::monitor_ui::release_slaves(_In_bytecount_(size) const void *data, _In_ size_t size) const +{ + assert(!size || data); + + for (list::const_iterator slave = m_slaves.begin(), slave_end = m_slaves.end(); slave != slave_end; ++slave) { + // Get slave's PID. + DWORD pid_slave; + GetWindowThreadProcessId(*slave, &pid_slave); + + // Get slave's process handle. + process proc_slave; + if (!proc_slave.open(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, 0, pid_slave)) + continue; + + // Allocate memory in slave's virtual memory space and save data to it. + vmemory mem_slave; + if (!mem_slave.alloc(proc_slave, NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) + continue; + if (!WriteProcessMemory(proc_slave, mem_slave, data, size, NULL)) + continue; + + // Notify slave. Use SendMessage(), not PostMessage(), as memory will get cleaned up. + SendMessage(*slave, s_msg_finish, (WPARAM)size, (LPARAM)(LPVOID)mem_slave); + } +} + + +LRESULT eap::monitor_ui::winproc( + _In_ UINT msg, + _In_ WPARAM wparam, + _In_ LPARAM lparam) +{ + UNREFERENCED_PARAMETER(wparam); + + if (msg == s_msg_attach) { + // Attach a new slave. + assert(m_is_master); + m_slaves.push_back((HWND)lparam); + + if (m_hwnd_popup) { + // Bring pop-up window up. + if (::IsIconic(m_hwnd_popup)) + ::SendMessage(m_hwnd_popup, WM_SYSCOMMAND, SC_RESTORE, 0); + ::SetActiveWindow(m_hwnd_popup); + ::SetForegroundWindow(m_hwnd_popup); + } + + return TRUE; + } else if (msg == s_msg_finish) { + // Master finished. + assert(!m_is_master); + m_data.assign((const unsigned char*)lparam, (const unsigned char*)lparam + wparam); + + // Finish slave too. + DestroyWindow(m_hwnd); + return TRUE; + } else if (msg == WM_DESTROY) { + // Stop the message pump. + PostQuitMessage(0); + return 0; + } + + return DefWindowProc(m_hwnd, msg, wparam, lparam); +} + + +LRESULT CALLBACK eap::monitor_ui::winproc( + _In_ HWND hwnd, + _In_ UINT msg, + _In_ WPARAM wparam, + _In_ LPARAM lparam) +{ + if (msg == WM_CREATE) { + // Set window's user data to "this" pointer. + const CREATESTRUCT *cs = (CREATESTRUCT*)lparam; + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)cs->lpCreateParams); + + // Forward to our handler. + return ((eap::monitor_ui*)cs->lpCreateParams)->winproc(msg, wparam, lparam); + } else { + // Get "this" pointer from window's user data. + eap::monitor_ui *_this = (eap::monitor_ui*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (_this) { + // Forward to our handler. + return _this->winproc(msg, wparam, lparam); + } else + return DefWindowProc(hwnd, msg, wparam, lparam); + } +} + + +const UINT eap::monitor_ui::s_msg_attach = RegisterWindowMessage(_T(PRODUCT_NAME_STR) _T("-Attach")); +const UINT eap::monitor_ui::s_msg_finish = RegisterWindowMessage(_T(PRODUCT_NAME_STR) _T("-Finish")); diff --git a/lib/WinStd b/lib/WinStd index 9867626..54ab70b 160000 --- a/lib/WinStd +++ b/lib/WinStd @@ -1 +1 @@ -Subproject commit 986762649ff433b5f4321b805239fcd8f4da17f5 +Subproject commit 54ab70b26356eaccec1f793bc9e15624eb2e89f8