#pragma once
// Copyright (c) 2011, Alf P. Steinbach
#ifdef _MSC_VER
# pragma warning( disable: 4373 ) // "hey, previous versions of compiler had more bugs"
# pragma warning( disable: 4100 ) // Unreferenced formal parameter
#endif
//-------------------------------------------------- Dependencies:
#include <progrock/compiler_specific/fixups.h> // CPPX_NOEXCEPT
#include <progrock/cppx/error_handling.h> // cppx::throwX etc.
#include <progrock/cppx/string_building.h> // S
#include <progrock/winapi/wrapper/windows_h.h>
#include <system_error>
//-------------------------------------------------- Interface:
#ifndef CPPX_NOEXCEPT
# define CPPX_NOEXCEPT noexcept
#endif
namespace progrock{ namespace winapi{
using std::string;
typedef DWORD ErrorCode;
typedef cppx::narrow::S NarrowS;
typedef unsigned char UChar;
namespace errorMessage {
inline string fromApi( ErrorCode const code )
{
struct RawMessage
{
char const* p;
RawMessage(): p(0) {}
~RawMessage() { ::LocalFree( const_cast<char*>( p ) ); }
};
RawMessage message;
int const stringLength = FormatMessageA(
0 | FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS,
0, // lpSource
code,
MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ),
reinterpret_cast<char*>( &message.p ),
0, // nSize
0
);
// Just in case message.p isn't set:
if( stringLength == 0 ) { return ""; }
// Remove trailing CR and LF control chars:
char const* pEnd = message.p + stringLength;
for( ;; )
{
if( pEnd == message.p ) { break; }
if( static_cast<UChar>( *(pEnd - 1) ) >= ' ' ) { break; }
--pEnd;
}
return string( message.p, pEnd );
}
inline string withCode( ErrorCode const code, string const& text )
{
return NarrowS() << text << " (code " << code << ")";
}
inline string forUnknown( ErrorCode const code )
{
return errorMessage::withCode( code, "Unknown error" );
}
inline string explaining( ErrorCode const code )
{
if( code == 0 )
{
// ERROR_SUCCESS.
// Instead of "The operation completed successfully.":
return errorMessage::forUnknown( code );
}
string const rawMessage = fromApi( code );
if( rawMessage.length() == 0 )
{
return errorMessage::forUnknown( code );
}
return errorMessage::withCode( code, rawMessage );
}
} // namespace errorMessage
class ErrorCategory
: public std::error_category
{
public:
virtual const char* name() const CPPX_NOEXCEPT
{
return "WinApiError";
}
virtual std::error_condition default_error_condition( int const code ) const CPPX_NOEXCEPT
{
return std::error_condition( code, *this );
}
virtual string message( int const code ) const
{
return errorMessage::explaining( code );
}
};
inline std::error_category const& errorCategory()
{
static ErrorCategory const theInstance;
return theInstance;
}
class Error
: public std::system_error
{
private:
mutable std::string fullDescription_;
public:
char const* what() const CPPX_NOEXCEPT
{
if( fullDescription_.length() == 0 )
{
fullDescription_ = NarrowS()
<< std::system_error::what() << ": " << code().message()
<< NarrowS::toString;
}
return fullDescription_.c_str();
}
Error( int const code, std::string const& description )
: std::system_error(
std::error_code( code, ErrorCategory() ),
description
)
{}
virtual ~Error() CPPX_NOEXCEPT {}
};
inline HRESULT hResultFrom( std::system_error const& x )
{
std::error_code const& error = x.code();
if( error.category() != winapi::errorCategory() )
{
return E_FAIL;
}
return HRESULT_FROM_WIN32( error.value() ); // Deals OK with HRESULT.
}
inline HRESULT hResultFromX()
{
try { throw; }
catch( std::system_error const& x ) { return hResultFrom( x ); }
catch( ... ) {}
return E_FAIL;
}
inline bool throwX( ErrorCode const errorCode, string const& s )
{
throw Error( errorCode, s );
}
} } // namespace progrock::winapi