diff options
-rw-r--r-- | c/CMakeLists.txt | 4 | ||||
-rw-r--r-- | c/data.cpp | 42 | ||||
-rw-r--r-- | c/data.h | 20 | ||||
-rw-r--r-- | c/debug.cpp | 9 | ||||
-rw-r--r-- | c/debug.h | 4 | ||||
-rw-r--r-- | c/err.cpp | 87 | ||||
-rw-r--r-- | c/err.h | 25 | ||||
-rw-r--r-- | c/ext.cpp | 10 | ||||
-rw-r--r-- | c/main.cpp | 9 | ||||
-rw-r--r-- | c/test.cpp | 27 | ||||
-rw-r--r-- | c/util.h | 18 | ||||
-rw-r--r-- | c/win32.cpp | 33 | ||||
-rw-r--r-- | c/win32.h | 15 |
13 files changed, 204 insertions, 99 deletions
diff --git a/c/CMakeLists.txt b/c/CMakeLists.txt index 0c8bb83..ccf8812 100644 --- a/c/CMakeLists.txt +++ b/c/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) add_compile_definitions(UNICODE _UNICODE) if(NOT CMAKE_CONFIGURATION_TYPES MATCHES "Release") - add_compile_definitions(_DEBUG) + add_compile_definitions(_DEBUG NDEBUG) endif() add_executable(EpisodeBrowser WIN32) @@ -24,6 +24,8 @@ target_sources(EpisodeBrowser PRIVATE drag.h episodelistview.cpp episodelistview.h + err.cpp + err.h ext.cpp ext.h listview.cpp @@ -7,28 +7,31 @@ #include "data.h" #include "episodelistview.h" +#include "err.h" #include "res.h" #include "util.h" #include "win32.h" #include "window.h" +using namespace std::string_literals; + UniqueOk<htmlParserCtxtPtr, xmlFreeParserCtxt> RemoteParserCtxt(const wchar_t* wszUrl, const char* szUrl) { static Unique<HINTERNET, InternetCloseHandle> hi = InternetOpenW(L"Episode Browser", INTERNET_OPEN_TYPE_DIRECT, nullptr, nullptr, 0); if (hi.Bad(0)) - throw Win32Error(); + throw Err(WINDOWS, L"Internet handle could not be opened: %s"); Unique<HINTERNET, InternetCloseHandle> hiUrl = InternetOpenUrlW( hi.v, wszUrl, nullptr, 0, INTERNET_FLAG_NO_UI, 0); if (hiUrl.Bad(0)) - throw InternetError(); + throw Err(WININET, L"Could not open "s + wszUrl + L": %s"); char bufX[1024]; Unique<htmlParserCtxtPtr, xmlFreeParserCtxt> ctx = htmlCreatePushParserCtxt( nullptr, nullptr, bufX, sizeof(bufX), szUrl, XML_CHAR_ENCODING_UTF8); if (ctx.Bad(0)) - throw XmlError(); + throw Err(LIBXML2, L"HTML parser context could not be created: %s"); htmlCtxtUseOptions(ctx.v, HTML_PARSE_RECOVER|HTML_PARSE_NOERROR|HTML_PARSE_NOWARNING); @@ -37,9 +40,9 @@ UniqueOk<htmlParserCtxtPtr, xmlFreeParserCtxt> RemoteParserCtxt(const wchar_t* w char bufI[1024]; while (r = InternetReadFile(hiUrl.v, bufI, sizeof(bufI), &cbRead), cbRead) { if (!r) - throw InternetError(); + throw Err(WININET, L"HTML could not be retrieved: %s"); if (!htmlParseChunk(ctx.v, bufI, cbRead, 0)) - throw XmlError(); + throw Err(LIBXML2, L"HTML could not be parsed: %s"); } htmlParseChunk(ctx.v, bufI, 0, 1); /* Stop parsing. */ @@ -52,7 +55,7 @@ template <size_t N> bool WcharsFromXmlchars(wchar_t (&dst)[N], Unique<xmlChar*, XmlFree> utf8) { if (utf8.Bad(0)) - throw XmlError(); + throw Err(LIBXML2, L"Node content could not be retrieved: %s"); /* Truncate if source is larger than destination. */ int lenUtf8 = xmlStrlen(utf8.v); @@ -125,8 +128,7 @@ void WaitFor(Window& window, void (*f)(unsigned char*)) sig |= DONE; } catch (...) { sig |= DONE; - ShowException(L"Remote data could not be fetched due to an error while %s: %s", - L"Error", MB_ICONWARNING); + EBMessageBox(What(), L"Remote Data Retrieval Error"); } }; @@ -155,8 +157,6 @@ void WaitFor(Window& window, void (*f)(unsigned char*)) void FetchData(unsigned char* sig) { - Act(L"fetching and parsing general episode data"); - /* The remote data is retrieved using WinINet from the * Detective Conan World wiki. Using libxml2's "push parser", * the HTML is parsed piece by piece as it is retrieved. The @@ -164,26 +164,26 @@ void FetchData(unsigned char* sig) * specific XPath query. This is fragile theoretically, but * unlikely to break practically. */ + wchar_t url[_countof(s_window->cfg.prefixUrl)+46+1]; + Swprintf(url, L"%shttps://www.detectiveconanworld.com/wiki/Anime", s_window->cfg.prefixUrl); + UniqueOk<htmlParserCtxtPtr, xmlFreeParserCtxt> ctx = - RemoteParserCtxt(L"https://www.detectiveconanworld.com/wiki/Anime", - "https://www.detectiveconanworld.com/wiki/Anime"); + RemoteParserCtxt(url, "https://www.detectiveconanworld.com/wiki/Anime"); Unique<xmlXPathContextPtr, xmlXPathFreeContext> xpathCtx = xmlXPathNewContext(ctx.v->myDoc); if (xpathCtx.Bad(0)) - throw XmlError(); + throw Err(LIBXML2, L"XPath context could not be created: %s"); Unique<xmlXPathObjectPtr, xmlXPathFreeObject> xpathObj = xmlXPathEvalExpression( reinterpret_cast<const xmlChar*>("//tr[./td[1] != '' and ./td[3][@style='background:#f2fde9;']]"), xpathCtx.v); if (xpathObj.Bad(0)) - throw XmlError(); + throw Err(LIBXML2, L"XPath object could not be created: %s"); xmlNodeSetPtr nodes = xpathObj.v->nodesetval; if (!nodes || !nodes->nodeNr) - throw std::runtime_error("could not find remote episode information"); - - Act(L"inserting remote data"); + throw Err(GENERIC, L"Data retrieval failed: No matching HTML nodes found."); for (int i = 0; i < nodes->nodeNr; i++) { if (*sig & ABORT) @@ -191,7 +191,7 @@ void FetchData(unsigned char* sig) const xmlNodePtr node = nodes->nodeTab[i]; if (xmlChildElementCount(node) != 8) - throw std::runtime_error("unexpected remote data format"); + throw Err(GENERIC, L"Data retrieval failed: Unexcepted number of columns in table."); ElvDataA& e = s_window->fvElv.At(i); DlvDataA& d = s_window->fvDlv.At(i); @@ -226,8 +226,6 @@ void FetchData(unsigned char* sig) void FetchScreenwriters(unsigned char* sig) { - Act(L"fetching and parsing screenwriter data"); - /* Screenwriters are expensive to fetch, so we try to avoid * fetching screenwriters for episodes that already have a * screenwriter. Additionally, in the same session, we don't @@ -271,13 +269,13 @@ void FetchScreenwriters(unsigned char* sig) UniqueOk<htmlParserCtxtPtr, xmlFreeParserCtxt> ctx = RemoteParserCtxt(url, nullptr); xpathCtx = xmlXPathNewContext(ctx.v->myDoc); if (xpathCtx.Bad(0)) - throw XmlError(); + throw Err(LIBXML2, L"XPath context could not be created: %s"); xpathObj = xmlXPathEvalExpression(reinterpret_cast<const xmlChar*>( "//th[contains(text(), 'Screenplay:')]/following-sibling::td"), xpathCtx.v); if (xpathObj.Bad(0)) - throw XmlError(); + throw Err(LIBXML2, L"XPath object could not be created: %s"); xmlNodeSetPtr nodes = xpathObj.v->nodesetval; if (nodes && nodes->nodeNr) @@ -5,9 +5,12 @@ #include <windows.h> #include <libxml/xmlerror.h> +#include "err.h" #include "util.h" #include "win32.h" +using namespace std::string_literals; + struct XmlError : public std::exception { const char* msg; @@ -102,6 +105,7 @@ struct FileView Unique<HANDLE, CloseHandle> hf; Unique<HANDLE, CloseHandle> hm; Unique<T*, FreeView> view; + const wchar_t* filename; size_t c; static FileView Initialized(const wchar_t* filename, size_t c, size_t cInit = 0) @@ -118,7 +122,7 @@ struct FileView return {filename, c}; } - FileView(const wchar_t* filename, size_t c) : c(c) + FileView(const wchar_t* filename, size_t c) : filename(filename), c(c) { hf = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); @@ -127,9 +131,9 @@ struct FileView hf = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); if (hf.Bad(INVALID_HANDLE_VALUE)) - throw Win32Error(); + throw Err(WINDOWS, L"File "s + filename + L" could not be created: %s"); } else - throw Win32Error(); + throw Err(WINDOWS, L"File "s + filename + L" could not be opened: %s"); } LARGE_INTEGER cbMap; @@ -137,11 +141,11 @@ struct FileView hm = CreateFileMapping(hf.v, nullptr, PAGE_READWRITE, cbMap.HighPart, cbMap.LowPart, nullptr); if (hm.Bad(0)) - throw Win32Error(); + throw Err(WINDOWS, L"File mapping for "s + filename + L" could not be created: %s"); view = reinterpret_cast<T*>(MapViewOfFile(hm.v, FILE_MAP_ALL_ACCESS, 0, 0, 0)); if (view.Bad(0)) - throw Win32Error(); + throw Err(WINDOWS, L"View for "s + filename + L"could not be mapped: %s"); } /* Access element by index, performing bounds check and, if @@ -150,7 +154,8 @@ struct FileView T& At(size_t i) { if (i >= c) - throw std::out_of_range("index larger than buffer"); + throw Err(GENERIC, L"Element in "s + filename + + L" could not be accessed: Index larger than buffer."); T& t = view.v[i]; @@ -159,7 +164,8 @@ struct FileView T t_; memcpy(&t, &t_, sizeof(T)); } else if (t.version != Version<T>) - throw std::runtime_error("invalid struct version"); + throw Err(GENERIC, L"Element in "s + filename + + L" could not be accessed: Invalid format version."); } /* TODO: Use a custom exception type that informs the diff --git a/c/debug.cpp b/c/debug.cpp index 3e20d73..39dfb5d 100644 --- a/c/debug.cpp +++ b/c/debug.cpp @@ -3,6 +3,7 @@ #include <windows.h> #include "debug.h" +#include "err.h" #include "win32.h" struct Avg { @@ -19,17 +20,17 @@ Benchmark::Benchmark(const char* const name, const int id, const int avgmax) { if (!freq) { if (!QueryPerformanceFrequency(&liFreq)) - throw Win32Error(); + throw Err(WINDOWS, L"Tick frequency could not be queried: %s"); freq = liFreq.QuadPart; } LARGE_INTEGER liTicks; if (!QueryPerformanceCounter(&liTicks)) - throw Win32Error(); + throw Err(WINDOWS, L"Ticks could not be retrieved: %s"); ticks = liTicks.QuadPart; } -void Benchmark::Disable() +void Benchmark::Disable() noexcept { disabled = true; } @@ -66,7 +67,7 @@ Benchmark::~Benchmark() } } -const char* WmName(const UINT uMsg) +const char* WmName(const UINT uMsg) noexcept { static const UINT vKey[] = { 0, 1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, @@ -13,7 +13,7 @@ struct Benchmark ~Benchmark(); /* Don't print time on destruction. */ - void Disable(); + void Disable() noexcept; long long ticks; bool disabled = false; @@ -23,6 +23,6 @@ struct Benchmark }; /* Return name of given window message (WM_*). */ -const char* WmName(UINT uMsg); +const char* WmName(UINT uMsg) noexcept; #endif diff --git a/c/err.cpp b/c/err.cpp new file mode 100644 index 0000000..fd35ddd --- /dev/null +++ b/c/err.cpp @@ -0,0 +1,87 @@ +#include <exception> +#include <windows.h> +#include <wininet.h> +#include <libxml/xmlerror.h> + +#include "err.h" +#include "util.h" +#include "win32.h" + +Err::Err(ErrType t, const wchar_t* fmt) +{ + switch (t) { + case GENERIC: + what = fmt; + break; + case WINDOWS: + { + wchar_t* msg; + DWORD lenMsg = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER + |FORMAT_MESSAGE_FROM_SYSTEM + |FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + GetLastError(), + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (wchar_t*)&msg, + 0, nullptr); + what = std::wstring(lenMsg+wcslen(fmt), 0); + Swprintf(what, fmt, msg); + HeapFree(GetProcessHeap(), 0, msg); + break; + } + case WININET: + { + DWORD code = GetLastError(); + if (code == ERROR_INTERNET_EXTENDED_ERROR) { + DWORD lenMsg; + InternetGetLastResponseInfo(&code, nullptr, &lenMsg); + std::wstring msg(lenMsg, 0); + if (InternetGetLastResponseInfoW(&code, msg.data(), &lenMsg)) { + what = std::wstring(lenMsg+wcslen(fmt), 0); + Swprintf(what, fmt, msg.c_str()); + EBMessageBox(msg, L"Test"); + } else + what = Err(WINDOWS, fmt).what; + } else { + wchar_t* wszMsg; + DWORD lenMsg = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER + |FORMAT_MESSAGE_FROM_SYSTEM + |FORMAT_MESSAGE_FROM_HMODULE + |FORMAT_MESSAGE_IGNORE_INSERTS, + GetModuleHandle(L"wininet.dll"), + code, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (wchar_t*)&wszMsg, + 0, nullptr); + what = std::wstring(lenMsg+wcslen(fmt), 0); + Swprintf(what, fmt, wszMsg); + HeapFree(GetProcessHeap(), 0, wszMsg); + } + break; + } + case LIBXML2: + { + std::wstring msg = WideFromNarrow(xmlGetLastError()->message); + what = std::wstring(msg.size()+wcslen(fmt), 0); + Swprintf(what, fmt, msg.c_str()); + break; + } + } +} + +std::wstring What(std::exception_ptr ex) +{ + try { + std::rethrow_exception(ex); + } catch (const Err& e) { + return e.what; + } catch (const WideException& e) { + return e.What(); + } catch (const std::exception& e) { + return WideFromNarrow(e.what()); + } catch (...) { + return L"Unknown exception"; + } +} @@ -0,0 +1,25 @@ +#ifndef ERR_H +#define ERR_H + +#include <exception> +#include <string> + +enum ErrType : unsigned char +{ + GENERIC, + WINDOWS, + WININET, + LIBXML2 +}; + +struct Err +{ + std::wstring what; + Err(ErrType t, const wchar_t* fmt = L"%s"); + inline Err(ErrType t, std::wstring fmt) : Err(t, fmt.c_str()) {} +}; + +/* Return a wide string describing exception. */ +std::wstring What(std::exception_ptr e = std::current_exception()); + +#endif @@ -12,7 +12,7 @@ bool OpenOnline(const CfgA& cfg, int iEp) INT_PTR r = reinterpret_cast<INT_PTR>( ShellExecuteW(nullptr, L"open", url, nullptr, nullptr, SW_SHOWNORMAL)); if (r <= 32) - throw Win32Error(); + throw Err(WINDOWS, L"Address "s + url + L" could not be opened: %s"); return true; } @@ -23,7 +23,7 @@ bool OpenWiki(const DlvDataA& d) INT_PTR r = reinterpret_cast<INT_PTR>( ShellExecuteW(nullptr, L"open", url, nullptr, nullptr, SW_SHOWNORMAL)); if (r <= 32) - throw Win32Error(); + throw Err(WINDOWS, L"Address "s + url + L" could not be opened: %s"); return true; } @@ -84,7 +84,7 @@ static bool FindMatchingFile(wchar_t (&file)[MAX_PATH], const wchar_t* const roo WIN32_FIND_DATA fdata; Unique<HANDLE, FindClose> h = FindFirstFileW(pat, &fdata); if (h.Bad(INVALID_HANDLE_VALUE)) - throw Win32Error(); + throw Err(WINDOWS, L"Directory "s + root + L" could not be traversed: %s"); do if (fdata.cFileName[0] == L'.') @@ -102,7 +102,7 @@ static bool FindMatchingFile(wchar_t (&file)[MAX_PATH], const wchar_t* const roo while (FindNextFileW(h.v, &fdata)); if (GetLastError() != ERROR_NO_MORE_FILES) - throw Win32Error(); + throw Err(WINDOWS, L"Next file in "s + root + L" could not be accessed: %s"); return false; } @@ -114,7 +114,7 @@ bool OpenLocally(CfgA& cfg, const ElvDataA& e) INT_PTR r = reinterpret_cast<INT_PTR>( ShellExecuteW(nullptr, L"open", file, nullptr, nullptr, SW_SHOWNORMAL)); if (r <= 32) - throw Win32Error(); + throw Err(WINDOWS, L"File "s + file + L" could not be opened: %s"); return true; } else return false; @@ -126,13 +126,11 @@ static void InitializeMainWindow_(const HWND hWnd) * initializes global variables that are used by WndProc. */ /* Look up DPI. */ - Act(L"looking up DPI"); if (auto lib = Library::Maybe(L"User32.dll"); auto GetDpiForWindow = lib? lib->GetProcAddress<UINT(HWND)>("GetDpiForWindow"): nullptr) g_dpi = GetDpiForWindow(hWnd); /* Load normal font. */ - Act(L"loading fonts"); if (auto lib = Library::Maybe(L"User32.dll"); lib && lib->GetProcAddress<void>("SystemParametersInfoW")) { NONCLIENTMETRICSW m = {sizeof(NONCLIENTMETRICSW)}; @@ -160,7 +158,6 @@ static void InitializeMainWindow_(const HWND hWnd) SetWindowTheme = (decltype(SetWindowTheme))(void*)GetProcAddress(hModule, "SetWindowTheme"); } - Act(L"setting up main window"); g_window = new Window(hWnd); } @@ -169,18 +166,17 @@ void InitializeMainWindow(const HWND hWnd) noexcept try { InitializeMainWindow_(hWnd); } catch (...) { - ShowException(L"Initialization failed due to an error while %s: %s"); + EBMessageBox(What(), L"Initialization Error", MB_ICONERROR); exit(1); } } LRESULT CALLBACK WndProc(const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) { - Act(nullptr); try { return g_window->WndProc(hWnd, uMsg, wParam, lParam); } catch (...) { - ShowException(L"The action was cancelled due to an error while %s: %s"); + EBMessageBox(What(), L"Error"); } return DefWindowProc(hWnd, uMsg, wParam, lParam); } @@ -223,7 +219,6 @@ LRESULT CALLBACK Window::WndProc(const HWND hWnd, const UINT uMsg, const WPARAM { switch (uMsg) { case WM_CREATE: - Act(L"creating main window"); UpdateTheme(); SetWindowPos(hWnd, nullptr, -1, -1, Dpi(510), Dpi(412), SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE); SetFocus(elv.hWnd); @@ -112,6 +112,31 @@ TESTS // #undef CPY // #undef SCPY // } + + TEST(NewCfg) + { + FileView<CfgA> fv2 = FileView<CfgA>::Initialized(L"cfg2.dat", 1); + CfgA* a = &s_window->cfg; + CfgA* b = fv2+0; + +#define CPY(member) b->member = a->member; +#define SCPY(member) Wcscpy(b->member, a->member); + + CPY(bViewWatched); + CPY(bViewTVOriginal); + CPY(iSortCol); + CPY(cEp); + CPY(iFocus); + CPY(heightDlv); + SCPY(limitToScreenwriter); + SCPY(root); + SCPY(glob); + SCPY(url); + Swprintf(b->prefixUrl, L"http://localhost:8000/?u="); + +#undef CPY +#undef SCPY + } }; int RunTests(Window& window) @@ -121,7 +146,7 @@ int RunTests(Window& window) const Test tests[] = { StrcpyWithSmallerDestination(), //IO(), - // MigrateCfg(), + //NewCfg(), }; printf("Results (%llu tests):\n", sizeof(tests)/sizeof(*tests)); @@ -1,10 +1,12 @@ #ifndef UTIL_H #define UTIL_H +#include <cassert> #include <cstring> #include <memory> #include <stdexcept> #include <string> +#include <type_traits> #include <windows.h> #define CONCAT_IMPL(a, b) a##b @@ -14,9 +16,7 @@ #define SET_TERMINATE \ std::set_terminate([]() noexcept \ { \ - ShowException( \ - L"Episode Browser was terminated due to an error while %s: %s", \ - L"Fatal Error", MB_ICONERROR); \ + EBMessageBox(What(), L"Fatal Error", MB_ICONERROR); \ _Exit(1); \ }) @@ -98,14 +98,12 @@ struct UniqueOk T v; UniqueOk(Unique<T, F>&& u) : v(std::move(u.v)) { - if (!u.ok) - throw std::runtime_error("cannot construct UniqueOk from non-ok Unique"); + assert(u.ok, "UniqueOk may not be constructed from non-ok Unique"); u.ok = false; } UniqueOk& operator =(Unique<T, F>&& u) { - if (!u.ok) - throw std::runtime_error("cannot construct UniqueOk from non-ok Unique"); + assert(u.ok, "UniqueOk may not be constructed from non-ok Unique"); F(v); v = std::move(u.v); u.ok = false; @@ -153,14 +151,16 @@ inline size_t Len(T (&)[N]) /* Format wide string. */ template<typename... T> -inline int Swprintf(Buf<wchar_t> buf, const wchar_t* const fmt, T... xs) +inline std::enable_if_t<!std::disjunction_v<std::is_class<T>...>, int> +Swprintf(Buf<wchar_t> buf, const wchar_t* const fmt, T... xs) { return _snwprintf_s(buf, buf.c, _TRUNCATE, fmt, xs...); } /* Format static narrow string. */ template<typename... T> -inline int Sprintf(Buf<char> buf, const char* const fmt, T... xs) +inline std::enable_if_t<!std::disjunction_v<std::is_class<T>...>, int> +Sprintf(Buf<char> buf, const char* const fmt, T... xs) { return _snprintf_s(buf, buf.c, _TRUNCATE, fmt, xs...); } diff --git a/c/win32.cpp b/c/win32.cpp index 2686f2e..2ecea50 100644 --- a/c/win32.cpp +++ b/c/win32.cpp @@ -4,6 +4,7 @@ #include <windows.h> #include <wininet.h> +#include "err.h" #include "util.h" #include "win32.h" #include "window.h" @@ -16,7 +17,7 @@ std::wstring WideFromNarrow(const std::string_view src, const int cp) 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(); + throw Err(WINDOWS, L"Narrow string could not be converted to wide string: %s"); return dst; } @@ -95,34 +96,6 @@ int EBMessageBox(const std::wstring_view text, const std::wstring_view caption, return MessageBox(hWnd, text.data(), caption.data(), uType); } -void Act(const wchar_t* action) -{ - s_action = action; -} - -void ShowException(const wchar_t* const fmt, const wchar_t* const title, const UINT uType) noexcept -{ - if (!s_action) - s_action = L"performing an unknown action"; - try { - std::rethrow_exception(std::current_exception()); - } catch (const WideException& e) { - std::wstring msg(wcslen(fmt)+wcslen(s_action)+wcslen(e.What()), 0); - Swprintf(msg, fmt, s_action, e.What()); - EBMessageBox(msg, title, uType); - } catch (const std::exception& e) { - std::wstring what = WideFromNarrow(e.what()); - std::wstring msg(wcslen(fmt)+wcslen(s_action)+what.length(), 0); - Swprintf(msg, fmt, s_action, what.c_str()); - EBMessageBox(msg, title, uType); - } catch (...) { - const wchar_t* what = L"an unknown error occurred"; - std::wstring msg(wcslen(fmt)+wcslen(s_action)+wcslen(what), 0); - Swprintf(msg, fmt, s_action, what); - EBMessageBox(msg, title, uType); - } -} - int GetRelativeCursorPos(const HWND hWnd, POINT* const pt) noexcept { RECT rc; @@ -229,7 +202,7 @@ Library::Library(const wchar_t* const lib) { m_hModule = LoadLibraryW(lib); if (!m_hModule) - throw Win32Error(); + throw Err(WINDOWS, L"Library "s + lib + L" could not be loaded: %s"); } Library::~Library() @@ -6,6 +6,8 @@ #include <windows.h> #include <commctrl.h> +#include "err.h" + /* Convert narrow to wide string. */ std::wstring WideFromNarrow(const std::string_view src, const int cp = CP_UTF8); @@ -13,16 +15,7 @@ std::wstring WideFromNarrow(const std::string_view src, const int cp = CP_UTF8); void WithNextWindow(void (*proc)(HWND)); /* Display message box centered in main window. */ -int EBMessageBox(std::wstring_view text, std::wstring_view data, UINT uType); - -/* Specify current action (used by ShowException). */ -void Act(const wchar_t* action); - -/* Show message box for current exception. */ -void ShowException( - const wchar_t* fmt = L"An error occurred: %s", - const wchar_t* title = L"Error", - UINT uType = MB_ICONWARNING) noexcept; +int EBMessageBox(std::wstring_view text, std::wstring_view data, UINT uType = MB_ICONWARNING); /* Retrieve mouse position relative to given window's client area. */ int GetRelativeCursorPos(HWND hWnd, POINT* pt) noexcept; @@ -89,7 +82,7 @@ T* Library::GetProcAddress(const char* const szProc) noexcept template <typename T> inline T Require(const T x) { - if (!x) throw Win32Error(); + if (!x) Err(WINDOWS, L"System error: %s"); return x; } |