微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么要使用`SEH`以及如何正确使用它?

如何解决为什么要使用`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 举报,一经查实,本站将立刻删除。