diff options
author | John Ankarström <john@ankarstrom.se> | 2022-09-02 20:16:04 +0200 |
---|---|---|
committer | John Ankarström <john@ankarstrom.se> | 2022-09-02 20:16:18 +0200 |
commit | bc4cef92d8efbf97a9215122abc2d7247c287f12 (patch) | |
tree | a5ca307281c4f143b5f172428c9fd2b629d6b426 /c/win32.cpp | |
parent | 5c1c2ce2bdbf9735ad8a4d162609a8c22a4f0954 (diff) | |
download | EpisodeBrowser-bc4cef92d8efbf97a9215122abc2d7247c287f12.tar.gz |
Improve Window object.
Diffstat (limited to 'c/win32.cpp')
-rw-r--r-- | c/win32.cpp | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/c/win32.cpp b/c/win32.cpp new file mode 100644 index 0000000..f06f96c --- /dev/null +++ b/c/win32.cpp @@ -0,0 +1,228 @@ +#include <utility> +#include <string> +#include <string_view> +#include <windows.h> +#include <wininet.h> + +#include "util.h" +#include "win32.h" +#include "window.h" + +std::wstring WideFromNarrow(const std::string_view src, const int cp) +{ + int cchNarrow = src.length()+1; + int cchWide = MultiByteToWideChar(cp, 0, src.data(), cchNarrow, nullptr, 0); + std::wstring dst(cchWide, 0); + if (!MultiByteToWideChar(cp, 0, src.data(), cchNarrow, dst.data(), cchWide)) + throw Win32Error(); + return dst; +} + +void WithNextWindow(void (*proc)(HWND)) +{ + /* WithNextWindow uses a CBT hook to call an arbitrary + * procedure just before the creation of the next window. The + * hook is removed as soon as HCBT_CREATEWND is received. + * Thus, proc is guaranteed to be executed only once. Note + * that static storage must be used, as SetWindowHookEx cannot + * accept a capturing lambda as the hook procedure. */ + + static thread_local auto procNext = proc; + static thread_local HHOOK hHook = Require(SetWindowsHookExW(WH_CBT, [](const int nCode, + const WPARAM wParam, const LPARAM lParam) -> LRESULT CALLBACK + { + if (nCode == HCBT_CREATEWND) { + Require(UnhookWindowsHookEx(hHook)); + procNext(reinterpret_cast<HWND>(wParam)); + return 0; + } else + return CallNextHookEx(0, nCode, wParam, lParam); + }, nullptr, GetCurrentThreadId())); +} + +/* Display next created window in the center of given window. */ +static void CenterNextWindow(HWND hWnd) +{ + if (!hWnd) + return; + + /* CenterNextWindow employs a trick similar to that above. + * Note, however, that repositioning does not work unless it + * is delayed until the new window is activated. This + * complicates the code somewhat. */ + + static thread_local HWND hWndParent; + static thread_local HWND hWndNext; + static thread_local HHOOK hHook; + + hWndParent = hWnd; + hWndNext = nullptr; + hHook = Require(SetWindowsHookExW(WH_CBT, [](const int nCode, + const WPARAM wParam, const LPARAM lParam) noexcept -> LRESULT CALLBACK + { + if (!hWndNext && nCode == HCBT_CREATEWND) { + hWndNext = reinterpret_cast<HWND>(wParam); + return 0; + } else if (nCode == HCBT_ACTIVATE + && reinterpret_cast<HWND>(wParam) == hWndNext) { + Require(UnhookWindowsHookEx(hHook)); + + long lStyle = GetWindowLongW(hWndNext, GWL_STYLE); + if (!(lStyle & WS_POPUP)) return 0; + + RECT rcMain, rcMsg; + GetWindowRect(hWndParent, &rcMain); + GetWindowRect(hWndNext, &rcMsg); + SetWindowPos(hWndNext, nullptr, + rcMain.left+(rcMain.right-rcMain.left)/2-(rcMsg.right-rcMsg.left)/2, + rcMain.top+(rcMain.bottom-rcMain.top)/2-(rcMsg.bottom-rcMsg.top)/2, + -1, -1, + SWP_NOZORDER|SWP_NOSIZE|SWP_NOACTIVATE); + + return 0; + } else + return CallNextHookEx(0, nCode, wParam, lParam); + }, nullptr, GetCurrentThreadId())); +} + +int EBMessageBox(const std::wstring_view text, const std::wstring_view caption, const UINT uType) +{ + extern Window* g_window; + CenterNextWindow(g_window->hWnd); + return MessageBox(g_window->hWnd, text.data(), caption.data(), uType); +} + +void ShowException(const wchar_t* const fmt, const wchar_t* const title, const UINT uType) noexcept +{ + try { + std::rethrow_exception(std::current_exception()); + } catch (const WideException& e) { + std::wstring msg(wcslen(fmt)+wcslen(e.What()), 0); + Swprintf(msg, fmt, e.What()); + EBMessageBox(msg, title, uType); + } catch (const std::exception& e) { + std::wstring what = WideFromNarrow(e.what()); + std::wstring msg(wcslen(fmt)+what.length(), 0); + Swprintf(msg, fmt, what.c_str()); + EBMessageBox(msg, title, uType); + } catch (...) { + const wchar_t* what = L"an unknown error occurred"; + std::wstring msg(wcslen(fmt)+wcslen(what), 0); + Swprintf(msg, fmt, what); + EBMessageBox(msg, title, uType); + } +} + +int GetRelativeCursorPos(const HWND hWnd, POINT* const pt) noexcept +{ + RECT rc; + if (!GetClientRect(hWnd, &rc)) return 0; + SetLastError(ERROR_SUCCESS); + if (!MapWindowPoints(hWnd, nullptr, (POINT*)&rc, 2)) return 0; + + POINT ptMouse; + if (!GetCursorPos(&ptMouse)) return 0; + pt->x = ptMouse.x-rc.left; + pt->y = ptMouse.y-rc.top; + return 1; +} + +Win32Error::Win32Error(const DWORD code, const HMODULE hModule) noexcept + : code(code), hModule(hModule) {} + +Win32Error::~Win32Error() +{ + if (m_szMsg) + HeapFree(GetProcessHeap(), 0, m_szMsg); + if (m_wszMsg) + HeapFree(GetProcessHeap(), 0, m_wszMsg); +} + +const char* Win32Error::what() const noexcept +{ + if (!m_szMsg) + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER + |FORMAT_MESSAGE_FROM_SYSTEM + |FORMAT_MESSAGE_FROM_HMODULE + |FORMAT_MESSAGE_IGNORE_INSERTS, + hModule, + code, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (char*)&m_szMsg, + 0, nullptr); + return m_szMsg? m_szMsg: "An unknown error occurred"; +} + +const wchar_t* Win32Error::What() const noexcept +{ + if (!m_wszMsg) + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER + |FORMAT_MESSAGE_FROM_SYSTEM + |FORMAT_MESSAGE_FROM_HMODULE + |FORMAT_MESSAGE_IGNORE_INSERTS, + hModule, + code, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (wchar_t*)&m_wszMsg, + 0, nullptr); + return m_wszMsg? m_wszMsg: L"An unknown error occurred"; +} + +InternetError::InternetError(DWORD codeSystem) +{ + if (codeSystem != ERROR_INTERNET_EXTENDED_ERROR) + throw Win32Error(codeSystem, GetModuleHandle(L"wininet.dll")); + + DWORD len, cch; + InternetGetLastResponseInfo(&code, nullptr, &len); + + cch = len+1; + m_szMsg = new char[cch]; + if (!InternetGetLastResponseInfoA(&code, m_szMsg, &cch)) + throw Win32Error(); + + cch = len+1; + m_wszMsg = new wchar_t[cch]; + if (!InternetGetLastResponseInfoW(&code, m_wszMsg, &len)) + throw Win32Error(); +} + +InternetError::~InternetError() +{ + delete m_szMsg; + delete m_wszMsg; +} + +const char* InternetError::what() const noexcept +{ + return m_szMsg; +} + +const wchar_t* InternetError::What() const noexcept +{ + return m_wszMsg; +} + +std::optional<Library> Library::Maybe(const wchar_t* const lib) noexcept +{ + HMODULE hModule = LoadLibraryW(lib); + if (!hModule) return {}; + return Library(hModule); +} + +Library::Library(const HMODULE hModule) noexcept + : m_hModule(hModule) {} + +Library::Library(const wchar_t* const lib) +{ + m_hModule = LoadLibraryW(lib); + if (!m_hModule) + throw Win32Error(); +} + +Library::~Library() +{ + FreeLibrary(m_hModule); +} |