aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ankarström <john@ankarstrom.se>2022-09-03 15:28:56 +0200
committerJohn Ankarström <john@ankarstrom.se>2022-09-03 15:28:56 +0200
commit6ae7e24675cff4ff6b808c3024f45083f35ced97 (patch)
tree2a47273f325d8367db2a8669691273f02ca83eb9
parent2cd22c671c67deaf2c1fcb659e3262bf57552557 (diff)
downloadEpisodeBrowser-6ae7e24675cff4ff6b808c3024f45083f35ced97.tar.gz
Improve error handling.
-rw-r--r--c/CMakeLists.txt4
-rw-r--r--c/data.cpp42
-rw-r--r--c/data.h20
-rw-r--r--c/debug.cpp9
-rw-r--r--c/debug.h4
-rw-r--r--c/err.cpp87
-rw-r--r--c/err.h25
-rw-r--r--c/ext.cpp10
-rw-r--r--c/main.cpp9
-rw-r--r--c/test.cpp27
-rw-r--r--c/util.h18
-rw-r--r--c/win32.cpp33
-rw-r--r--c/win32.h15
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
diff --git a/c/data.cpp b/c/data.cpp
index f862a5e..a75111a 100644
--- a/c/data.cpp
+++ b/c/data.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)
diff --git a/c/data.h b/c/data.h
index 2c287a2..bbc01d2 100644
--- a/c/data.h
+++ b/c/data.h
@@ -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,
diff --git a/c/debug.h b/c/debug.h
index e9cefa3..9a201a7 100644
--- a/c/debug.h
+++ b/c/debug.h
@@ -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";
+ }
+}
diff --git a/c/err.h b/c/err.h
new file mode 100644
index 0000000..d4a9976
--- /dev/null
+++ b/c/err.h
@@ -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
diff --git a/c/ext.cpp b/c/ext.cpp
index ea6718a..ab47ab4 100644
--- a/c/ext.cpp
+++ b/c/ext.cpp
@@ -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;
diff --git a/c/main.cpp b/c/main.cpp
index 9e8cee3..f9d4a42 100644
--- a/c/main.cpp
+++ b/c/main.cpp
@@ -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);
diff --git a/c/test.cpp b/c/test.cpp
index 21f52f4..e7c23e5 100644
--- a/c/test.cpp
+++ b/c/test.cpp
@@ -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));
diff --git a/c/util.h b/c/util.h
index da77ff9..668d040 100644
--- a/c/util.h
+++ b/c/util.h
@@ -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()
diff --git a/c/win32.h b/c/win32.h
index a88e97c..c18e70f 100644
--- a/c/win32.h
+++ b/c/win32.h
@@ -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;
}