C++异常处理
C++异常处理
一.关键Api介绍
1.HRESULT
H result,错误代码id;
2.FormatMessage
格式化消息字符串;
DWORD FormatMessage(
DWORD dwFlags, //格式化选项
LPCVOID lpSource, //消息定义的位置
DWORD dwMessageId, //消息标识符HRESULT
DWORD dwLanguageId, //语言标识符
LPTSTR lpBuffer, //指向缓冲区的指针,这个是接受格式化后的字符串
DWORD nSize,
va_list *Arguments
);
前面几个都有点用,最后两个好像没啥用;具体看MSDN官方文档;
这个Api将错误id转化为了可读的字符串;
3.exception
windows系统runtime中的自带了exception类;
除了构造,拷贝构造,同类赋值,析构四套件外,还有一个公有的what方法和私有字段_Data结构体;
以下是__std_exception_data结构体;What是异常常量字符串;DoFree字面意思是否释放该内存;
what方法中,判断data中的what字符串为空,返回未知异常,否则返回what;
二.封装exception
最终目的是为了把异常打印出来,需要知道异常的具体位置,异常种类或消息;
GetOriginString :按格式打印错误种类;
其他都是简单代码;
//.h
class ChiliException : public std::exception
{
public:
ChiliException( int line,const char* file ) noexcept;
const char* what() const noexcept override;
virtual const char* GetType() const noexcept;
int GetLine() const noexcept;
const std::string& GetFile() const noexcept;
std::string GetOriginString() const noexcept;
private:
int line;
std::string file;
protected:
mutable std::string whatBuffer;
};
//Cpp
ChiliException::ChiliException( int line,const char* file ) noexcept
:
line( line ),
file( file )
{}
const char* ChiliException::what() const noexcept
{
std::ostringstream oss;
oss << GetType() << std::endl
<< GetOriginString();
whatBuffer = oss.str();
return whatBuffer.c_str();
}
const char* ChiliException::GetType() const noexcept
{
return "Chili Exception";
}
int ChiliException::GetLine() const noexcept
{
return line;
}
const std::string& ChiliException::GetFile() const noexcept
{
return file;
}
std::string ChiliException::GetOriginString() const noexcept
{
std::ostringstream oss;
oss << "[File] " << file << std::endl
<< "[Line] " << line;
return oss.str();
}
三.封装HRESULT异常类
在window类中添加一下两个异常类,对HRESULT格式化,将错误代码转成可读字符串返回出来;
//.h
class Exception : public ChiliException
{
using ChiliException::ChiliException;
public:
static std::string TranslateErrorCode(HRESULT hr) noexcept;
};
class HrException : public Exception
{
public:
HrException(int line, const char* file, HRESULT hr) noexcept;
const char* what() const noexcept override;
const char* GetType() const noexcept override;
HRESULT GetErrorCode() const noexcept;
std::string GetErrorDescription() const noexcept;
private:
HRESULT hr;
};
为了方便调用添加了两个宏定义,在需要抛出异常时,只需要写宏定义即可:
#define CHWND_EXCEPT( hr ) Window::HrException( __LINE__,__FILE__,(hr) )
#define CHWND_LAST_EXCEPT() Window::HrException( __LINE__,__FILE__,GetLastError() )
GetLastError :Win32Api,获取最后一个错误id,用来监控程序崩溃的异常;
//.cpp
std::string Window::Exception::TranslateErrorCode(HRESULT hr) noexcept
{
char* pMsgBuf = nullptr;
// windows will allocate memory for err string and make our pointer point to it
//格式化消息字符串
const DWORD nMsgLen = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPSTR>(&pMsgBuf), 0, nullptr
);
// 0 string length returned indicates a failure
if (nMsgLen == 0)
{
return "Unidentified error code";
}
// copy error string from windows-allocated buffer to std::string
std::string errorString = pMsgBuf;
// free windows buffer
LocalFree(pMsgBuf);
return errorString;
}
Window::HrException::HrException(int line, const char* file, HRESULT hr) noexcept
:
Exception(line, file),
hr(hr)
{}
const char* Window::HrException::what() const noexcept
{
std::ostringstream oss;
oss << GetType() << std::endl
<< "[Error Code] 0x" << std::hex << std::uppercase << GetErrorCode()
<< std::dec << " (" << (unsigned long)GetErrorCode() << ")" << std::endl
<< "[Description] " << GetErrorDescription() << std::endl
<< GetOriginString();
whatBuffer = oss.str();
return whatBuffer.c_str();
}
const char* Window::HrException::GetType() const noexcept
{
return "Chili Window Exception";
}
HRESULT Window::HrException::GetErrorCode() const noexcept
{
return hr;
}
std::string Window::HrException::GetErrorDescription() const noexcept
{
return Exception::TranslateErrorCode(hr);
}
四.捕获异常
在WinMain中添加以下代码,捕获三种异常(系统异常,运行时,未知);
MessageBox是Win32Api,弹出错误框;
try{
//程序运行代码
}
catch (const ChiliException& e)
{
MessageBox(nullptr, e.what(), e.GetType(), MB_OK | MB_ICONEXCLAMATION);
}
catch (const std::exception& e)
{
MessageBox(nullptr, e.what(), "Standard Exception", MB_OK | MB_ICONEXCLAMATION);
}
catch (...)
{
MessageBox(nullptr, "No details available", "Unknown Exception", MB_OK | MB_ICONEXCLAMATION);
}
测试代码:
//test报错机制
//throw CHWND_EXCEPT( ERROR_ABIOS_ERROR);
//throw std::runtime_error("xxxxxxx");
//throw "x";
效果:
另外,通过修改一些参数再次测试正常报错机制(修改了创建窗口的类名);
Life is too short for so much sorrow.
本博客所有文章除特别声明外,均采用
CC BY-NC-SA 4.0 许可协议。转载请注明来自 小紫苏!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)