愿愿的vc++最佳异常处理机制心得。

vc++最令人头疼的莫过于异常处理,与内存泄漏。今天愿愿先提出一个异常处理的方案。以供后来者,学习参考之用。


以下是代码,如果不详细,请谅解,我只能保证,我所贴出的代码是经过验证的,是正确的,至于能不能被大家理解,请谅解。

标红色的,是需要注意的。

_cMyExceptionImpl.h   文件内容如下:

#pragma once
#include "_cSetLogFile2.h"
#include "MessageDialog.h"

//#include <boost/exception/info.hpp>
#include <boost/exception/exception.hpp>
#include <boost/throw_exception.hpp>


class _cMyExceptionImpl: //Base for all exception objects we throw.
    public virtual std::exception,
    public virtual boost::exception
{
public:
    
    static void SetLogFile(const TCHAR * m_szLogFileName) throw();
    _cMyExceptionImpl(CString & error_info,CWnd* parentWnd) throw();
    _cMyExceptionImpl(const TCHAR * error_tchar,CWnd* parentWnd) throw();
    _cMyExceptionImpl(const _cMyExceptionImpl& myExp,CWnd* parentWnd) throw();    // 拷贝构造函数
    char const *what() const throw();
    virtual ~_cMyExceptionImpl();
private:
    CWnd* m_parentWnd;
    char const * PrintErrorInfo()const throw();
    static _cSetLogFile2 m_setLogFileVar;
};
std::shared_ptr<TCHAR> char_To_TCHAR(const char *chr);
std::shared_ptr<char> __cdecl TCHAR_To_Char(const TCHAR * errorBuffer);

_cMyExceptionImpl.h完

_cMyExceptionImpl.cpp 内容如下:

#include "StdAfx.h"
#include "_cMyExceptionImpl.h"
//#include "boost\scoped_array.hpp"
//#include <boost/exception/all.hpp>

#include <boost/exception/info.hpp>
#include <boost/exception/get_error_info.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
typedef boost::error_info<struct tag_error_Date,boost::posix_time::ptime> errinfo_time;
typedef boost::error_info<struct tag_error_strSharedChars,std::shared_ptr<char>> errinfo_charStr;
//using namespace boost;
#include  <sstream>
std::shared_ptr<TCHAR> char_To_TCHAR(const char *chr)   
{      
    int i=strlen(chr);
    DWORD dwLen = MultiByteToWideChar( CP_ACP, 0, chr, -1,NULL, 0);  
    if(dwLen==0)
    {
        return NULL;
    }
    std::shared_ptr<TCHAR> ptr(new TCHAR[dwLen], std::default_delete<TCHAR[]>());
    //std::shared_ptr<TCHAR[]> ptr(new TCHAR[dwLen]);
#ifdef UNICODE
    if(MultiByteToWideChar( CP_ACP, 0, chr, -1,ptr.get(), dwLen)==0)
    {
        return NULL;
    }
#else
    strcpy_s((char *)ptr.get(),dwLen,chr);
#endif
    return ptr;
}   
std::shared_ptr<char> __cdecl TCHAR_To_Char(const TCHAR * errorBuffer)
{    
    DWORD dwLen = WideCharToMultiByte(CP_ACP, NULL, errorBuffer, -1, NULL, NULL, NULL, FALSE );
    if(dwLen==0)
    {
        return NULL;
    }
    //std::shared_ptr<char[]> ptr(new char[dwLen]);
    std::shared_ptr<char> ptr(new char[dwLen], std::default_delete<char[]>());
#ifdef UNICODE
    if(WideCharToMultiByte(CP_ACP, NULL,
        errorBuffer, -1, ptr.get(), dwLen, NULL, FALSE )==0)
    {//WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,lpszStr,dwSize,NULL,FALSE);
        return NULL;
    }
#else
    strcpy_s(ptr.get(),dwLen,(const char *)errorBuffer);
#endif
    return ptr;
}
_cSetLogFile2 _cMyExceptionImpl::m_setLogFileVar;
//static int countkd=0;
//static int countkd2=0;
char const *_cMyExceptionImpl::what()const
{
    return PrintErrorInfo();
};
_cMyExceptionImpl::_cMyExceptionImpl(const _cMyExceptionImpl& myExp,CWnd* parentWnd)
    :boost::exception(((boost::exception &)myExp))
    ,std::exception((std::exception &)myExp),m_parentWnd(parentWnd)
{
}
_cMyExceptionImpl::_cMyExceptionImpl(CString & error_info,CWnd* parentWnd)
    :m_parentWnd(parentWnd)
{
    //TRACE(_T("construct:%d\n"),countkd++);
    *this<<errinfo_charStr(TCHAR_To_Char(error_info.GetBuffer()));
    error_info.ReleaseBuffer();
}
_cMyExceptionImpl::_cMyExceptionImpl(const TCHAR * error_tchar,CWnd* parentWnd)
    :m_parentWnd(parentWnd)
{
    *this<<errinfo_charStr(TCHAR_To_Char(error_tchar));
}
_cMyExceptionImpl::~_cMyExceptionImpl()
{
    //TRACE(_T("destruct:%d\n"),countkd2++);
}
void _cMyExceptionImpl::SetLogFile(const TCHAR * m_szLogFileName)
{
    _cSetLogFile2::SetLogFileName((TCHAR *)m_szLogFileName);
}
char const * _cMyExceptionImpl::PrintErrorInfo()const
{  
    boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();  
       char const * const * function=boost::get_error_info<boost::throw_function>(*this);
        char const * const * file=boost::get_error_info<boost::throw_file>(*this);
        int const * line=boost::get_error_info<boost::throw_line>(*this);
        const std::shared_ptr<char> * data=boost::get_error_info<errinfo_charStr>(*this);
        std::ostringstream ostr;//下面的代码看见吗?该异常处理机制,可以输出自定义提示,出错函数,以及出错源代码行!!
        ostr<<"错误提示:"<<data->get()<<"\r\n"
            <<"日期:"<<now<<"\r\n"
            <<"文件:"<<*file<<"\r\n"
            <<"函数:"<<*function<<"\r\n"
            <<"行数:"<<*line<<"\r\n"<<std::endl;
        std::string tmpString=ostr.str();//=*data;//ostr.str();
        const char *p=tmpString.c_str();
        std::shared_ptr<TCHAR> ptrStr(char_To_TCHAR(p));
        if(m_parentWnd!=NULL)
        {
    ShowMessageDialog(ptrStr.get(),_T("错误"),m_parentWnd);
        }
        else{
            ::AfxMessageBox(ptrStr.get());
        }
    _cSetLogFile2::PrintErrorInfo(ptrStr.get());
    return data->get();


_cMyExceptionImpl.cpp 完

_cSetLogFile2.h 如下:

#pragma once
#include <fstream>
#include <memory>
#include <functional>
typedef std::basic_ofstream<TCHAR> _MyOfstream;
//auto Delete = [](_MyOfstream *p) {if(p->is_open())p->close();delete p;};
class _cOfstreamDeleter{
// What should be the default?? No deleter? delete? I choose to
// mimic the default of unique_ptr.
public:
    _cOfstreamDeleter(): f([](_MyOfstream *p) {
        if(p->is_open()){
            p->close();
        }
        delete p;})
{};

// allow the user to specify what type of deleter to use
explicit _cOfstreamDeleter(std::tr1::function< void(_MyOfstream *)> const &f_ )
: f(f_)
{};

void operator()(_MyOfstream *p) const
{
f(p);
};

private:
std::tr1::function< void(_MyOfstream *)> f;
};


typedef std::unique_ptr<_MyOfstream,_cOfstreamDeleter> _MyOfstreamUnp;
class _cSetLogFile2{
    //class _cMyExceptionImpl;
private:
    static _MyOfstreamUnp m_unp_Fout;
    static _MyOfstreamUnp InitFout(void);
    //static std::ofstream fout;
public:
    _cSetLogFile2(void);
    virtual ~_cSetLogFile2();
public:

    //    //return fout;
    //};
    static void SetLogFileName( PTSTR pszLogFileName );
    static void PrintErrorInfo(const TCHAR* exp){
        (*m_unp_Fout).imbue(std::locale("chs"));
        *m_unp_Fout<<std::endl<<exp;};

    //static TCHAR m_szLogFileName[MAX_PATH];
    //static HANDLE m_hReportFile;

};

_cSetLogFile2.cpp

#include "StdAfx.h"
#include "_cSetLogFile2.h"
#include "_cMyExceptionImpl.h"
//TCHAR _cSetLogFile2::m_szLogFileName[MAX_PATH];
//HANDLE _cSetLogFile2::m_hReportFile;
//std::ofstream _cSetLogFile2::fout;
_MyOfstreamUnp _cSetLogFile2::InitFout(void)
{
    static TCHAR szLogFileName[MAX_PATH];
    GetModuleFileName( 0, szLogFileName, MAX_PATH );

    // Look for the '.' before the "EXE" extension.  Replace the extension
    // with "RPT"
    PTSTR pszDot = _tcsrchr( szLogFileName, _T('.') );
    if ( pszDot )
    {
        pszDot++;   // Advance past the '.'
        if ( _tcslen(pszDot) >= 3 )
            _tcscpy_s(pszDot,_tcslen(_T("RPT"))+1,_T("RPT"));// "RPT" -> "Report"
        //_tcscpy( pszDot, _T("RPT") );   // "RPT" -> "Report"
    }
    _MyOfstreamUnp tmpFout(new _MyOfstream(szLogFileName,std::ios::app),_cOfstreamDeleter());
    return tmpFout;
}
_MyOfstreamUnp _cSetLogFile2::m_unp_Fout=InitFout();
_cSetLogFile2::_cSetLogFile2(void)
{
}
_cSetLogFile2::~_cSetLogFile2()
{
    //m_unp_Fout->close();
    //fout.close();
}
void _cSetLogFile2::SetLogFileName( PTSTR pszLogFileName )
{
    m_unp_Fout.reset(new _MyOfstream(pszLogFileName,std::ios::app));
    //fout.open(pszLogFileName,std::ios::out|std::ios::app);

}

_cSetLogFile2.cpp完


// MessageDialog.h : MessageDialog DLL 的主头文件
//

#pragma once

#ifndef __AFXWIN_H__
    #error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"
#endif

#include "resource.h"        // 主符号
// CMessageDialog 对话框

class CMessageDialog : public CDialogEx
{
    DECLARE_DYNAMIC(CMessageDialog)

public:
    explicit CMessageDialog(const CString& ErrorInfoStr,
        const CString& TitleStr=_T("错误!"),
        CWnd* pParent = NULL);   // 标准构造函数
    virtual ~CMessageDialog();

// 对话框数据
    enum { IDD = IDD_DIALOG_ERROR };

protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
private:
    CString m_TitleStr;
    CString m_ErrorInfoStr;

    DECLARE_MESSAGE_MAP()
    CEdit m_CEdit_ErrorContent;
    virtual BOOL OnInitDialog();
public:
};
void ShowMessageDialog(//不能在易失模态对话框前使用
    const TCHAR * const strErrorInfo=_T("错误!"),
    const TCHAR * const strTitle=_T("错误!"),CWnd* pParent=NULL);
void ShowMessageDialog(//不能在易失模态对话框前使用
    CString strErrorInfo=_T("错误!"),

    CString strTitle=_T("错误!"),CWnd* pParent=NULL);

MessageDialog.h完


MessageDialog.cpp 内容如下:

// MessageDialog.cpp : 定义 DLL 的初始化例程。
//

#include "stdafx.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//
//TODO: 如果此 DLL 相对于 MFC DLL 是动态链接的,
//        则从此 DLL 导出的任何调入
//        MFC 的函数必须将 AFX_MANAGE_STATE 宏添加到
//        该函数的最前面。
//
//        例如:
//
//        extern "C" BOOL PASCAL EXPORT ExportedFunction()
//        {
//            AFX_MANAGE_STATE(AfxGetStaticModuleState());
//            // 此处为普通函数体
//        }
//
//        此宏先于任何 MFC 调用
//        出现在每个函数中十分重要。这意味着
//        它必须作为函数中的第一个语句
//        出现,甚至先于所有对象变量声明,
//        这是因为它们的构造函数可能生成 MFC
//        DLL 调用。
//
//        有关其他详细信息,
//        请参阅 MFC 技术说明 33 和 58。
//




// CMessageDialogApp
//
//BEGIN_MESSAGE_MAP(CMessageDialogApp, CWinApp)
//END_MESSAGE_MAP()
//
//
//// CMessageDialogApp 构造
//
//CMessageDialogApp::CMessageDialogApp()
//{
//    // TODO: 在此处添加构造代码,
//    // 将所有重要的初始化放置在 InitInstance 中
//}
//
//
//// 唯一的一个 CMessageDialogApp 对象
//
//CMessageDialogApp theApp;
//
//
//// CMessageDialogApp 初始化
//
//BOOL CMessageDialogApp::InitInstance()
//{
//    CWinApp::InitInstance();
//
//    return TRUE;
//}
// MessageDialog.cpp : 实现文件
//

#include "MessageDialog.h"
#include "_cMyExceptionImpl.h"

// CMessageDialog 对话框

IMPLEMENT_DYNAMIC(CMessageDialog, CDialogEx)

    CMessageDialog::CMessageDialog(const CString& ErrorInfoStr,
    const CString& TitleStr,CWnd* pParent /*=NULL*/)
    : CDialogEx(CMessageDialog::IDD, pParent),m_TitleStr(TitleStr)
    ,m_ErrorInfoStr(ErrorInfoStr)
{

}

CMessageDialog::~CMessageDialog()
{
}

void CMessageDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_EDIT1, m_CEdit_ErrorContent);
}


BEGIN_MESSAGE_MAP(CMessageDialog, CDialogEx)
END_MESSAGE_MAP()

// CMessageDialog 消息处理程序


BOOL CMessageDialog::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // TODO:  在此添加额外的初始化
    this->SetWindowTextW(m_TitleStr);
    m_CEdit_ErrorContent.SetWindowTextW(m_ErrorInfoStr);
    return TRUE;  // return TRUE unless you set the focus to a control
    // 异常: OCX 属性页应返回 FALSE
}
void ShowMessageDialog(
    const TCHAR * const strErrorInfo,
    const TCHAR * const strTitle,CWnd* pParent)
{
    //AFX_MANAGE_STATE(AfxGetStaticModuleState());
    try{
    CMessageDialog dlg(strErrorInfo,strTitle,pParent);
    dlg.DoModal();
    }catch(...)
    {
        ::AfxMessageBox(_T("My消息对话框显示错误!"));
    }
}
void ShowMessageDialog(
    CString strErrorInfo,
    CString strTitle,CWnd* pParent)
{
    //AFX_MANAGE_STATE(AfxGetStaticModuleState());
    const TCHAR * tmpTCHAR=strErrorInfo.GetBuffer(0);
    const TCHAR * tmpTCHAR2=strTitle.GetBuffer(0);
    ShowMessageDialog(tmpTCHAR,tmpTCHAR2,pParent);
    strErrorInfo.ReleaseBuffer();
    strTitle.ReleaseBuffer();

}

MessageDialog.cpp

以下是应用代码:注意红字

BOOL CFormViewFormatTrans_JinSan::readSettings(void)
{
    try{
        CDocumentSetting*pTmpDoc=(CDocumentSetting*)theApp.GetCDocumentSetting();
        if(pTmpDoc==NULL||pTmpDoc->m_DBObjs==NULL||
            pTmpDoc->m_DBObjs->m_pDb==NULL||
            pTmpDoc->m_DBObjs->m_pDb->m_pConnection==NULL||
            pTmpDoc->m_DBObjs->m_pDb->m_pConnection->GetState() == adStateClosed)
        {
            CString m_MDBFilePath,SqlStr,strTmp;
            m_MDBFilePath=GetAppPath();
            m_MDBFilePath+=_T("\\TmpDB_Setting_JinSan.mdb");
            BOOL bIsExist=PathFileExists(m_MDBFilePath);
            if(bIsExist==FALSE)
            {
                BOOST_THROW_EXCEPTION(_cMyExceptionImpl(_T("设置数据库文件不存在!"),this));
            }
            CString StrSql;
            StrSql.Format(_T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s;\
                             User ID=admin;Password=;Jet OLEDB:Database Password=1234567;"),
                             m_MDBFilePath);
            //m_pConnection->PutMode(adModeReadWrite);
            //_ConnectionPtr m_pConnection;
            //m_pConnection.CreateInstance(__uuidof(Connection));
            _cDataBaseCommonVarSetting m_DBObjs(StrSql);
            if(ReadProduceSettings(m_DBObjs)==FALSE)
            {
                BOOST_THROW_EXCEPTION(_cMyExceptionImpl(_T("ReadProduceSettings失败!"),this));
            }
            if(CreateComlumnMingChenMap(m_DBObjs)==FALSE)
            {
                BOOST_THROW_EXCEPTION(_cMyExceptionImpl(_T("CreateComlumnMingChenMap失败!"),this));
            }
        }
        else
        {
            _cDataBaseCommonVarSetting m_DBObjs(((CDocumentSetting*)theApp.GetCDocumentSetting())->m_DBObjs->m_pDb->GetActiveConnection());

            if(ReadProduceSettings(m_DBObjs)==FALSE)
            {
                BOOST_THROW_EXCEPTION(_cMyExceptionImpl(_T("ReadProduceSettings失败!"),this));
            }
            if(CreateComlumnMingChenMap(m_DBObjs)==FALSE)
            {
                BOOST_THROW_EXCEPTION(_cMyExceptionImpl(_T("CreateComlumnMingChenMap失败!"),this));
            }
        }

    }catch(std::exception & exp )
    {
        exp.what();
        return FALSE;
    }
    catch(_com_error &e)
    {
        dump_com_error(e);
        return FALSE;    
    }
    catch(...)   
    {   
        return FALSE;
    }  
    return TRUE;
}
看到了吗,VC++的异常处理也可以做到像java那样,用一个语句就搞定,在异常处理模块中,也不用很多判断,尽情享用简洁代码的快感吧。

猜你喜欢

转载自blog.csdn.net/tom_xuzg/article/details/80331416
今日推荐