《Windows核心编程》1对程序的错误处理读书笔记

1.函数返回值:


2.获得错误消息

从系统内部来讲,当一个 Windows函数检测到一个错误时,它会使用一个称为线程本地存储器(thread-local storage)的机制,将相应的错误代码号码与调用的线程关联起来。这将使线程能够互相独立地运行,而不会影响各自的错误代码。当函数返回时,它的返回值就能指明一个错误已经发生。使用GetlastError()获得错误代码(Windows函数运行失败时,应该立即调用GetLastError函数。如果调用另一个Windows函数,它的值很可能被改写)。

WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI GetLastError(
    VOID);

对应的错误消息在WinError.h可以查看。每个错误都有3种表示法:一个消息ID(这是你可以在源代码中使用的一个宏,以便与GetLastError的返回值进行比较),消息文本(对错误的英文描述)和一个号码(应该避免使用这个号码,可使用消息ID)。如:

//
// MessageId: ERROR_PROFILING_NOT_STOPPED
//
// MessageText:
//
// Profiling not stopped.
//
#define ERROR_PROFILING_NOT_STOPPED      551L

//
// MessageId: ERROR_COULD_NOT_INTERPRET
//
// MessageText:
//
// The passed ACL did not contain the minimum required information.
//
#define ERROR_COULD_NOT_INTERPRET        552L

注意 GetLastError能返回线程产生的最后一个错误。如果该线程调用的 Windows函数运行成功,那么最后一个错误代码就不被改写,并且不指明运行成功。有少数Windows函数并不遵循这一规则,它会更改最后的错误代码;但是 Platform SDK文档通常指明,当函数运行成功时,该函数会更改最后的错误代码。

3.定义自己的错误代码
//设置线程错误代码
WINBASEAPI VOID WINAPI SetLastError(
    _In_ DWORD dwErrCode);      //   32位的错误代码

自定义错误代码29位必须设置为1.

3.1编写错误信息文本


自定义错误代码:点击打开链接

4.错误查看小工具:

vs查看错误:

通过选定 Watch窗口(快速监视窗口)中的一行,并键入“@ e r r, h r”,能显示线程的最后错误代码的号码和该错误的英文描述。

在vs目录下VSSystem\Common7\Tools\errlook.exe可以用来查看错误信息代码所代表的意思。

5.FormatMessage函数:
DWORD WINAPI FormatMessage (
                            DWORD dwFlags, // source and processing options
                            LPCVOID lpSource, // message source
                            DWORD dwMessageId, // message identifier
                            DWORD dwLanguageId, // language identifier
                            LPTSTR lpBuffer, // message buffer
                            DWORD nSize, // maximum size of message buffer
                            va_list *Arguments // array of message inserts
                            );
dwFlags
标志位,决定如何说明lpSource参数,dwFlags的低位制定如何处理换行功能在输出缓冲区,也决定最大宽度的格式化输出行。可选参数:
标志 标志说明
FORMAT_MESSAGE_ALLOCATE_BUFFER
函数会分配一个足够大的缓冲区保存格式化消息,并且通过lpBuffer指向该
地址。当不再使用lpBuffer数据时,需调用LocalFree释放内存。
FORMAT_MESSAGE_ARGUMENT_ARRAY
Arguments参数不是指向 va_list结构体,是一个指向保存参数的数组指针。
FORMAT_MESSAGE_FROM_HMODULE
指定lpSource参数是要去搜索的一个包含消息表的模块句柄。如果 lpSource 是NULL,会搜索当前进程的主模块,这个标志不能与 FORMAT_MESSAGE_FROM_STRING 同时使用。
FORMAT_MESSAGE_FROM_STRING
lpSource参数是一个指向以NULL结尾的字符串,字符串包含一个消息定义,
这个消息定义可以包含 插入序列。此标志不能与 FORMAT_MESSAGE_FROM_HMODULE 、
FORMAT_MESSAGE_FROM_SYSTEM同时使用
FORMAT_MESSAGE_FROM_SYSTEM
0x00001000
函数会从系统信息列表中搜索所请求的信息。如果使用
FORMAT_MESSAGE_FROM_HMODULE,函数会先在lpSource指定
的模块中搜索请求的消息,如果搜索不到再去搜索系统消息表资源。此
标志不能与 FORMAT_MESSAGE_FROM_STRING同时使用。
FORMAT_MESSAGE_IGNORE_INSERTS
指定消息定义中的插入序列将被忽略,并将其直接传递给输出缓冲区。 此标志对于获取稍后格式化的消息很有用。 如果设置了此标志,则忽略Arguments参数。
lpSource
根据dwFlags标志而定。 [1]  
dwMessageId
请求的消息的标识符。当dwFlags标志为 FORMAT_MESSAGE_FROM_STRING时会被忽略。 [1]  
dwLanguageId
请求的消息的语言标识符。
LPTSTR lpBuffer
接收错误信息描述的缓冲区指针。
nSize
如果 FORMAT_MESSAGE_ALLOCATE_BUFFER标志没有被指定,这个参数必须指定为输出缓冲区的大小,如果指定,这个参数指定为分配给输出缓冲区的最小数。 [1]  
Arguments
保存格式化信息中的插入值的一个数组。

6.消息分流器:

对于系统带有的消息分流器HANDLE_MESSAGENAME,如HANDLE_WM_COMMAND、HANDLE_WM_HSCROLL等。本质上是使用函数指针实现。

#define HANDLE_WM_COMMAND(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (int)(LOWORD(wParam)), (HWND)(lParam), (UINT)HIWORD(wParam)), 0L)
#define HANDLE_WM_HSCROLL(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L)
#define HANDLE_WM_CREATE(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (LPCREATESTRUCT)(lParam)) ? 0L : (LRESULT)-1L)
使用:
LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
return HANDLE_WM_CREATE(hwnd, wParam, lParam, Cls_OnCreate);    //Cls_OnCreate(HWND hwnd, LPCREATESTRUCT lParam)
case WM_PAINT:
return HANDLE_WM_PAINT(hwnd, wParam, lParam, Cls_OnPaint);
case WM_DESTROY:
return HANDLE_WM_DESTROY(hwnd, wParam, lParam, Cls_OnDestroy);
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
在《Windows核心编程》中作者使用如下宏
// The normal HANDLE_MSG macro in WindowsX.h does not work properly for dialog
// boxes because DlgProc return 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))))

而在<windowsx.h>中有宏HANDLE_MSG

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

在chHANDLE_DLGMSG中实际上是调用SetDlgMsgResult实现:

#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))
SetWindowLongPtr函数功能:
该函数改变指定窗口的属性。函数也将指定的一个值设置在窗口的额外存储空间的指定偏移位置。
该函数用于取代SetWindowLong函数。如果您想要您编写的代码兼容32位和64位版本的Windows系统,请使用SetWindowLongPtr函数。

对于SetWindowLongPtr在ASCII下:

#define SetWindowLongPtr  SetWindowLongPtrA
#define SetWindowLongPtrA   SetWindowLongA
WINUSERAPI LONG WINAPI SetWindowLongA(
    _In_ HWND hWnd,            //窗口句柄,间接给出窗口所属的类。
    _In_ int nIndex,          //指定将设定的大于等于0的偏移值。有效值的范围从0到额外类的存储空间的字节数减去一个整型的大小(-sizeof(int))。
要设置其他任何值,可以指定下面值之一:
    _In_ LONG dwNewLong);    //指定的替换值。
nlndex 说明
GWL_EXSTYLE
设定一个新的扩展风格。更多信息,请见 CreateWindowEx
GWL_STYLE 设定一个新的窗口风格。
GWL_WNDPROC 为窗口过程设置一个新的地址。
GWL_HINSTANCE 设置一个新的应用程序实例句柄。
GWL_ID 设置一个新的窗口标识符。
GWL_USERDATA
设置与该窗口相关的用户数据。这些用户数据可以在程序创建该窗口时被使用。用户数据的初始值为0。
当hWnd参数标识了一个对话框时,也可使用下列值:
DWL_DLGPROC 设置对话框过程的新地址。
DWL_MSGRESULT 设置对话框中的消息处理程序的返回值。
DWL_USER 设置的应用程序所私有的新的额外信息,例如句柄或指针。
参考:

消息分流器:点击打开链接

SetWindowLongPtr:点击打开链接

DialogProc与WndProc的区别:

DialogProc與WndProc不同,前者要返回True或者False,後者成功時返回0。如果返回False,對話框管理器將處理消息。如果消息需要一個特定的返回值,必須在返回True之前調用SetWindowLong(hwndDlg, DWL_MSGRESULT, lResult)。

参考:点击打开链接



猜你喜欢

转载自blog.csdn.net/sinat_36391009/article/details/80754201
今日推荐