diff --git a/include/WinStd/Common.h b/include/WinStd/Common.h index 83d0f08a..238a08c1 100644 --- a/include/WinStd/Common.h +++ b/include/WinStd/Common.h @@ -290,6 +290,354 @@ static int sprintf(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &str, _In_z_ _ return res; } +/// +/// Maps a UTF-16 (wide character) string to a std::string. The new character string is not necessarily from a multibyte character set. +/// +/// \sa [WideCharToMultiByte function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd374130.aspx) +/// +template +static _Success_(return != 0) int WideCharToMultiByte(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cchWideChar) LPCWSTR lpWideCharStr, _In_ int cchWideChar, _Out_ std::basic_string &sMultiByteStr, _In_opt_z_ LPCSTR lpDefaultChar, _Out_opt_ LPBOOL lpUsedDefaultChar) noexcept +{ + CHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES / sizeof(CHAR)]; + + // Try to convert to stack buffer first. + int cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, szStackBuffer, _countof(szStackBuffer), lpDefaultChar, lpUsedDefaultChar); + if (cch) { + // Copy from stack. Be careful not to include zero terminator. + sMultiByteStr.assign(szStackBuffer, cchWideChar != -1 ? strnlen(szStackBuffer, cch) : (size_t)cch - 1); + } + else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + // Query the required output size. Allocate buffer. Then convert again. + cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar); + std::unique_ptr szBuffer(new CHAR[cch]); + cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, szBuffer.get(), cch, lpDefaultChar, lpUsedDefaultChar); + sMultiByteStr.assign(szBuffer.get(), cchWideChar != -1 ? strnlen(szBuffer.get(), cch) : (size_t)cch - 1); + } + + return cch; +} + +/// +/// Maps a UTF-16 (wide character) string to a std::vector. The new character vector is not necessarily from a multibyte character set. +/// +/// \sa [WideCharToMultiByte function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd374130.aspx) +/// +template +static _Success_(return != 0) int WideCharToMultiByte(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cchWideChar) LPCWSTR lpWideCharStr, _In_ int cchWideChar, _Out_ std::vector &sMultiByteStr, _In_opt_z_ LPCSTR lpDefaultChar, _Out_opt_ LPBOOL lpUsedDefaultChar) noexcept +{ + CHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES / sizeof(CHAR)]; + + // Try to convert to stack buffer first. + int cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, szStackBuffer, _countof(szStackBuffer), lpDefaultChar, lpUsedDefaultChar); + if (cch) { + // Copy from stack. + sMultiByteStr.assign(szStackBuffer, szStackBuffer + cch); + } + else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + // Query the required output size. Allocate buffer. Then convert again. + cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar); + sMultiByteStr.resize(cch); + cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, sMultiByteStr.data(), cch, lpDefaultChar, lpUsedDefaultChar); + } + + return cch; +} + +/// +/// Maps a UTF-16 (wide character) string to a std::string. The new character string is not necessarily from a multibyte character set. +/// +/// \sa [WideCharToMultiByte function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd374130.aspx) +/// +template +static _Success_(return != 0) int WideCharToMultiByte(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_ std::basic_string sWideCharStr, _Out_ std::basic_string &sMultiByteStr, _In_opt_z_ LPCSTR lpDefaultChar, _Out_opt_ LPBOOL lpUsedDefaultChar) noexcept +{ + CHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES / sizeof(CHAR)]; + + // Try to convert to stack buffer first. + int cch = ::WideCharToMultiByte(CodePage, dwFlags, sWideCharStr.c_str(), (int)sWideCharStr.length(), szStackBuffer, _countof(szStackBuffer), lpDefaultChar, lpUsedDefaultChar); + if (cch) { + // Copy from stack. + sMultiByteStr.assign(szStackBuffer, cch); + } + else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + // Query the required output size. Allocate buffer. Then convert again. + cch = ::WideCharToMultiByte(CodePage, dwFlags, sWideCharStr.c_str(), (int)sWideCharStr.length(), NULL, 0, lpDefaultChar, lpUsedDefaultChar); + std::unique_ptr szBuffer(new CHAR[cch]); + cch = ::WideCharToMultiByte(CodePage, dwFlags, sWideCharStr.c_str(), (int)sWideCharStr.length(), szBuffer.get(), cch, lpDefaultChar, lpUsedDefaultChar); + sMultiByteStr.assign(szBuffer.get(), cch); + } + + return cch; +} + +/// +/// Maps a UTF-16 (wide character) string to a std::string. The new character string is not necessarily from a multibyte character set. +/// +/// \note This function cleans all internal buffers using SecureZeroMemory() before returning. +/// +/// \sa [WideCharToMultiByte function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd374130.aspx) +/// +template +static _Success_(return != 0) int SecureWideCharToMultiByte(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cchWideChar) LPCWSTR lpWideCharStr, _In_ int cchWideChar, _Out_ std::basic_string &sMultiByteStr, _In_opt_z_ LPCSTR lpDefaultChar, _Out_opt_ LPBOOL lpUsedDefaultChar) noexcept +{ + CHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES / sizeof(CHAR)]; + + // Try to convert to stack buffer first. + int cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, szStackBuffer, _countof(szStackBuffer), lpDefaultChar, lpUsedDefaultChar); + if (cch) { + // Copy from stack. Be careful not to include zero terminator. + sMultiByteStr.assign(szStackBuffer, cchWideChar != -1 ? strnlen(szStackBuffer, cch) : (size_t)cch - 1); + } + else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + // Query the required output size. Allocate buffer. Then convert again. + cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar); + std::unique_ptr szBuffer(new CHAR[cch]); + cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, szBuffer.get(), cch, lpDefaultChar, lpUsedDefaultChar); + sMultiByteStr.assign(szBuffer.get(), cchWideChar != -1 ? strnlen(szBuffer.get(), cch) : (size_t)cch - 1); + SecureZeroMemory(szBuffer.get(), sizeof(CHAR) * cch); + } + + SecureZeroMemory(szStackBuffer, sizeof(szStackBuffer)); + + return cch; +} + +/// +/// Maps a UTF-16 (wide character) string to a std::vector. The new character vector is not necessarily from a multibyte character set. +/// +/// \note This function cleans all internal buffers using SecureZeroMemory() before returning. +/// +/// \sa [WideCharToMultiByte function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd374130.aspx) +/// +template +static _Success_(return != 0) int SecureWideCharToMultiByte(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cchWideChar) LPCWSTR lpWideCharStr, _In_ int cchWideChar, _Out_ std::vector &sMultiByteStr, _In_opt_z_ LPCSTR lpDefaultChar, _Out_opt_ LPBOOL lpUsedDefaultChar) noexcept +{ + CHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES / sizeof(CHAR)]; + + // Try to convert to stack buffer first. + int cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, szStackBuffer, _countof(szStackBuffer), lpDefaultChar, lpUsedDefaultChar); + if (cch) { + // Copy from stack. + sMultiByteStr.assign(szStackBuffer, szStackBuffer + cch); + } + else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + // Query the required output size. Allocate buffer. Then convert again. + cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar); + sMultiByteStr.resize(cch); + cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, sMultiByteStr.data(), cch, lpDefaultChar, lpUsedDefaultChar); + } + + SecureZeroMemory(szStackBuffer, sizeof(szStackBuffer)); + + return cch; +} + +/// +/// Maps a UTF-16 (wide character) string to a std::string. The new character string is not necessarily from a multibyte character set. +/// +/// \note This function cleans all internal buffers using SecureZeroMemory() before returning. +/// +/// \sa [WideCharToMultiByte function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd374130.aspx) +/// +template +static _Success_(return != 0) int SecureWideCharToMultiByte(_In_ UINT CodePage, _In_ DWORD dwFlags, _Out_ std::basic_string sWideCharStr, _Out_ std::basic_string &sMultiByteStr, _In_opt_z_ LPCSTR lpDefaultChar, _Out_opt_ LPBOOL lpUsedDefaultChar) noexcept +{ + CHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES / sizeof(CHAR)]; + + // Try to convert to stack buffer first. + int cch = ::WideCharToMultiByte(CodePage, dwFlags, sWideCharStr.c_str(), (int)sWideCharStr.length(), szStackBuffer, _countof(szStackBuffer), lpDefaultChar, lpUsedDefaultChar); + if (cch) { + // Copy from stack. + sMultiByteStr.assign(szStackBuffer, cch); + } + else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + // Query the required output size. Allocate buffer. Then convert again. + cch = ::WideCharToMultiByte(CodePage, dwFlags, sWideCharStr.c_str(), (int)sWideCharStr.length(), NULL, 0, lpDefaultChar, lpUsedDefaultChar); + std::unique_ptr szBuffer(new CHAR[cch]); + cch = ::WideCharToMultiByte(CodePage, dwFlags, sWideCharStr.c_str(), (int)sWideCharStr.length(), szBuffer.get(), cch, lpDefaultChar, lpUsedDefaultChar); + sMultiByteStr.assign(szBuffer.get(), cch); + SecureZeroMemory(szBuffer.get(), sizeof(CHAR) * cch); + } + + SecureZeroMemory(szStackBuffer, sizeof(szStackBuffer)); + + return cch; +} + +/// +/// Maps a character string to a UTF-16 (wide character) std::wstring. The character string is not necessarily from a multibyte character set. +/// +/// \sa [MultiByteToWideChar function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072.aspx) +/// +template +static _Success_(return != 0) int MultiByteToWideChar(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cbMultiByte) LPCSTR lpMultiByteStr, _In_ int cbMultiByte, _Out_ std::basic_string &sWideCharStr) noexcept +{ + WCHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES / sizeof(WCHAR)]; + + // Try to convert to stack buffer first. + int cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, szStackBuffer, _countof(szStackBuffer)); + if (cch) { + // Copy from stack. + sWideCharStr.assign(szStackBuffer, cbMultiByte != -1 ? wcsnlen(szStackBuffer, cch) : (size_t)cch - 1); + } + else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + // Query the required output size. Allocate buffer. Then convert again. + cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0); + std::unique_ptr szBuffer(new WCHAR[cch]); + cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, szBuffer.get(), cch); + sWideCharStr.assign(szBuffer.get(), cbMultiByte != -1 ? wcsnlen(szBuffer.get(), cch) : (size_t)cch - 1); + } + + return cch; +} + +/// +/// Maps a character string to a UTF-16 (wide character) std::vector. The character vector is not necessarily from a multibyte character set. +/// +/// \sa [MultiByteToWideChar function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072.aspx) +/// +template +static _Success_(return != 0) int MultiByteToWideChar(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cbMultiByte) LPCSTR lpMultiByteStr, _In_ int cbMultiByte, _Out_ std::vector &sWideCharStr) noexcept +{ + WCHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES / sizeof(WCHAR)]; + + // Try to convert to stack buffer first. + int cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, szStackBuffer, _countof(szStackBuffer)); + if (cch) { + // Copy from stack. + sWideCharStr.assign(szStackBuffer, szStackBuffer + cch); + } + else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + // Query the required output size. Allocate buffer. Then convert again. + cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0); + sWideCharStr.resize(cch); + cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, sWideCharStr.data(), cch); + } + + return cch; +} + +/// +/// Maps a character string to a UTF-16 (wide character) std::wstring. The character string is not necessarily from a multibyte character set. +/// +/// \sa [MultiByteToWideChar function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072.aspx) +/// +template +static _Success_(return != 0) int MultiByteToWideChar(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_ const std::basic_string &sMultiByteStr, _Out_ std::basic_string &sWideCharStr) noexcept +{ + WCHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES / sizeof(WCHAR)]; + + // Try to convert to stack buffer first. + int cch = ::MultiByteToWideChar(CodePage, dwFlags, sMultiByteStr.c_str(), (int)sMultiByteStr.length(), szStackBuffer, _countof(szStackBuffer)); + if (cch) { + // Copy from stack. + sWideCharStr.assign(szStackBuffer, cch); + } + else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + // Query the required output size. Allocate buffer. Then convert again. + cch = ::MultiByteToWideChar(CodePage, dwFlags, sMultiByteStr.c_str(), (int)sMultiByteStr.length(), NULL, 0); + std::unique_ptr szBuffer(new WCHAR[cch]); + cch = ::MultiByteToWideChar(CodePage, dwFlags, sMultiByteStr.c_str(), (int)sMultiByteStr.length(), szBuffer.get(), cch); + sWideCharStr.assign(szBuffer.get(), cch); + } + + return cch; +} + +/// +/// Maps a character string to a UTF-16 (wide character) std::wstring. The character string is not necessarily from a multibyte character set. +/// +/// \note This function cleans all internal buffers using SecureZeroMemory() before returning. +/// +/// \sa [MultiByteToWideChar function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072.aspx) +/// +template +static _Success_(return != 0) int SecureMultiByteToWideChar(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cbMultiByte) LPCSTR lpMultiByteStr, _In_ int cbMultiByte, _Out_ std::basic_string &sWideCharStr) noexcept +{ + WCHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES / sizeof(WCHAR)]; + + // Try to convert to stack buffer first. + int cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, szStackBuffer, _countof(szStackBuffer)); + if (cch) { + // Copy from stack. + sWideCharStr.assign(szStackBuffer, cbMultiByte != -1 ? wcsnlen(szStackBuffer, cch) : (size_t)cch - 1); + } + else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + // Query the required output size. Allocate buffer. Then convert again. + cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0); + std::unique_ptr szBuffer(new WCHAR[cch]); + cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, szBuffer.get(), cch); + sWideCharStr.assign(szBuffer.get(), cbMultiByte != -1 ? wcsnlen(szBuffer.get(), cch) : (size_t)cch - 1); + SecureZeroMemory(szBuffer.get(), sizeof(WCHAR) * cch); + } + + SecureZeroMemory(szStackBuffer, sizeof(szStackBuffer)); + + return cch; +} + +/// +/// Maps a character string to a UTF-16 (wide character) std::vector. The character vector is not necessarily from a multibyte character set. +/// +/// \note This function cleans all internal buffers using SecureZeroMemory() before returning. +/// +/// \sa [MultiByteToWideChar function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072.aspx) +/// +template +static _Success_(return != 0) int SecureMultiByteToWideChar(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cbMultiByte) LPCSTR lpMultiByteStr, _In_ int cbMultiByte, _Out_ std::vector &sWideCharStr) noexcept +{ + WCHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES / sizeof(WCHAR)]; + + // Try to convert to stack buffer first. + int cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, szStackBuffer, _countof(szStackBuffer)); + if (cch) { + // Copy from stack. + sWideCharStr.assign(szStackBuffer, szStackBuffer + cch); + } + else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + // Query the required output size. Allocate buffer. Then convert again. + cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0); + sWideCharStr.resize(cch); + cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, sWideCharStr.data(), cch); + } + + SecureZeroMemory(szStackBuffer, sizeof(szStackBuffer)); + + return cch; +} + +/// +/// Maps a character string to a UTF-16 (wide character) std::wstring. The character string is not necessarily from a multibyte character set. +/// +/// \note This function cleans all internal buffers using SecureZeroMemory() before returning. +/// +/// \sa [MultiByteToWideChar function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072.aspx) +/// +template +static _Success_(return != 0) int SecureMultiByteToWideChar(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_ const std::basic_string &sMultiByteStr, _Out_ std::basic_string &sWideCharStr) noexcept +{ + WCHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES / sizeof(WCHAR)]; + + // Try to convert to stack buffer first. + int cch = ::MultiByteToWideChar(CodePage, dwFlags, sMultiByteStr.c_str(), (int)sMultiByteStr.length(), szStackBuffer, _countof(szStackBuffer)); + if (cch) { + // Copy from stack. + sWideCharStr.assign(szStackBuffer, cch); + } + else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + // Query the required output size. Allocate buffer. Then convert again. + cch = ::MultiByteToWideChar(CodePage, dwFlags, sMultiByteStr.c_str(), (int)sMultiByteStr.length(), NULL, 0); + std::unique_ptr szBuffer(new WCHAR[cch]); + cch = ::MultiByteToWideChar(CodePage, dwFlags, sMultiByteStr.c_str(), (int)sMultiByteStr.length(), szBuffer.get(), cch); + sWideCharStr.assign(szBuffer.get(), cch); + SecureZeroMemory(szBuffer.get(), sizeof(WCHAR) * cch); + } + + SecureZeroMemory(szStackBuffer, sizeof(szStackBuffer)); + + return cch; +} + /// /// Formats a message string. /// @@ -1073,15 +1421,22 @@ namespace winstd class win_runtime_error : public num_runtime_error { public: + /// + /// Constructs an exception + /// + /// \param[in] num Windows error code + /// + win_runtime_error(_In_ error_type num) : num_runtime_error(num, message(num)) + {} + /// /// Constructs an exception /// /// \param[in] num Windows error code /// \param[in] msg Error message /// - win_runtime_error(_In_ error_type num, _In_ const std::string& msg) : num_runtime_error(num, msg) - { - } + win_runtime_error(_In_ error_type num, _In_ const std::string& msg) : num_runtime_error(num, msg + ": " + message(num)) + {} /// /// Constructs an exception @@ -1089,41 +1444,49 @@ namespace winstd /// \param[in] num Windows error code /// \param[in] msg Error message /// - win_runtime_error(_In_ error_type num, _In_opt_z_ const char *msg = nullptr) : num_runtime_error(num, msg) - { - } + win_runtime_error(_In_ error_type num, _In_z_ const char *msg) : num_runtime_error(num, std::string(msg) + ": " + message(num)) + {} + + /// + /// Constructs an exception using `GetLastError()` + /// + win_runtime_error() : num_runtime_error(GetLastError(), message(GetLastError())) + {} /// /// Constructs an exception using `GetLastError()` /// /// \param[in] msg Error message /// - win_runtime_error(_In_ const std::string& msg) : num_runtime_error(GetLastError(), msg) - { - } + win_runtime_error(_In_ const std::string& msg) : num_runtime_error(GetLastError(), msg + ": " + message(GetLastError())) + {} /// /// Constructs an exception using `GetLastError()` /// /// \param[in] msg Error message /// - win_runtime_error(_In_opt_z_ const char *msg = nullptr) : num_runtime_error(GetLastError(), msg) - { - } + win_runtime_error(_In_z_ const char *msg) : num_runtime_error(GetLastError(), std::string(msg) + ": " + message(GetLastError())) + {} + protected: /// /// Returns a user-readable Windows error message /// /// \sa [FormatMessage function](https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-formatmessage) /// - tstring msg(_In_opt_ DWORD dwLanguageId = 0) const + static std::string message(_In_ error_type num, _In_opt_ DWORD dwLanguageId = 0) { - tstring str; - if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, m_num, dwLanguageId, str, NULL)) { + error_type runtime_num = GetLastError(); + std::wstring wstr; + if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, num, dwLanguageId, wstr, NULL)) { // Stock Windows error messages contain CRLF. Well... Trim all the trailing white space. - str.erase(str.find_last_not_of(_T(" \t\n\r\f\v")) + 1); + wstr.erase(wstr.find_last_not_of(L" \t\n\r\f\v") + 1); } else - sprintf(str, m_num >= 0x10000 ? _T("Error 0x%X") : _T("Error %u"), m_num); + sprintf(wstr, num >= 0x10000 ? L"Error 0x%X" : L"Error %u", num); + std::string str; + WideCharToMultiByte(CP_UTF8, 0, wstr, str, NULL, NULL); + SetLastError(runtime_num); return str; } }; diff --git a/include/WinStd/Win.h b/include/WinStd/Win.h index c670ecd3..ffafc16d 100644 --- a/include/WinStd/Win.h +++ b/include/WinStd/Win.h @@ -627,342 +627,6 @@ static LSTATUS RegLoadMUIStringW(_In_ HKEY hKey, _In_opt_z_ LPCWSTR pszValue, _O #endif -/// -/// Maps a UTF-16 (wide character) string to a std::string. The new character string is not necessarily from a multibyte character set. -/// -/// \sa [WideCharToMultiByte function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd374130.aspx) -/// -template -static _Success_(return != 0) int WideCharToMultiByte(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cchWideChar) LPCWSTR lpWideCharStr, _In_ int cchWideChar, _Out_ std::basic_string &sMultiByteStr, _In_opt_z_ LPCSTR lpDefaultChar, _Out_opt_ LPBOOL lpUsedDefaultChar) noexcept -{ - CHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES/sizeof(CHAR)]; - - // Try to convert to stack buffer first. - int cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, szStackBuffer, _countof(szStackBuffer), lpDefaultChar, lpUsedDefaultChar); - if (cch) { - // Copy from stack. Be careful not to include zero terminator. - sMultiByteStr.assign(szStackBuffer, cchWideChar != -1 ? strnlen(szStackBuffer, cch) : (size_t)cch - 1); - } else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - // Query the required output size. Allocate buffer. Then convert again. - cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar); - std::unique_ptr szBuffer(new CHAR[cch]); - cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, szBuffer.get(), cch, lpDefaultChar, lpUsedDefaultChar); - sMultiByteStr.assign(szBuffer.get(), cchWideChar != -1 ? strnlen(szBuffer.get(), cch) : (size_t)cch - 1); - } - - return cch; -} - -/// -/// Maps a UTF-16 (wide character) string to a std::vector. The new character vector is not necessarily from a multibyte character set. -/// -/// \sa [WideCharToMultiByte function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd374130.aspx) -/// -template -static _Success_(return != 0) int WideCharToMultiByte(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cchWideChar) LPCWSTR lpWideCharStr, _In_ int cchWideChar, _Out_ std::vector &sMultiByteStr, _In_opt_z_ LPCSTR lpDefaultChar, _Out_opt_ LPBOOL lpUsedDefaultChar) noexcept -{ - CHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES/sizeof(CHAR)]; - - // Try to convert to stack buffer first. - int cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, szStackBuffer, _countof(szStackBuffer), lpDefaultChar, lpUsedDefaultChar); - if (cch) { - // Copy from stack. - sMultiByteStr.assign(szStackBuffer, szStackBuffer + cch); - } else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - // Query the required output size. Allocate buffer. Then convert again. - cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar); - sMultiByteStr.resize(cch); - cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, sMultiByteStr.data(), cch, lpDefaultChar, lpUsedDefaultChar); - } - - return cch; -} - -/// -/// Maps a UTF-16 (wide character) string to a std::string. The new character string is not necessarily from a multibyte character set. -/// -/// \sa [WideCharToMultiByte function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd374130.aspx) -/// -template -static _Success_(return != 0) int WideCharToMultiByte(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_ std::basic_string sWideCharStr, _Out_ std::basic_string &sMultiByteStr, _In_opt_z_ LPCSTR lpDefaultChar, _Out_opt_ LPBOOL lpUsedDefaultChar) noexcept -{ - CHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES/sizeof(CHAR)]; - - // Try to convert to stack buffer first. - int cch = ::WideCharToMultiByte(CodePage, dwFlags, sWideCharStr.c_str(), (int)sWideCharStr.length(), szStackBuffer, _countof(szStackBuffer), lpDefaultChar, lpUsedDefaultChar); - if (cch) { - // Copy from stack. - sMultiByteStr.assign(szStackBuffer, cch); - } else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - // Query the required output size. Allocate buffer. Then convert again. - cch = ::WideCharToMultiByte(CodePage, dwFlags, sWideCharStr.c_str(), (int)sWideCharStr.length(), NULL, 0, lpDefaultChar, lpUsedDefaultChar); - std::unique_ptr szBuffer(new CHAR[cch]); - cch = ::WideCharToMultiByte(CodePage, dwFlags, sWideCharStr.c_str(), (int)sWideCharStr.length(), szBuffer.get(), cch, lpDefaultChar, lpUsedDefaultChar); - sMultiByteStr.assign(szBuffer.get(), cch); - } - - return cch; -} - -/// -/// Maps a UTF-16 (wide character) string to a std::string. The new character string is not necessarily from a multibyte character set. -/// -/// \note This function cleans all internal buffers using SecureZeroMemory() before returning. -/// -/// \sa [WideCharToMultiByte function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd374130.aspx) -/// -template -static _Success_(return != 0) int SecureWideCharToMultiByte(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cchWideChar) LPCWSTR lpWideCharStr, _In_ int cchWideChar, _Out_ std::basic_string &sMultiByteStr, _In_opt_z_ LPCSTR lpDefaultChar, _Out_opt_ LPBOOL lpUsedDefaultChar) noexcept -{ - CHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES/sizeof(CHAR)]; - - // Try to convert to stack buffer first. - int cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, szStackBuffer, _countof(szStackBuffer), lpDefaultChar, lpUsedDefaultChar); - if (cch) { - // Copy from stack. Be careful not to include zero terminator. - sMultiByteStr.assign(szStackBuffer, cchWideChar != -1 ? strnlen(szStackBuffer, cch) : (size_t)cch - 1); - } else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - // Query the required output size. Allocate buffer. Then convert again. - cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar); - std::unique_ptr szBuffer(new CHAR[cch]); - cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, szBuffer.get(), cch, lpDefaultChar, lpUsedDefaultChar); - sMultiByteStr.assign(szBuffer.get(), cchWideChar != -1 ? strnlen(szBuffer.get(), cch) : (size_t)cch - 1); - SecureZeroMemory(szBuffer.get(), sizeof(CHAR)*cch); - } - - SecureZeroMemory(szStackBuffer, sizeof(szStackBuffer)); - - return cch; -} - -/// -/// Maps a UTF-16 (wide character) string to a std::vector. The new character vector is not necessarily from a multibyte character set. -/// -/// \note This function cleans all internal buffers using SecureZeroMemory() before returning. -/// -/// \sa [WideCharToMultiByte function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd374130.aspx) -/// -template -static _Success_(return != 0) int SecureWideCharToMultiByte(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cchWideChar) LPCWSTR lpWideCharStr, _In_ int cchWideChar, _Out_ std::vector &sMultiByteStr, _In_opt_z_ LPCSTR lpDefaultChar, _Out_opt_ LPBOOL lpUsedDefaultChar) noexcept -{ - CHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES/sizeof(CHAR)]; - - // Try to convert to stack buffer first. - int cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, szStackBuffer, _countof(szStackBuffer), lpDefaultChar, lpUsedDefaultChar); - if (cch) { - // Copy from stack. - sMultiByteStr.assign(szStackBuffer, szStackBuffer + cch); - } else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - // Query the required output size. Allocate buffer. Then convert again. - cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar); - sMultiByteStr.resize(cch); - cch = ::WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, sMultiByteStr.data(), cch, lpDefaultChar, lpUsedDefaultChar); - } - - SecureZeroMemory(szStackBuffer, sizeof(szStackBuffer)); - - return cch; -} - -/// -/// Maps a UTF-16 (wide character) string to a std::string. The new character string is not necessarily from a multibyte character set. -/// -/// \note This function cleans all internal buffers using SecureZeroMemory() before returning. -/// -/// \sa [WideCharToMultiByte function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd374130.aspx) -/// -template -static _Success_(return != 0) int SecureWideCharToMultiByte(_In_ UINT CodePage, _In_ DWORD dwFlags, _Out_ std::basic_string sWideCharStr, _Out_ std::basic_string &sMultiByteStr, _In_opt_z_ LPCSTR lpDefaultChar, _Out_opt_ LPBOOL lpUsedDefaultChar) noexcept -{ - CHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES/sizeof(CHAR)]; - - // Try to convert to stack buffer first. - int cch = ::WideCharToMultiByte(CodePage, dwFlags, sWideCharStr.c_str(), (int)sWideCharStr.length(), szStackBuffer, _countof(szStackBuffer), lpDefaultChar, lpUsedDefaultChar); - if (cch) { - // Copy from stack. - sMultiByteStr.assign(szStackBuffer, cch); - } else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - // Query the required output size. Allocate buffer. Then convert again. - cch = ::WideCharToMultiByte(CodePage, dwFlags, sWideCharStr.c_str(), (int)sWideCharStr.length(), NULL, 0, lpDefaultChar, lpUsedDefaultChar); - std::unique_ptr szBuffer(new CHAR[cch]); - cch = ::WideCharToMultiByte(CodePage, dwFlags, sWideCharStr.c_str(), (int)sWideCharStr.length(), szBuffer.get(), cch, lpDefaultChar, lpUsedDefaultChar); - sMultiByteStr.assign(szBuffer.get(), cch); - SecureZeroMemory(szBuffer.get(), sizeof(CHAR)*cch); - } - - SecureZeroMemory(szStackBuffer, sizeof(szStackBuffer)); - - return cch; -} - -/// -/// Maps a character string to a UTF-16 (wide character) std::wstring. The character string is not necessarily from a multibyte character set. -/// -/// \sa [MultiByteToWideChar function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072.aspx) -/// -template -static _Success_(return != 0) int MultiByteToWideChar(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cbMultiByte) LPCSTR lpMultiByteStr, _In_ int cbMultiByte, _Out_ std::basic_string &sWideCharStr) noexcept -{ - WCHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES/sizeof(WCHAR)]; - - // Try to convert to stack buffer first. - int cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, szStackBuffer, _countof(szStackBuffer)); - if (cch) { - // Copy from stack. - sWideCharStr.assign(szStackBuffer, cbMultiByte != -1 ? wcsnlen(szStackBuffer, cch) : (size_t)cch - 1); - } else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - // Query the required output size. Allocate buffer. Then convert again. - cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0); - std::unique_ptr szBuffer(new WCHAR[cch]); - cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, szBuffer.get(), cch); - sWideCharStr.assign(szBuffer.get(), cbMultiByte != -1 ? wcsnlen(szBuffer.get(), cch) : (size_t)cch - 1); - } - - return cch; -} - -/// -/// Maps a character string to a UTF-16 (wide character) std::vector. The character vector is not necessarily from a multibyte character set. -/// -/// \sa [MultiByteToWideChar function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072.aspx) -/// -template -static _Success_(return != 0) int MultiByteToWideChar(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cbMultiByte) LPCSTR lpMultiByteStr, _In_ int cbMultiByte, _Out_ std::vector &sWideCharStr) noexcept -{ - WCHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES/sizeof(WCHAR)]; - - // Try to convert to stack buffer first. - int cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, szStackBuffer, _countof(szStackBuffer)); - if (cch) { - // Copy from stack. - sWideCharStr.assign(szStackBuffer, szStackBuffer + cch); - } else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - // Query the required output size. Allocate buffer. Then convert again. - cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0); - sWideCharStr.resize(cch); - cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, sWideCharStr.data(), cch); - } - - return cch; -} - -/// -/// Maps a character string to a UTF-16 (wide character) std::wstring. The character string is not necessarily from a multibyte character set. -/// -/// \sa [MultiByteToWideChar function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072.aspx) -/// -template -static _Success_(return != 0) int MultiByteToWideChar(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_ const std::basic_string &sMultiByteStr, _Out_ std::basic_string &sWideCharStr) noexcept -{ - WCHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES/sizeof(WCHAR)]; - - // Try to convert to stack buffer first. - int cch = ::MultiByteToWideChar(CodePage, dwFlags, sMultiByteStr.c_str(), (int)sMultiByteStr.length(), szStackBuffer, _countof(szStackBuffer)); - if (cch) { - // Copy from stack. - sWideCharStr.assign(szStackBuffer, cch); - } else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - // Query the required output size. Allocate buffer. Then convert again. - cch = ::MultiByteToWideChar(CodePage, dwFlags, sMultiByteStr.c_str(), (int)sMultiByteStr.length(), NULL, 0); - std::unique_ptr szBuffer(new WCHAR[cch]); - cch = ::MultiByteToWideChar(CodePage, dwFlags, sMultiByteStr.c_str(), (int)sMultiByteStr.length(), szBuffer.get(), cch); - sWideCharStr.assign(szBuffer.get(), cch); - } - - return cch; -} - -/// -/// Maps a character string to a UTF-16 (wide character) std::wstring. The character string is not necessarily from a multibyte character set. -/// -/// \note This function cleans all internal buffers using SecureZeroMemory() before returning. -/// -/// \sa [MultiByteToWideChar function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072.aspx) -/// -template -static _Success_(return != 0) int SecureMultiByteToWideChar(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cbMultiByte) LPCSTR lpMultiByteStr, _In_ int cbMultiByte, _Out_ std::basic_string &sWideCharStr) noexcept -{ - WCHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES/sizeof(WCHAR)]; - - // Try to convert to stack buffer first. - int cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, szStackBuffer, _countof(szStackBuffer)); - if (cch) { - // Copy from stack. - sWideCharStr.assign(szStackBuffer, cbMultiByte != -1 ? wcsnlen(szStackBuffer, cch) : (size_t)cch - 1); - } else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - // Query the required output size. Allocate buffer. Then convert again. - cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0); - std::unique_ptr szBuffer(new WCHAR[cch]); - cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, szBuffer.get(), cch); - sWideCharStr.assign(szBuffer.get(), cbMultiByte != -1 ? wcsnlen(szBuffer.get(), cch) : (size_t)cch - 1); - SecureZeroMemory(szBuffer.get(), sizeof(WCHAR)*cch); - } - - SecureZeroMemory(szStackBuffer, sizeof(szStackBuffer)); - - return cch; -} - -/// -/// Maps a character string to a UTF-16 (wide character) std::vector. The character vector is not necessarily from a multibyte character set. -/// -/// \note This function cleans all internal buffers using SecureZeroMemory() before returning. -/// -/// \sa [MultiByteToWideChar function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072.aspx) -/// -template -static _Success_(return != 0) int SecureMultiByteToWideChar(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_z_count_(cbMultiByte) LPCSTR lpMultiByteStr, _In_ int cbMultiByte, _Out_ std::vector &sWideCharStr) noexcept -{ - WCHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES/sizeof(WCHAR)]; - - // Try to convert to stack buffer first. - int cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, szStackBuffer, _countof(szStackBuffer)); - if (cch) { - // Copy from stack. - sWideCharStr.assign(szStackBuffer, szStackBuffer + cch); - } else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - // Query the required output size. Allocate buffer. Then convert again. - cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0); - sWideCharStr.resize(cch); - cch = ::MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, sWideCharStr.data(), cch); - } - - SecureZeroMemory(szStackBuffer, sizeof(szStackBuffer)); - - return cch; -} - -/// -/// Maps a character string to a UTF-16 (wide character) std::wstring. The character string is not necessarily from a multibyte character set. -/// -/// \note This function cleans all internal buffers using SecureZeroMemory() before returning. -/// -/// \sa [MultiByteToWideChar function](https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072.aspx) -/// -template -static _Success_(return != 0) int SecureMultiByteToWideChar(_In_ UINT CodePage, _In_ DWORD dwFlags, _In_ const std::basic_string &sMultiByteStr, _Out_ std::basic_string &sWideCharStr) noexcept -{ - WCHAR szStackBuffer[WINSTD_STACK_BUFFER_BYTES/sizeof(WCHAR)]; - - // Try to convert to stack buffer first. - int cch = ::MultiByteToWideChar(CodePage, dwFlags, sMultiByteStr.c_str(), (int)sMultiByteStr.length(), szStackBuffer, _countof(szStackBuffer)); - if (cch) { - // Copy from stack. - sWideCharStr.assign(szStackBuffer, cch); - } else if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - // Query the required output size. Allocate buffer. Then convert again. - cch = ::MultiByteToWideChar(CodePage, dwFlags, sMultiByteStr.c_str(), (int)sMultiByteStr.length(), NULL, 0); - std::unique_ptr szBuffer(new WCHAR[cch]); - cch = ::MultiByteToWideChar(CodePage, dwFlags, sMultiByteStr.c_str(), (int)sMultiByteStr.length(), szBuffer.get(), cch); - sWideCharStr.assign(szBuffer.get(), cch); - SecureZeroMemory(szBuffer.get(), sizeof(WCHAR)*cch); - } - - SecureZeroMemory(szStackBuffer, sizeof(szStackBuffer)); - - return cch; -} - /// /// Normalizes characters of a text string according to Unicode 4.0 TR#15. /// diff --git a/include/WinStd/WinSock2.h b/include/WinStd/WinSock2.h index 2fe879d9..bc7a34ea 100644 --- a/include/WinStd/WinSock2.h +++ b/include/WinStd/WinSock2.h @@ -28,11 +28,9 @@ namespace winstd /// Constructs an exception /// /// \param[in] num WinSock2 error code - /// \param[in] msg Error message /// - ws2_runtime_error(_In_ error_type num, _In_ const std::string& msg) : num_runtime_error(num, msg) - { - } + ws2_runtime_error(_In_ error_type num) : num_runtime_error(num, message(num)) + {} /// /// Constructs an exception @@ -40,41 +38,57 @@ namespace winstd /// \param[in] num WinSock2 error code /// \param[in] msg Error message /// - ws2_runtime_error(_In_ error_type num, _In_opt_z_ const char *msg = nullptr) : num_runtime_error(num, msg) - { - } + ws2_runtime_error(_In_ error_type num, _In_ const std::string& msg) : num_runtime_error(num, msg + ": " + message(num)) + {} + + /// + /// Constructs an exception + /// + /// \param[in] num WinSock2 error code + /// \param[in] msg Error message + /// + ws2_runtime_error(_In_ error_type num, _In_z_ const char *msg) : num_runtime_error(num, std::string(msg) + ": " + message(num)) + {} + + /// + /// Constructs an exception using `WSAGetLastError()` + /// + ws2_runtime_error() : num_runtime_error(WSAGetLastError(), message(WSAGetLastError())) + {} /// /// Constructs an exception using `WSAGetLastError()` /// /// \param[in] msg Error message /// - ws2_runtime_error(_In_ const std::string& msg) : num_runtime_error(WSAGetLastError(), msg) - { - } + ws2_runtime_error(_In_ const std::string& msg) : num_runtime_error(WSAGetLastError(), msg + ": " + message(WSAGetLastError())) + {} /// /// Constructs an exception using `WSAGetLastError()` /// /// \param[in] msg Error message /// - ws2_runtime_error(_In_opt_z_ const char *msg = nullptr) : num_runtime_error(WSAGetLastError(), msg) - { - } + ws2_runtime_error(_In_z_ const char *msg) : num_runtime_error(WSAGetLastError(), std::string(msg) + ": " + message(WSAGetLastError())) + {} + protected: /// /// Returns a user-readable Windows error message /// /// \sa [FormatMessage function](https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-formatmessage) /// - tstring msg(_In_opt_ DWORD dwLanguageId = 0) const + static std::string message(_In_ error_type num, _In_opt_ DWORD dwLanguageId = 0) { - tstring str; - if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, m_num, dwLanguageId, str, NULL)) { + std::wstring wstr; + if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, num, dwLanguageId, wstr, NULL)) { // Stock Windows error messages contain CRLF. Well... Trim all the trailing white space. - str.erase(str.find_last_not_of(_T(" \t\n\r\f\v")) + 1); - } else - sprintf(str, m_num >= 0x10000 ? _T("Error 0x%X") : _T("Error %u"), m_num); + wstr.erase(wstr.find_last_not_of(L" \t\n\r\f\v") + 1); + } + else + sprintf(wstr, num >= 0x10000 ? L"Error 0x%X" : L"Error %u", num); + std::string str; + WideCharToMultiByte(CP_UTF8, 0, wstr, str, NULL, NULL); return str; } };