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字面意思是否釋放該記憶體;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5SN1UGOzgDM5E2YhljMkNTYjhDOyQmN0EGM3I2MmFzYx8CX2AzLcBTMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLzM3Lc9CX6MHc0RHaiojIsJye.png)
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";
效果: