zoukankan      html  css  js  c++  java
  • 第1章 错误处理

    1.1 定义自己的错误代码

    (1)常见的Windows函数返回值数据类型

    数据类型

    指出函数调用失败的值

    VOID

    这个函数不可能失败。只有极少数Windows函数的返回值类型为VOID

    BOOL

    如果函数失败,返回值为0;否则,成功时返回值非0。

    ★应避免测试返回值是否为TRUE,最稳妥的做法是检查它是否不为FALSE。(因为TRUE在不同的机器上,可能值是不同的,但FALSE却都是0)。

    如,if(FALSE!=func)、if(FALSE==func())、if(!func())或if(func())。

    但if(TRUE==func)或if(TRUE!=func())不可取。

    HANDLE

    失败——NULL或INVALID_HANDLE_VALUE(-1):要按MSDN函数说明来判断到底属哪种?

    否则——标识一个可操纵的对象。

    PVOID

    失败——NULL。

    成功——标识一个数据块的内存地址

    LONG/DWORD

    这种类型比例复杂。(要看MSDN中具体函数的说明)

    eg.返回计数的函数,如果因某种原因不能计数,通常返回0或-1。

    (2)GetLastError——利用“线程本地存储机制”,使用每个线程的错误代码不会互相干挠,注意,这个错误代码是属于线程的。当API函数调用失败时,这个函数会设置调用该函数的线程(简称为Calling Thread)的错误代码值。即“调用线程”最近的错误代码,部分函数在成功调用时,可能用ERROR_SUCCESS改写此值。(所以,0表示操作成功,没有错误)

       ①为什么成功调用仍需改写这个“Last Error Code”?

       有时创建对象成功,可能是因为内核中存在一个同名的事件内核对象,函数直接返回该对象。但某些情况下,我们需要知道这种情况的发生,所以特定函数调用成功时,Windows通过改写这个Last Error Code的值,用户就通过GetLastError来知道这件事或得到一些额外的信息。

       ②让VS始终显示线程的上一个错误代码的文本描述。

     

    在“Watch(监视)”窗口中添加一行,在名称栏中输入“$err,hr”,其中的“,hr”表示列出错误代码的文本描述。

       ③错误消息:包含消息ID、消息编号、消息文本;

           eg.#define ERROR_SUCCESS  0L;//消息ID为ERROR_SUCCESS,编号为0L。

    (3)FormatMessage——将错误代码转换为相应的文本描述(可以转成指定语言的文本,如中文!)

    参数

    含义

    DWORD dwFlags

    格式化选项,对lpSource参数值有指导作用

    ①FORMAT_MESSAGE_ALLOCATE_BUFFER:为接收的描述字符串分配内存。

    ②FORMAT_MESSAGE_FROM_SYSTEM:在系统的id映射表中寻找描述字符串

    ③FORMAT_MESSAGE_FROM_HANDLE:在其他资源模块(eg.dll)中寻找描述字符串

    ④FORMAT_MESSAGE_FROM_STRING:消息ID是个字符串,不是DWORD

    ⑤FORMAT_MESSAGE_IGNORE_INSERTS:允许我们获得含有%占位符的消息,不传递这个标志,就必须在Arguments参数中提供这些占位符信息。(即直接返回含占位符的错误文本)。

    通常为:FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM

    LPCVOID lpSource

    这个值是消息资源表示来自哪里,当dwFlags为FORMAT_MESSAGE_FROM_HANDLE时该参数表示模块的句柄。为FORMAT_MESSAGE_FROM_STRING时,通常为NULL

    DWORD dwMessageId

    消息ID。如果指定了FORMAT_MESSAGE_STRING时,该项将被忽略。

    DWORD dwLanguageId

    消息描述所用的语言。通常0表示自动选择。

    LPTSTR lpBuffer

    如果未指定FORMAT_MESSAGE_ALLOCATE_BUFFER,则要我们自己提供缓冲区。否则为系统LocalAlloc分配,但用完后,仍需要被调用LocalFree来释放缓冲区。

    DWORD nSize

    当指定FORMAT_MESSAGE_ALLOCATE_BUFFER时,为系统分配的最小缓冲区大小。

    否则为我们指供的缓冲区的大小。(以TCHARs为单位的)

    va_list *Arguments

    在消息定义中的插入序列将会被忽略,这个标示符在获取一个格式化好的消息十分有用。如果这个标示符设置好了,那么Arguments参数将被忽略。一般可设为NULL。

    (4)定义自己的错误代码——可用于我们自己的函数,而不是API函数。

      ①VOID SetLastError(DWORD dwErrCode)——错误代码的格式

    31-30

    29

    28

    27-16

    15-0

    内容

    严重性

    Microsoft/客户

    保留

    Facility代码

    异常代码

    含义

    0=成功

    1=信息(提示)

    2=警告

    3=错误

    0=Microsoft定义的代码。

    1=客户定义的代码

    必须为0

    前256个值由Microsoft保留。

    可查看winerror.h文件

    Microsoft/客户定义的代码

    1.2 ErrorShow示例程序

    (1)消息分流器(Messasge Craker)——使窗口过程代码更简洁,逻辑更清晰。

       ①HANDLE_MSG宏及问题(该宏一般用在WndProc而不能用在DlgProc中,见后面分析)

    #define HANDLE_MSG(hwnd, message, fn)   

        case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))

    A、如果调用HANDLE_MSG(hwnd,WM_INITDIALOG,Dlg_InitDialog),而其中的

    HANDLE_##message相当于HANDLE_WM_INITDIALOG宏,其定义如下:

    #define HANDLE_WM_INITDIALOG(hwnd, wParam, lParam, fn)

        (LRESULT)(DWORD)(UINT)(BOOL)(fn)((hwnd), (HWND)(wParam), lParam)

    而从上面宏定义可以看出返回值为BOOL型,该调用如果用在DlgProc中没问题,但是

       B、当处理HANDLE_WM_COMMAND宏时,返回值为0L(注意逗号表达式),这对于一般的窗口过程WndProc来说WM_COMMAND中返回0没问题,但对于对话框过程来说,就出问题了,因为WM_COMMAND应return TRUE或break,而不一定是FALSE(TRUE表示该消息处理过了),这就是 HANDLE_MSG的问题!

    #define HANDLE_WM_COMMAND(hwnd, wParam, lParam, fn) /

            ((fn)((hwnd), (int)(LOWORD(wParam)), (HWND)(lParam),

    (UINT)HIWORD(wParam)), 0L)

    ②chHANDLE_DLGMSG宏:对于对话框消息处理过程要使用SetDlgMsgResult宏来正确返回。这就是大牛Jeffery定义的chHANDLE_DLGMSG宏,其中封装了SetDlgMsgResult。这个chHANDLE_DLGMSG宏会正确返回值或句柄。该宏一般在对话框过程中使用。

    #define chHANDLE_DLGMSG(hWnd, message, fn)                

        case (message): return (SetDlgMsgResult(hWnd, uMsg,    

           HANDLE_##message((hWnd), (wParam), (lParam), (fn))))

    //DWLP_MSGRESULT,替换内置对话框过程的返回值

    #define     SetDlgMsgResult(hwnd, msg, result) ((

            (msg) == WM_CTLCOLORMSGBOX      ||

            (msg) == WM_CTLCOLOREDIT        ||

            (msg) == WM_CTLCOLORLISTBOX     ||

            (msg) == WM_CTLCOLORBTN         ||

            (msg) == WM_CTLCOLORDLG         ||

            (msg) == WM_CTLCOLORSCROLLBAR   ||

            (msg) == WM_CTLCOLORSTATIC      ||

            (msg) == WM_COMPAREITEM         ||

            (msg) == WM_VKEYTOITEM          ||

            (msg) == WM_CHARTOITEM          ||

            (msg) == WM_QUERYDRAGICON       ||

            (msg) == WM_INITDIALOG            

    ) ? (BOOL)(result) : (SetWindowLongPtr((hwnd), DWLP_MSGRESULT, (LPARAM)(LRESULT)(result)), TRUE)) //(注意逗号表达式里返回的是TRUE)

    可见,如果消息是中的一个,那么返回值就是我们写的fn函数的返回值,如果消息不是其中的,那么先执行SetWindowLongPtr把对话框中消息处理程序的返回值设置为fn函数的返回值,最后返回TRUE告诉对话框内置消息过程该消息己经处理过了。(根据逗号操作符特性)。

    为什么要这么做呢?这些WM_CTLCOLORMSGBOX 、 WM_CTLCOLOREDIT……有什么特殊的吗? 这些消息的返回值都是有已知的且有自己含义的,对于这些消息只需要返回它们函数处理的返回值就让程序正常运行。但对于消息WM_COMMAND就很难说了,很有可能是某个控件的通知消息,需要一个值,这个值可能是零也可能不是零,所以不能像WM_CTLCOLORMSGBOX那样处理了,因为对话框过程中TRUE大部分时候代表用户处理此消息,FALSE或0代表用户没处理此消息,那么系统内置的对话框处理过程会进行默认处理。这样的话,如果一个WM_COMMAND的处理消息需要返回0,怎么办?这样办,先返回TRUE,然后用SetWindowLongPtr设置需要返回的返回值,返回TRUE是向内置对话框过程表明该消息已经处理了,但返回值最后被代替成了SetWindowlongPtr的值,这样才能正常工作。

    【延伸】Windows内的对话框消息过程

    LRESULT CALLBACK DefDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 

    {  

    //获取自定义的对话框过程dp

      DLGPROC dp = (DLGPROC)GetWindowLongPtr(hdlg, DWLP_DLGPROC);//dp为对话框过

      SetWindowLongPtr(hdlg, DWLP_MSGRESULT, 0); //设置内置消息过程的返回值

      INT_PTR fResult = dp(hdlg, uMsg, wParam, lParam); //调用对话框过程

      //从SetDlgMsgResult可知,dp返回的是TRUE或FALSE,但真正的返回值保存在

      //DWLP_MSGRESULT中。

    if (fResult) return GetWindowLongPtr(hdlg, DWLP_MSGRESULT); //如果处理过了

                                  //从SetDlgMsgResult的处理可知dp处理完后,

                                  // SetDlgMsgResult返回TRUE给fResult,同时

                                  // DWLP_MSGRESULT被设置我们的自定义的返回值

                                  //然后通过Get这个返回值,返回给DefDlgProc。

                                 

      else ...做默认的事...       //FALSE,没处理,则进行默认处理

    }

    (2)FORWARD_WM_COMMAND宏

    #define FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, fn)

             (void)(fn)((hwnd), WM_COMMAND, MAKEWPARAM((UINT)(id),

    (UINT)(codeNotify)), (LPARAM)(HWND)(hwndCtl))

        ★模拟“查询”按钮点击(通过发送WM_COMMAND消息)

            FORWARD_WM_COMMAND(hwnd, IDOK, GetDlgItem(hwnd, IDOK), BN_CLICKED,

                PostMessage);//PostMessage为系统API

    【ErrorShow程序】

    /*-----------------------------------------------------------------------------
    Module:  ErrorShow.cpp
    Notices: Copyright(c)2008 Jeffery Richter & Christophe Nasarre
    ------------------------------------------------------------------------------*/
    #include "....CommonFilesCmnHdr.h"  //里面包含Windows.h、CommCtrl.h和process.h
    #include "resource.h"
    #include <tchar.h>
    
    
    ////////////////////////////////////////////////////////////////////////////////
    #define ESM_POKECODEANDLOOKUP  (WM_USER + 100)
    
    const TCHAR g_szAppName[] = TEXT("Error Show");
    
    INT_PTR CALLBACK AboutDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    ////////////////////////////////////////////////////////////////////////////////
    
    BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
    {
        chSETDLGICONS(hwnd, IDI_ERRORSHOW);
    
        //限制错误代码在5位长度以内
        Edit_LimitText(GetDlgItem(hwnd, IDC_ERRORCODE), 5); 
    
        //查找命令行传入的错误代码号
        SendMessage(hwnd, ESM_POKECODEANDLOOKUP, lParam, 0);
        return TRUE;
    }
    //根据不同的语言来显示错误文本描述
    BOOL ShowErrorText(HWND hwnd,int idCtrl, DWORD dwLangID,DWORD dwError)
    {
        HLOCAL hlocal = NULL; //获得错误文本描述的缓冲区
        HMODULE hDll = NULL;
        BOOL iRet = TRUE;
    
        //获取错误文本描述
        BOOL fOk = FormatMessage(
                                    FORMAT_MESSAGE_FROM_SYSTEM | 
                                    FORMAT_MESSAGE_IGNORE_INSERTS |
                                    FORMAT_MESSAGE_ALLOCATE_BUFFER, 
                                    NULL, 
                                    dwError, 
                                    dwLangID,
                                    (PTSTR)&hlocal,
                                    0, 
                                    NULL);
    
        if (!fOk) //查询失败,可能是网络相关的错误代码
        {
            hDll = LoadLibraryEx(TEXT("netmsg.dll"), NULL, DONT_RESOLVE_DLL_REFERENCES);
            if (hDll != NULL)
            {
                //注意:改为FORMAT_MESSAGE_FROM_HMOUDLE,并传hdll给lpSource参数
                fOk = FormatMessage(
                    FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS |
                    FORMAT_MESSAGE_ALLOCATE_BUFFER, hDll, dwError, dwLangID,
                    (PTSTR)&hlocal, 0, NULL);
                FreeLibrary(hDll);
            }
        }
        
        iRet = fOk && (hlocal != NULL);
        if (iRet)
        {
            SetDlgItemText(hwnd, idCtrl, (PCTSTR)LocalLock(hlocal));
            LocalFree(hlocal);
        }
        return iRet;
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    //id =LOWORD(wParam);
    //codeNotify =HIWORD(wParam)
    //hwndCtrl = lParam;
    void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtrl, UINT codeNotity)
    {
        switch (id)
        {
        case IDC_QUIT:
            EndDialog(hwnd, id);
            break;
    
        case IDC_ABOUT:
            DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_ABOUT), hwnd, AboutDlgProc);
            break;
    
        case IDC_ALWAYSONTOP:
            SetWindowPos(hwnd, IsDlgButtonChecked(hwnd, IDC_ALWAYSONTOP) ? 
                         HWND_TOPMOST:HWND_NOTOPMOST,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE);
            break;
    
        case IDC_ERRORCODE:
            EnableWindow(GetDlgItem(hwnd, IDOK), Edit_GetTextLength(hwndCtrl) > 0);
            break;
    
        case IDOK:
            //获取错误代码
            DWORD  dwError = GetDlgItemInt(hwnd, IDC_ERRORCODE, NULL, FALSE);
    
            //使用默认语言(中文)来查看错误文本描述,MAKELANGID组合一个0值
            DWORD  dwLangID = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
            if (!ShowErrorText(hwnd, IDC_ERRORTEXT_CN, dwLangID, dwError))
                SetDlgItemText(hwnd, IDC_ERRORTEXT_CN, TEXT("没有发现这个错误代码的描述文本"));
    
            //使用美国英语来查看错误文本描述
            dwLangID = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
            if (!ShowErrorText(hwnd, IDC_ERRORTEXT_US, dwLangID, dwError))
                SetDlgItemText(hwnd, IDC_ERRORTEXT_US, TEXT("No text found for this error number."));
            break;
        }
    }
    //////////////////////////////////////////////////////////////////////////////////
    
    //注意,一般对话框过程是返回BOOL型的,但这里经过chHANDLG_DLGMSG宏处理后
    //会返回:值或句柄
    INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
            //以下两句代码在VS2013下,“IntelliSense”会提示错误,但实际上这并没错
            //因为这里的WM_INIDIALOG会被“智能感知”错误地认为会被宏替宏为0x0110
            //但实际上,因为chHANDLE_DLGMSG内部是通过HANDL_##message连接的,所以
            //编译器仍然会正确解析。因此如果觉得不爽,可以禁用IntelliSense方法是
            //工具→选项→文本编辑器→C/C++→高级→禁用IntelliSense或禁用波形曲线
            chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog);
            chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand);
            
        case ESM_POKECODEANDLOOKUP: //命令行传递过来的查询代码
            SetDlgItemInt(hwnd, IDC_ERRORCODE, (UINT)wParam, FALSE);
    
            //模拟“查询”按钮点击(通过发送WM_COMMAND消息)
            FORWARD_WM_COMMAND(hwnd, IDOK, GetDlgItem(hwnd, IDOK), BN_CLICKED,
                               PostMessage);
            //该函数将创建指定窗口的线程设置到前台,并且激活该窗口
            SetForegroundWindow(hwnd);
            break;
    
        case WM_CLOSE:
            EndDialog(hwnd, IDC_QUIT);
            break;
        }
    
        return FALSE;
    }
    
    //“关于”对话框
    INT_PTR CALLBACK AboutDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
            case IDCANCEL:
                EndDialog(hwnd, 0);
                return TRUE;
            }
    
        case WM_CLOSE:
            EndDialog(hwnd, 0);
            return TRUE;
    
        }
        return FALSE;
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR pszCmdLine, int)
    {
        //第1个参数:#32270表示对话框类名,Windows内部注册的窗口类名
        //第2个参数:窗口名称
        HWND hwnd = FindWindow(TEXT("#32270"), TEXT("Error Show"));
    
        if (IsWindow(hwnd)) //判断定给定的窗口句柄是否标识一个已存在的窗口
        {
            //己经存在一个正在运行的实例,激活该窗口并设置编辑框中的错误为wParam,然后查询
            //int i=_ttoi("123");将字符串转为整型
            SendMessage(hwnd, ESM_POKECODEANDLOOKUP, _ttoi(pszCmdLine), 0);
        }
        else
            DialogBoxParam(hInstExe, MAKEINTRESOURCE(IDD_ERRORSHOW), NULL, 
                           Dlg_Proc,_ttoi(pszCmdLine));
        return 0;
                   
    }
    //{{NO_DEPENDENCIES}}
    // Microsoft Visual C++ 生成的包含文件。
    // 供 ErrorShow.rc 使用
    //
    #define IDD_ERRORSHOW                   101
    #define IDI_ERRORSHOW                   102
    #define IDD_ABOUT                       103
    #define IDC_ERRORCODE                   1001
    #define IDC_ALWAYSONTOP                 1002
    #define IDC_ERRORTEXT_CN                1003
    #define IDC_ERRORTEXT_US                1004
    #define IDC_ABOUT                       1005
    #define IDC_QUIT                        1006
    
    // Next default values for new objects
    // 
    #ifdef APSTUDIO_INVOKED
    #ifndef APSTUDIO_READONLY_SYMBOLS
    #define _APS_NEXT_RESOURCE_VALUE        104
    #define _APS_NEXT_COMMAND_VALUE         40001
    #define _APS_NEXT_CONTROL_VALUE         1006
    #define _APS_NEXT_SYMED_VALUE           101
    #endif
    #endif

    //ErrorShow.rc

    // Microsoft Visual C++ generated resource script.
    //
    #include "resource.h"
    
    #define APSTUDIO_READONLY_SYMBOLS
    /////////////////////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 2 resource.
    //
    #include "winres.h"
    
    /////////////////////////////////////////////////////////////////////////////
    #undef APSTUDIO_READONLY_SYMBOLS
    
    /////////////////////////////////////////////////////////////////////////////
    // 中文(简体,中国) resources
    
    #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
    LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
    
    #ifdef APSTUDIO_INVOKED
    /////////////////////////////////////////////////////////////////////////////
    //
    // TEXTINCLUDE
    //
    
    1 TEXTINCLUDE 
    BEGIN
        "resource.h"
    END
    
    2 TEXTINCLUDE 
    BEGIN
        "#include ""winres.h""
    "
        ""
    END
    
    3 TEXTINCLUDE 
    BEGIN
        "
    "
        ""
    END
    
    #endif    // APSTUDIO_INVOKED
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // Dialog
    //
    
    IDD_ERRORSHOW DIALOGEX 0, 0, 245, 154
    STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
    CAPTION "Error Show"
    FONT 8, "MS Shell Dlg", 400, 0, 0x1
    BEGIN
        DEFPUSHBUTTON   "查找",IDOK,125,17,40,14,WS_DISABLED
        LTEXT           "十进制错误代码:",IDC_STATIC,15,20,76,8
        CONTROL         "窗口置顶",IDC_ALWAYSONTOP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,179,19,48,10
        EDITTEXT        IDC_ERRORTEXT_CN,16,50,212,31,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER,WS_EX_CLIENTEDGE
        EDITTEXT        IDC_ERRORCODE,83,18,27,14,ES_AUTOHSCROLL | ES_NUMBER
        LTEXT           "错误信息文本(本地语言)",IDC_STATIC,16,39,88,8
        EDITTEXT        IDC_ERRORTEXT_US,16,98,212,31,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER,WS_EX_CLIENTEDGE
        LTEXT           "错误信息文本(美国英语)",IDC_STATIC,16,87,88,8
        PUSHBUTTON      "关于",IDC_ABOUT,52,132,50,14
        PUSHBUTTON      "退出",IDC_QUIT,127,132,50,14
    END
    
    IDD_ABOUT DIALOGEX 0, 0, 165, 82
    STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
    CAPTION "关于Error Show"
    FONT 8, "MS Shell Dlg", 400, 0, 0x1
    BEGIN
        DEFPUSHBUTTON   "关闭",IDCANCEL,63,57,50,14
        CTEXT           "Error Show 程序
    
    参照《Windows核心编程》实现。
    
    xjj1977@qq.com
    
    2015.07.06",IDC_STATIC,10,6,144,41
    END
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // DESIGNINFO
    //
    
    #ifdef APSTUDIO_INVOKED
    GUIDELINES DESIGNINFO
    BEGIN
        IDD_ERRORSHOW, DIALOG
        BEGIN
            LEFTMARGIN, 7
            RIGHTMARGIN, 238
            TOPMARGIN, 7
            BOTTOMMARGIN, 147
        END
    
        IDD_ABOUT, DIALOG
        BEGIN
            LEFTMARGIN, 7
            RIGHTMARGIN, 158
            TOPMARGIN, 1
            BOTTOMMARGIN, 79
        END
    END
    #endif    // APSTUDIO_INVOKED
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // Icon
    //
    
    // Icon with lowest ID value placed first to ensure application icon
    // remains consistent on all systems.
    IDI_ERRORSHOW           ICON                    "ErrorShow.ico"
    #endif    // 中文(简体,中国) resources
    /////////////////////////////////////////////////////////////////////////////
    
    
    
    #ifndef APSTUDIO_INVOKED
    /////////////////////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 3 resource.
    //
    
    
    /////////////////////////////////////////////////////////////////////////////
    #endif    // not APSTUDIO_INVOKED

    //cmnHdr.h

    /******************************************************************************
    Module:  CmnHdr.h
    Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre
    Purpose: Common header file containing handy macros and definitions
             used throughout all the applications in the book.
             See Appendix A.
    ******************************************************************************/
    
    
    #pragma once   // Include this header file once per compilation unit
    //Windows版本构建选项
    //////////////////////// Windows Version Build Option /////////////////////////
    
    //因本书程序调用了一些Windows Vista中的新函数,在Vista以上必须定义_WIN32_WINNT
    //才能调用这些函数,否则会引起编译错误。
    // = 0x0600 for VISTA level from sdkddkver.h
    #define _WIN32_WINNT _WIN32_WINNT_LONGHORN  //_WIN32_WINNT_LONGHORN =0x0600
    #define WINVER       _WIN32_WINNT_LONGHORN 
    
    
    //Unicode构建选项,本书所有程序即可编译成ANSI版本,也可编译成Unicode版本
    //////////////////////////// Unicode Build Option /////////////////////////////
    
    
    // Always compiler using Unicode.
    #ifndef UNICODE
        #define UNICODE
    #endif
    
    // When using Unicode Windows functions, use Unicode C-Runtime functions too.
    #ifdef UNICODE
       #ifndef _UNICODE
          #define _UNICODE
       #endif
    #endif
    
    //Windows定义和编译警告级别,警告级别越高,表示检查越严格
    ///////////////////////// Include Windows Definitions /////////////////////////
    
    #pragma warning(push, 3) //存储当前警告级别,并设置警级别为3,来编译Windows.h文件
    #include <windows.h>
    #include <windowsx.h>    //要用到SetDlgMsgResult、Edit_LimitText等宏
    #pragma warning(pop)     //恢复原新的警告级别
    #pragma warning(push, 4) //存储当前警告级别,并设置为4级,来编译后面的内容
    #include <CommCtrl.h>
    #include <process.h>       // For _beginthreadex
    
    
    ///////////// Verify that the proper header files are being used //////////////
    
    
    #ifndef FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
    #pragma message("You are not using the latest Platform SDK header/library ")
    #pragma message("files. This may prevent the project from building correctly.")
    #endif
    
    //在警告级别下,允许禁用以下的警告
    ////////////// Allow code to compile cleanly at warning level 4 ///////////////
    
    //允许使用非标准扩展的单行注释
    /* nonstandard extension 'single line comment' was used */
    #pragma warning(disable:4001)
    
    // unreferenced formal parameter
    #pragma warning(disable:4100)
    
    // Note: Creating precompiled header 
    #pragma warning(disable:4699)
    
    // function not inlined
    #pragma warning(disable:4710)
    
    // unreferenced inline function has been removed
    #pragma warning(disable:4514)
    
    // assignment operator could not be generated
    #pragma warning(disable:4512)
    
    // conversion from 'LONGLONG' to 'ULONGLONG', signed/unsigned mismatch
    #pragma warning(disable:4245)
    
    // 'type cast' : conversion from 'LONG' to 'HINSTANCE' of greater size
    #pragma warning(disable:4312)
    
    // 'argument' : conversion from 'LPARAM' to 'LONG', possible loss of data
    #pragma warning(disable:4244)
    
    // 'wsprintf': name was marked as #pragma deprecated
    #pragma warning(disable:4995)
    
    // unary minus operator applied to unsigned type, result still unsigned
    #pragma warning(disable:4146)
    
    // 'argument' : conversion from 'size_t' to 'int', possible loss of data
    #pragma warning(disable:4267)
    
    // nonstandard extension used : nameless struct/union
    #pragma warning(disable:4201)
    
    ///////////////////////// Pragma message helper macro /////////////////////////
    
    //以下的前缀ch表示Common Header的缩写
    /* 
    //为了让程序能够运行起来,可以先忽略一些代码,但在输出窗口中给予提示
    When the compiler sees a line like this: //当编译器发现以下代码时,会在输出窗口中
       #pragma chMSG(Fix this later)         //提示“某某文件,第几个以后要修复”,
                                             //注意为了使用方便,字符串不需要加引号
    
    it outputs a line like this:
    
      c:CDCmnHdr.h(82):Fix this later     //提示的内容
    
    You can easily jump directly to this line and examine the surrounding code.
    */
    
    #define chSTR2(x) #x  //#x,即表示把x当成字符(串)来用
    #define chSTR(x)  chSTR2(x)
    //#desc表示将desc变量当成字符串。这样输入时,可以不用加引用了。(如#pragma  chMsg(这里需要修复!))
    #define chMSG(desc) message(__FILE__ "(" chSTR(__LINE__) "):" #desc) //见上面的例子
    
    
    ////////////////////////////// chINRANGE Macro ////////////////////////////////
    
    
    // This macro returns TRUE if a number is between two others
    //该宏用来检查一个值是否介于两个值之间
    #define chINRANGE(low, Num, High) (((low) <= (Num)) && ((Num) <= (High)))
    
    
    
    ///////////////////////////// chSIZEOFSTRING Macro ////////////////////////////
    
    
    // This macro evaluates to the number of bytes needed by a string.
    //该宏用来计算字符串占用的字节总数(含)
    #define chSIZEOFSTRING(psz)   ((lstrlen(psz) + 1) * sizeof(TCHAR))
    
    

    //////////////////////////////// chDIMOF Macro ////////////////////////////////
    // 用来统计数组中元素个数的宏
      #define chDIMOF(Array) (sizeof(Array) / sizeof(Array[0]))

    /////////////////// chROUNDDOWN & chROUNDUP inline functions //////////////////
    
    
    // This inline function rounds a value down to the nearest multiple
    //计算一个数,让其最接近Value(小于等于Value,因为是向下),但这个数是Multiple的整数倍
    //eg.chROUNDDOWN(10,3) =9,表示显然9最接近10,但这个数是3的倍数。
    template <class TV, class TM>
    inline TV chROUNDDOWN(TV Value, TM Multiple) {
       return((Value / Multiple) * Multiple);
    }
    
    // This inline function rounds a value down to the nearest multiple
    //计算一个数,让其最接近Value(大于等于Value,因为是向上),但这个数是Multiple的整数倍
    //eg.chROUNDUP(10,3) =12,表示显然12最接近10,但这个数是3的倍数。
    template <class TV, class TM>
    inline TV chROUNDUP(TV Value, TM Multiple) {
       return(chROUNDDOWN(Value, Multiple) + 
          (((Value % Multiple) > 0) ? Multiple : 0));
    }
    
    
    
    ///////////////////////////// chBEGINTHREADEX Macro ///////////////////////////
    
    
    // This macro function calls the C runtime's _beginthreadex function. 
    // The C runtime library doesn't want to have any reliance on Windows' data 
    // types such as HANDLE. This means that a Windows programmer needs to cast
    // values when using _beginthreadex. Since this is terribly inconvenient, 
    // I created this macro to perform the casting.
    typedef unsigned (__stdcall *PTHREAD_START) (void *);
    
    #define chBEGINTHREADEX(psa, cbStackSize, pfnStartAddr, 
       pvParam, dwCreateFlags, pdwThreadId)                 
          ((HANDLE)_beginthreadex(                          
             (void *)        (psa),                         
             (unsigned)      (cbStackSize),                 
             (PTHREAD_START) (pfnStartAddr),                
             (void *)        (pvParam),                     
             (unsigned)      (dwCreateFlags),               
             (unsigned *)    (pdwThreadId)))
    
    
    ////////////////// DebugBreak Improvement for x86 platforms ///////////////////
    //允许进程不是通过调试器启动,仍然可以停在某一个断点上。
    //在Windows平台上,线程是调用DebugBreak来实现断点的,这个Kernel32.dll中的函数
    //让我们在附加一个调试器到进程时,指令指针会停在那条触发断点的CPU指令上。
    //因为这条指令是Kernel32.dll中的函数指令,为了调试我们的代码,可以在代码中
    //嵌入汇编int 3重新定义DebugBreak函数,从而产生一个效果等价的断点功能
    
    #ifdef _X86_
       #define DebugBreak()    _asm { int 3 }
    #endif
    
    
    /////////////////////////// Software Exception Macro //////////////////////////
    
    
    // Useful macro for creating your own software exception codes
    //创建软件异常码,也就是SetLasCode中要用的异常码
    #define MAKESOFTWAREEXCEPTION(Severity, Facility, Exception) 
       ((DWORD) ( 
       /* Severity code    */  (Severity       ) |     
       /* MS(0) or Cust(1) */  (1         << 29) |     
       /* Reserved(0)      */  (0         << 28) |     
       /* Facility code    */  (Facility  << 16) |     
       /* Exception code   */  (Exception <<  0)))
    
    
    /////////////////////////// Quick MessageBox Macro ////////////////////////////
    
    //弹出一个消息对话框,对话框的标题是调用进程对应的可执行文件的完整路径名称。
    inline void chMB(PCSTR szMsg) {
       char szTitle[MAX_PATH];
       GetModuleFileNameA(NULL, szTitle, _countof(szTitle)); //模拟完整的路径名称
       MessageBoxA(GetActiveWindow(), szMsg, szTitle, MB_OK);
    }
    
    
    //////////////////////////// Assert/Verify Macros /////////////////////////////
    
    //失败时弹出对话框,并设置断点
    inline void chFAIL(PSTR szMsg) {
       chMB(szMsg);
       DebugBreak();
    }
    
    // Put up an assertion failure message box. 
    //ASSERT(断言)为FALSE时,弹出消息对话框,并设置断点
    inline void chASSERTFAIL(LPCSTR file, int line, PCSTR expr) { 
       char sz[2*MAX_PATH];
       wsprintfA(sz, "File %s, line %d : %s", file, line, expr);
       chFAIL(sz);
    }
    
    
    // Put up a message box if an assertion fails in a debug build.
    #ifdef _DEBUG
       #define chASSERT(x) if (!(x)) chASSERTFAIL(__FILE__, __LINE__, #x) //调试版,弹对话框
    #else
       #define chASSERT(x)   //发行版就不弹
    #endif
    
    
    // Assert in debug builds, but don't remove the code in retail builds.
    #ifdef _DEBUG
       #define chVERIFY(x) chASSERT(x)
    #else
       #define chVERIFY(x) (x)  //发行版时,代码不删除
    #endif
    
    
    /////////////////////////// chHANDLE_DLGMSG Macro /////////////////////////////
    //消息分流器(Messasge Craker)
    //一般的对话框过程只返回TRUE或FALSE来说明消息有没有被处理,而以下的宏会像
    //WndProc中消息处理的LRESULT结果,返回值或句柄。该宏一般在对话框过程中使用
    // The normal HANDLE_MSG macro in WindowsX.h does not work properly for dialog
    // boxes because DlgProc returns a BOOL instead of an LRESULT (like
    // WndProcs). This chHANDLE_DLGMSG macro corrects the problem:
    #define chHANDLE_DLGMSG(hWnd, message, fn)                 
       case (message): return (SetDlgMsgResult(hWnd, uMsg,     
            HANDLE_##message((hWnd), (wParam), (lParam), (fn))))
    
    //////////////////////// Dialog Box Icon Setting Macro ////////////////////////
    
    
    // Sets the dialog box icons
    //设置对话框的图标(可以以WM_INITDIALOG中调用以下函数)
    inline void chSETDLGICONS(HWND hWnd, int idi) {
       SendMessage(hWnd, WM_SETICON, ICON_BIG,  (LPARAM) 
          LoadIcon((HINSTANCE) GetWindowLongPtr(hWnd, GWLP_HINSTANCE), 
             MAKEINTRESOURCE(idi)));
       SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) 
          LoadIcon((HINSTANCE) GetWindowLongPtr(hWnd, GWLP_HINSTANCE), 
          MAKEINTRESOURCE(idi)));
    }
        
    
    /////////////////////////// Common Linker Settings ////////////////////////////
    
    
    #pragma comment(linker, "/nodefaultlib:oldnames.lib") //强制编译器寻找(w)WinMain入口点函数
    
    // Needed for supporting XP/Vista styles.
    //支持XP风格的用户界面主题,默认下应用程序是不支持的
    #if defined(_M_IA64)
    #pragma comment(linker, "/manifestdependency:"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='IA64' publicKeyToken='6595b64144ccf1df' language='*'"")
    #endif
    #if defined(_M_X64)
    #pragma comment(linker, "/manifestdependency:"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.6000.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'"")
    #endif
    #if defined(M_IX86)
    #pragma comment(linker, "/manifestdependency:"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'"")
    #endif
    
    
    ///////////////////////////////// End of File /////////////////////////////////
  • 相关阅读:
    D-Power Products
    B2
    软考知识点梳理--螺旋模型
    软考知识点梳理--敏捷方法
    软考知识点梳理--瀑布模型
    软考知识点梳理--统一软件开发过程RUP
    软考知识点梳理--信息系统生命周期
    软考知识点梳理--信息资源管理
    软考知识点梳理--以太网
    软考知识点梳理--应急储备与管理储备
  • 原文地址:https://www.cnblogs.com/5iedu/p/4637496.html
Copyright © 2011-2022 走看看