《Windows核心编程》读书笔记一 错误处理

第一章 错误处理


所有范例代码可以从 http://www.wintellect.com/books.aspx下载 (改网址已经不可用)


网盘:https://pan.baidu.com/s/1nv1HxxB 


本章内容

定义自己的错误代码

ErrorShow实例程序


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

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

BOOL 如果函数失败,返回0.否则返回一个非0值。避免测试返回值为TRUE应该测试返回是否FALSE

HANDLE 如果函数失败,通常返回NULL,否则HANDLE将返回一个可操作的对象。但是某些函数会返回 INVALID_HANDLE_VALUE(-1)

PVOID 如果调用失败,返回NULL;否之指向一个内存地址

LONG/DWORD (可能返回0或者-1) 具体要参阅MSDN


通常情况下Windows函数能返回错误代码,将有助于我们理解函数调用为什么会失败。

Windows采用Thread Local Storage机制保存一个跟线程相关的错误代码,可以调用GetLastError获取

通常的错误信息定义与WinError.h中


摘录部分

//
// MessageId: ERROR_SUCCESS
//
// MessageText:
//
// The operation completed successfully.
//
#define ERROR_SUCCESS                    0L

#define NO_ERROR 0L                                                 // dderror
#define SEC_E_OK                         ((HRESULT)0x00000000L)

//
// MessageId: ERROR_INVALID_FUNCTION
//
// MessageText:
//
// Incorrect function.
//
#define ERROR_INVALID_FUNCTION           1L    // dderror

//
// MessageId: ERROR_FILE_NOT_FOUND
//
// MessageText:
//
// The system cannot find the file specified.
//
#define ERROR_FILE_NOT_FOUND             2L

//
// MessageId: ERROR_PATH_NOT_FOUND
//
// MessageText:
//
// The system cannot find the path specified.
//
#define ERROR_PATH_NOT_FOUND             3L

//
// MessageId: ERROR_TOO_MANY_OPEN_FILES
//
// MessageText:
//
// The system cannot open the file.
//
#define ERROR_TOO_MANY_OPEN_FILES        4L

//
// MessageId: ERROR_ACCESS_DENIED
//
// MessageText:
//
// Access is denied.
//
#define ERROR_ACCESS_DENIED              5L

每个错误有3种表示:一个消息ID(一个宏),消息文本(消息的描述) 和一个编号(避免使用此编号使用错误的ID宏)


在Windows函数调用失败以后,应该马上调用GetLastError。否则调用另一个Windows函数可能会改写此值。


例如CreateEvent,如果已经存在此具名事件,GetLastError返回ERROR_ALREADY_EXISTS


在VISUAL Studio中可以配置Watch 输入 $err,hr .可以实施监控windows api的执行返回情况

《Windows核心编程》读书笔记一 错误处理_第1张图片


VS还搭载了一个小的使用程序,名为Error lookup。可以讲错误代码转换为文本 VS->Tools->Error Lookup启动该应用(VS 2013中)

《Windows核心编程》读书笔记一 错误处理_第2张图片


如果要像用户显示错误的意思。有一个FormatMessage函数

FormatMessageW(
    _In_     DWORD dwFlags,
    _In_opt_ LPCVOID lpSource,
    _In_     DWORD dwMessageId,
    _In_     DWORD dwLanguageId,
    _Out_    LPWSTR lpBuffer,
    _In_     DWORD nSize,
    _In_opt_ va_list *Arguments
    );

而且他支持多语言。直接使用不同国家的语言向用户提示错误。具体应用参见后面的例子。



1.1 定义自己的错误代码

windows的这种错误代码机制也可以用于自己的函数中。

在线程上设置错误代码 VOID SetLastError(DWORD dwErrorCode);

错误代码是一个32位数 


错误代码的不同字段


后续章节再详细讨论错误代码的含义。 注意29位。 windows api此位设置为0, 客户定义的代码设置为1.

Facility代码可以容纳4096个错误,前256个值为Microsoft保留的,其余可由我们自己ID应用程序来定义。


1.2 ErrorShow示例程序

演示了一个如何显示错误消息的例子

ErrorShow.cpp

/*
	Module:	ErrorShow.cpp
	Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre
*/

#include "..\CommonFiles\CmnHdr.h"
#include 
#include 
#include "resource.h"

///////////////////////////////////////////////////////////////////////////////

#define ESM_POKECODEANDLOOKUP		(WM_USER + 100)
const TCHAR g_szAppName[] = TEXT("Error Show");

///////////////////////////////////////////////////////////////////////////////

BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam){
	chSETDLGICONS(hwnd, IDI_ERRORSHOW);

	// don't accept error codes more than 5 digits long
	Edit_LimitText(GetDlgItem(hwnd, IDC_ERRORCODE), 5);

	// look up the command-line passed error number
	SendMessage(hwnd, ESM_POKECODEANDLOOKUP, lParam, 0);
	return (TRUE);
}

///////////////////////////////////////////////////////////////////////////////

void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify){
	switch (id){

	case IDCANCEL:
		EndDialog(hwnd, id);
		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(hwndCtl) > 0);
		break;

	case IDOK:
		// Get the error code
		DWORD dwError = GetDlgItemInt(hwnd, IDC_ERRORCODE, NULL, FALSE);

		HLOCAL hlocal = NULL;		// Buffer that gets the error message string

		// Use the default system locale since we look for windows messages.
		// Note: this MAKELANGID combination has 0 as value
		DWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
		//DWORD systemLocale = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);

		// Get the error code's textual description
		BOOL fOk = FormatMessage(
			FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
			FORMAT_MESSAGE_ALLOCATE_BUFFER,
			NULL, dwError, systemLocale,
			(PTSTR) &hlocal, 0, NULL);
		
		if (!fOk) {
			// Is it a network-related error?
			HMODULE hDll = LoadLibraryEx(TEXT("netmsg.dll"), NULL,
				DONT_RESOLVE_DLL_REFERENCES);

			if (hDll != NULL) {
				fOk = FormatMessage(
					FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS |
					FORMAT_MESSAGE_ALLOCATE_BUFFER,
					hDll, dwError, systemLocale,
					(PTSTR)&hlocal, 0, NULL);
				FreeLibrary(hDll);
			}
		}

		if (fOk && (hlocal != NULL)) {
			SetDlgItemText(hwnd, IDC_ERRORTEXT, (PCTSTR)LocalLock(hlocal));
			LocalFree(hlocal);
		}
		else {
			SetDlgItemText(hwnd, IDC_ERRORTEXT,
				TEXT("No text found for this error number."));
		}

		break;
	}
}

///////////////////////////////////////////////////////////////////////////////
INT_PTR	WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	
	switch (uMsg) {
		chHANDLE_DLGMSG(hwnd, WM_INITDIALOG,	Dlg_OnInitDialog);
		chHANDLE_DLGMSG(hwnd, WM_COMMAND,		Dlg_OnCommand);

	case ESM_POKECODEANDLOOKUP:
		SetDlgItemInt(hwnd, IDC_ERRORCODE, (UINT)wParam, FALSE);
		FORWARD_WM_COMMAND(hwnd, IDOK, GetDlgItem(hwnd, IDOK), BN_CLICKED,
			PostMessage);
		SetForegroundWindow(hwnd);
		break;
	}

	return (FALSE);
}

///////////////////////////////////////////////////////////////////////////////
int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int) {
	HWND hwnd = FindWindow(TEXT("#32770"), TEXT("Error Show"));
	if (IsWindow(hwnd)) {
		// An instance is already running, activate it and send it the new #
		SendMessage(hwnd, ESM_POKECODEANDLOOKUP, _ttoi(pszCmdLine), 0);
	}
	else {
		DialogBoxParam(hinstExe, MAKEINTRESOURCE(IDD_ERRORSHOW),
			NULL, Dlg_Proc, _ttoi(pszCmdLine));
	}
}

运行结果

《Windows核心编程》读书笔记一 错误处理_第3张图片


FormatMessage函数

传入FORMAT_MESSAGE_FROM_SYSTEM 获取系统定义的错误代码对应的字符串

FORMAT_MESSAGE_ALLOCATE_BUFFER,要求函数分配一块足矣容纳文本描述的内存。存于hlocal

FORMAT_MESSAGE_IGNORE_INSERTS 允许获得占位符信息。例如

《Windows核心编程》读书笔记一 错误处理_第4张图片


LANG_NEUTRAL 和 SUBLANG_NEUTRAL 组合表示系统默认语言。

这种情况下,我们不能硬编码一种特定的语言,因为不知道操作系统安装的语言是什么。


如果FormatMessage成功会返回获取的字符串,若失败在搜索是否是网络消息netmsg.dll

每个dll都有自己的错误代码,可以像自己的模块添加代码。使用Message Compiler(MC.exe)创建消息资源并添加到dll中。

你可能感兴趣的:(Windows)