如何解决为什么要使用`SEH`以及如何正确使用它?
我正在从 Michael Dunn 撰写的旧的但仍然出色的“The Complete Idiot's Guide”系列文章中学习 Shell 扩展。 second part 描述了允许通过上下文菜单注册和取消注册 COM Servers
的扩展。有一个 CProgressDlg
类负责遍历实现所需过程的 dll 列表,尝试加载它们,调用所需过程,将操作结果保存为稍后显示给用户的消息。以下是代码摘录:
typedef std::list< std::basic_string<TCHAR> > string_list;
class CProgressDlg : public CDialogImpl<CProgressDlg>
{
protected:
bool m_bStopSign;
HWND m_hwndList;
const string_list* m_pFileList;
string_list m_lsstatusMessages; // list of status messages for the dialog
const CMINVOKECOMMANDINFO* m_pCmdInfo;
void DoWork();
}
void CProgressDlg::DoWork()
{
USES_CONVERSION;
HRESULT(STDAPICALLTYPE * pfn)();
HINSTANCE hinst;
TCHAR szMsg[512];
LPCSTR pszFnName;
WORD wCmd;
LVITEM rItem;
int nIndex = 0;
bool bSuccess;
// work area - this used to be outside DoWork() so we Could use try/finally in that function.
std::basic_string<TCHAR> strMsg;
wCmd = LOWORD(m_pCmdInfo->lpVerb);
// We only support 2 commands,so check the value passed in lpVerb.
if (0 != wCmd && 1 != wCmd)
{
ATLASSERT(0); // should never get here
return;
}
// Determine which function we'll be calling. Note that these strings are
// not enclosed in the _T macro,since GetProcAddress() only takes an
// ANSI string for the function name.
pszFnName = wCmd ? "DllUnregisterServer" : "DllRegisterServer";
for (string_list::const_iterator it = m_pFileList->begin();
it != m_pFileList->end(); it++)
{
bSuccess = false;
hinst = NULL;
*szMsg = '\0';
// We will print a status message into szMsg,which will eventually
// be stored in the LParaM of a listview control item.
__try
{
// Try to load the next file.
hinst = LoadLibraryEx(it->c_str(),NULL,LOAD_WITH_ALTERED_SEARCH_PATH);
// If it Failed,construct a friendly error message.
if (NULL == hinst)
{
void* pvMsgBuf = NULL;
DWORD dwLastErr = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYstem |
FORMAT_MESSAGE_IGnorE_INSERTS,dwLastErr,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&pvMsgBuf,NULL);
wsprintf(szMsg,_T("LoadLibraryEx Failed on this module.\nError 0x%08lX (%s)"),pvMsgBuf ? pvMsgBuf : _T("No description available"));
LocalFree(pvMsgBuf);
continue;
}
// Get the address of the register/unregister function.
(FARPROC&)pfn = GetProcAddress(hinst,pszFnName);
// If it wasn't found,construct an error message.
if (NULL == pfn)
{
wsprintf(szMsg,_T("%hs not found in this module."),pszFnName);
continue;
}
// Call the function!
HRESULT hr = pfn();
// Construct a message listing the result of the function call.
if (SUCCEEDED(hr))
{
bSuccess = true;
wsprintf(szMsg,_T("%hs succeeded on %s"),pszFnName,it->c_str());
}
else
{
void* pvMsgBuf = NULL;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYstem |
FORMAT_MESSAGE_IGnorE_INSERTS,hr,_T("%hs Failed on %s\nError 0x%08lX: %s"),it->c_str(),(DWORD)hr,NULL != pvMsgBuf ? pvMsgBuf : _T("(No description available)"));
LocalFree(pvMsgBuf);
}
} // end __try
__finally
{
// Fill in the LVITEM struct. The item text will be the filename,// and the LParaM will point to a copy of the szMsg status message.
strMsg = szMsg;
m_lsstatusMessages.push_back(strMsg);
ZeroMemory(&rItem,sizeof(LVITEM));
rItem.mask = LVIF_ParaM | LVIF_TEXT;
rItem.iItem = nIndex;
rItem.pszText = const_cast<LPTSTR>(it->c_str());
rItem.lParam = (LParaM)(DWORD_PTR)m_lsstatusMessages.back().c_str();
ListView_InsertItem(m_hwndList,&rItem);
// Set the text in column 2 to "succeeded" or "Failed" depending
// on the outcome of the function call.
ListView_SetItemText(m_hwndList,nIndex++,1,bSuccess ? _T("Succeeded") : _T("Failed"));
if (NULL != hinst)
FreeLibrary(hinst);
}
// Process messages in our queue - this is much easier than doing it
// "the real way" with a worker thread. This is how we detect a click
// on the Stop button.
MSG msg;
while (PeekMessage(&msg,m_hWnd,PM_REMOVE))
{
TranslateMessage(&msg);
dispatchMessage(&msg);
}
// Resize the list columns.
ListView_SetColumnWidth(m_hwndList,LVSCW_AUTOSIZE_USEHEADER);
ListView_SetColumnWidth(m_hwndList,LVSCW_AUTOSIZE_USEHEADER);
} // end for
}
我想了解他最初为什么决定使用 SEH
,为什么他不能使用常规的 try/catch
?他甚至将 strMsg
从方法中移到了全局空间,据我所知,最好避免使用全局空间。但是仍然只能使用异常处理 /EHa
选项编译项目,我可以推测这是因为 it
循环中使用的 for
变量,但我不是确定我的假设是正确的,为什么需要解构列表迭代变量,它不是一种指向当前列表项的指针吗?
为了以可接受的方式完成工作,如何重写此函数?我已经读过 RAII
包装器,为了获得使用 SEH
的函数以及需要展开编译的对象而不改变上述异常处理选项,这两个函数必须分离到不同的函数中,但是我只是无法理解所有这些,非常感谢您的帮助。
更新
为了使对象能够展开,我将 SEH
异常处理从循环中转移到不同的方法中。
void CProgressDlg::ProcessLib(LPCWSTR lpLibFileName,LPCSTR pszFnName)
{
bool bSuccess;
HINSTANCE hinst;
TCHAR szMsg[512];
HRESULT(STDAPICALLTYPE * pfn)();
LVITEM rItem;
int nIndex = 0;
bSuccess = false;
hinst = NULL;
*szMsg = '\0';
// We will print a status message into szMsg,which will eventually
// be stored in the LParaM of a listview control item.
__try
{
// Try to load the next file.
hinst = LoadLibraryEx(lpLibFileName,LOAD_WITH_ALTERED_SEARCH_PATH);
// If it Failed,construct a friendly error message.
if (NULL == hinst)
{
void* pvMsgBuf = NULL;
DWORD dwLastErr = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYstem |
FORMAT_MESSAGE_IGnorE_INSERTS,NULL);
wsprintf(szMsg,pvMsgBuf ? pvMsgBuf : _T("No description available"));
LocalFree(pvMsgBuf);
return;
}
// Get the address of the register/unregister function.
(FARPROC&)pfn = GetProcAddress(hinst,pszFnName);
// If it wasn't found,construct an error message.
if (NULL == pfn)
{
wsprintf(szMsg,pszFnName);
return;
}
// Call the function!
HRESULT hr = pfn();
// Construct a message listing the result of the function call.
if (SUCCEEDED(hr))
{
bSuccess = true;
wsprintf(szMsg,lpLibFileName);
}
else
{
void* pvMsgBuf = NULL;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYstem |
FORMAT_MESSAGE_IGnorE_INSERTS,lpLibFileName,NULL != pvMsgBuf ? pvMsgBuf : _T("(No description available)"));
LocalFree(pvMsgBuf);
}
} // end __try
__finally
{
// Fill in the LVITEM struct. The item text will be the filename,// and the LParaM will point to a copy of the szMsg status message.
//strMsg = szMsg;
m_lsstatusMessages.push_back(szMsg); // <-- This line gives C2712 Cannot use __try in functions that require object unwinding
ZeroMemory(&rItem,sizeof(LVITEM));
rItem.mask = LVIF_ParaM | LVIF_TEXT;
rItem.iItem = nIndex;
rItem.pszText = const_cast<LPTSTR>(lpLibFileName);
rItem.lParam = (LParaM)(DWORD_PTR)m_lsstatusMessages.back().c_str();
ListView_InsertItem(m_hwndList,&rItem);
// Set the text in column 2 to "succeeded" or "Failed" depending
// on the outcome of the function call.
ListView_SetItemText(m_hwndList,bSuccess ? _T("Succeeded") : _T("Failed"));
if (NULL != hinst)
FreeLibrary(hinst);
}
}
但是现在字符串列表的 push_back
是类的一部分,它给了我 C2712
错误。为什么修改存在于需要对象展开的函数范围之外的对象会出现此错误?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。