将 GetLastError() 转换为异常
我有一个 Visual Studio 2008 C++ 项目,在出现异常错误时使用 Win32Exception
类。 Win32Exception
类如下所示:
/// defines an exception based on Win32 error codes. The what() function will
/// return a formatted string returned from FormatMessage()
class Win32Exception : public std::runtime_error
{
public:
Win32Exception() : std::runtime_error( ErrorMessage( &error_code_ ) )
{
};
virtual ~Win32Exception() { };
/// return the actual error code
DWORD ErrorCode() const throw() { return error_code_; };
private:
static std::string ErrorMessage( DWORD* error_code )
{
*error_code = ::GetLastError();
std::string error_messageA;
wchar_t* error_messageW = NULL;
DWORD len = ::FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
*error_code,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
reinterpret_cast< LPWSTR >( &error_messageW ),
0,
NULL );
if( NULL != error_messageW )
{
// this may generate a C4244 warning. It is safe to ignore.
std::copy( error_messageW,
error_messageW + len,
std::back_inserter( error_messageA ) );
::LocalFree( error_messageW );
}
return error_messageA;
};
/// error code returned by GetLastError()
DWORD error_code_;
}; // class Win32Exception
该类在它所使用的情况下运行良好。我想知道是否有任何明显的情况会失败,我应该注意。欢迎任何其他问题、警告或一般性改进建议。
请注意,boost 库不是此代码的选项。
I have a Visual Studio 2008 C++ project that uses a Win32Exception
class in cases where there is an exceptional error. The Win32Exception
class looks like this:
/// defines an exception based on Win32 error codes. The what() function will
/// return a formatted string returned from FormatMessage()
class Win32Exception : public std::runtime_error
{
public:
Win32Exception() : std::runtime_error( ErrorMessage( &error_code_ ) )
{
};
virtual ~Win32Exception() { };
/// return the actual error code
DWORD ErrorCode() const throw() { return error_code_; };
private:
static std::string ErrorMessage( DWORD* error_code )
{
*error_code = ::GetLastError();
std::string error_messageA;
wchar_t* error_messageW = NULL;
DWORD len = ::FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
*error_code,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
reinterpret_cast< LPWSTR >( &error_messageW ),
0,
NULL );
if( NULL != error_messageW )
{
// this may generate a C4244 warning. It is safe to ignore.
std::copy( error_messageW,
error_messageW + len,
std::back_inserter( error_messageA ) );
::LocalFree( error_messageW );
}
return error_messageA;
};
/// error code returned by GetLastError()
DWORD error_code_;
}; // class Win32Exception
The class works well in the situations it has been used in. What I would like to know is if there are any obvious cases where this will fail that I should be aware of. Any other gotchas, caveats, or general suggestions on improvements are welcome.
Please note that the boost library is not an option for this code.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
讽刺的是,您的代码不是异常安全的。
请注意,如果
back_inserter
导致抛出std::bad_alloc
,则FormatMessage
内分配的内存将被泄漏。Ironically, your code is not exception safe.
Note that if the
back_inserter
causesstd::bad_alloc
to be thrown, the memory allocated insideFormatMessage
is leaked.真是巧合啊!我在所有项目中都使用类似的代码!这实际上是一个好主意。
这段代码有问题:
它只是将 WCHAR 转换为字符。您可以显式使用 FormatMessageA 来获取当前代码页中的消息(好吧,您不能像您所说的那样),或者约定所有字符串均采用 UTF-8 编码。我选择了后者,请参阅这个原因。
错误消息本身可能没有用。捕获堆栈跟踪可能是个好主意。
What a coincidence! I use a similar code in all my projects! It is actually a good idea.
This code is problematic:
It just trancates WCHARs to chars. Your can either use FormatMessageA explicitly to get a message in the current code-page (ok, you can't as you said), or make convention that all your stings are UTF-8 encoded. I chose the later, see this why.
Error message by itself may be not useful. Capturing the stack trace may be a good idea.
意识到这已经过时了,但至少在 VC++ 2015 中,您可以抛出一个
system_error
,它将使用system_category()
函数完成所有这些操作:这将打印:“无法写入文件:访问被拒绝”
Realize this is old, but at least with VC++ 2015 you can throw a
system_error
that will do all this with thesystem_category()
function:This would print: "Failed to write file: Access is denied"
FormatMessage
本身可能会失败。对于这种情况,可能需要一些中性的“代码为 %d 的未知错误”。SHFileOperation
),您必须单独处理它们。如果您希望处理它们,那就是。FormatMessage
may itself fail. Some neutral "Unknown error with code %d" might be in order for such case.SHFileOperation
) that you must handle separately. If you want them to be handled, that is.我在此类消息检索方面遇到的主要问题是
ERROR_SUCCESS
。当某些操作失败并伴有“操作成功”的错误消息时,这是相当令人困惑的。人们不会认为这种情况会发生,但它确实发生了。我想这是辩证法所指出的一个特例,即“某些错误代码并不是真正的错误”,但对于大多数代码来说,至少该消息通常是可以接受的。
第二个问题是大多数Windows系统错误信息末尾都有回车+换行。将消息插入其他文本是有问题的,并且它打破了 C++ 异常消息的约定。所以,删除这些字符是个好主意。
现在,不再重复其他人已经注意到的所有内容,而是介绍一下设计。
如果将 ErrorMessage 函数公开或移出类,并按值获取错误代码,而不是获取指针参数,则该函数会更有用。这就是职责分开的原则。促进重用。
如果使用析构函数来释放内存,
ErrorMessage
中的代码会更加清晰、安全和高效。然后,您也可以直接在 return 语句中构造字符串,而不是使用带有后插入器的复制循环。干杯&呵呵,
The main I've problem I've had with such message retrieval has been
ERROR_SUCCESS
. It's rather perplexing when some operation fails, accompanied by error message "The operation succeeded". One wouldn't think that could happen, but it does.I guess this is a special case of what Dialecticus noted, that "Some error codes are not really errors", but for most of those codes at least the message is generally acceptable.
The second problem is that most Windows system error message have a carriage return + linefeed at the end. It's problematic for insertion of messages into other text, and it breaks the convention for C++ exception messages. So, good idea to remove those chars.
Now, instead of repeating all that others have already noted, a few words about the design.
The
ErrorMessage
function would much more usable if was made public or moved out of the class, and took the error code by value, instead of taking pointer argument. This is the principle of keeping separate responsibilities separate. Promotes reuse.The code in
ErrorMessage
would be more clear and safe and efficient if you used a destructor to deallocate the memory. Then you could also just construct the string directly in thereturn
statement instead of using a copy loop with back inserter.Cheers & hth.,
我最近正在研究一个非常相似的类,在阅读此线程后尝试使复制部分异常安全。我引入了一个小帮助器类,它除了保存指向
::FormatMessage
返回的字符串的指针并在其析构函数中使用::LocalFree
释放它之外什么也不做。不允许复制、分配和移动,因此不会惹上麻烦。以下是我的总体想法:
也许这对你们中的一些人有用。
I was recently working on a very similar class and after reading this thread tried to make the copying part exception-safe. I introduced a little helper class that does nothing but hold the pointer to the string returned by
::FormatMessage
and free it with::LocalFree
in its destructor. Copying, assigning and moving is not allowed, so one cannot get into trouble.Here is what I came up with in total:
Maybe this will be useful for some of you.