Windows核心编程学习二:错误码的文本描述

注:源码为学习《Windows核心编程》的一些尝试,非原创。若能有助于一二访客,幸甚。

1.设置图标

/*******************************************************************************
 * File:	ErrorShow.cpp
 * Author:	guzhoudiaoke@126.com
 * Time:	2013-04-15
 * 描述:	修改试验原书同名程序
 *******************************************************************************/

#include <Windows.h>
#include <Windowsx.h>
#include <tchar.h>
#include "Resource.h"


/* SetDlgMsgResult This macro maps to the SetWindowLong function.
 * SetWindowLong changes an attribute of the specified window, also sets a 32-bit (LONG) 
 * value at the specified offset into the extra window memory of a window.
 * The normal HANDLE_MSG macro in WindowsX.h does not work properly for dialog
 * boxes because DlgProc returns a BOOL instead of an LRESULT (likeWndProcs). 
 * 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))))

// Sets the dialog box icons
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)));
}

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

void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
	switch (id) {
		case IDCANCEL:
			EndDialog(hwnd, id);
			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);
	}

	return FALSE;
}


int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int)
{
	DialogBoxParam(hinstExe, MAKEINTRESOURCE(IDD_ERRORSHOW), NULL, Dlg_Proc, _ttoi(pszCmdLine));
	return 0;
}

需要制作图标资源,ID为IDI_ERRORSHOW如下图所示:


运行结果:


此时的按键并不起作用。

2.限制编辑框中文本长度

上面的编辑框中可以接受任意长度的文本,而错误码一般不会超过5位,我们设置编辑框的文本长度限制来使文本长度不超过5位
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);

   return(TRUE);
}

3.获取编辑框中输入的数字,转换成文本显示

void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
	switch (id) {
		case IDCANCEL:
			EndDialog(hwnd, id);
			break;
		
		case IDOK:
			// Get the error code
			DWORD dwError = GetDlgItemInt(hwnd, IDC_ERRORCODE, NULL, FALSE);

			// 定义缓存存储错误码转换成的文本
			TCHAR chErrorText[6];
			_itot_s(dwError, chErrorText, 6, 10);
		
			// 显示文本
			SetDlgItemText(hwnd, IDC_ERRORTEXT, chErrorText);
			break;
	}
}


4.尝试使用HLOCAL和FormatMessage

HLOCAL:

Handle to a local memory block. This type is declared in WinDef.h as follows:

typedef HANDLE HLOCAL;

FormatMessage:

This function formats a message string. 

DWORD FormatMessage(
	DWORD dwFlags, LPCVOID
	LPCVOID lpSource, DWORD
	DWORD dwMessageId, DWORD
	DWORD dwLanguageId, LPTSTR
	LPTSTR lpBuffer, DWORD
	DWORD nSize, va_list*
	va_list* Arguments
);



void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
	switch (id) {
		case IDCANCEL:
			EndDialog(hwnd, id);
			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
			// LANG_NEUTRAL, SUBLANG_NEUTRAL为两个特定的常量,用来生成语言标识符
			// 这两个常量联合到一起讲生成一个0值,即操作系统的默认语言
			DWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);

			// FORMAT_MESSAGE_FROM_SYSTEM告诉FormatMessage我们希望获得一个系统定义的错误码对应的字符串
			// FORMAT_MESSAGE_ALLOCATE_BUFFER要求该函数分配一块足以容纳错误文本的内存,该块内存在hlocal中返回
			// FORMAT_MESSAGE_IGNORE_INSERTS允许我们获得含有%占位符的消息,以提供更多上下文相关信息
			// 第二个参数Pointer to the location of the message definition. 
			// 第三个参数指出想要查找的错误代码
			// 第四个参数指出要用什么语言来显示文本描述
			// 第五个参数Pointer to a buffer for the formatted (and null-terminated) message
			// 第六个参数If the FORMAT_MESSAGE_ALLOCATE_BUFFER flag is not set, this parameter specifies 
			// the maximum number of characters that can be stored in the output buffer. 
			// If FORMAT_MESSAGE_ALLOCATE_BUFFER is set, this parameter specifies the minimum number of 
			// bytes or characters to allocate for an output buffer. 
			// 第七个参数Pointer to an array of 32-bit values that are used as insert values in the formatted message. 
			// A %1 in the format string indicates the first value in the Arguments array; 
			// a %2 indicates the second argument; and so on. 
			BOOL fOk = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | 
				FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, dwError, systemLocale, (PTSTR)&hlocal, 0, NULL);

			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;
	}
}


5.尝试在NetMsg.dll模块中查找消息代码

如果前面的FormatMessage调用失败,我们尝试在NetMsg.dll模块中查找消息代码,看错误是否与网络有关。每个DLL或.exe都可以有自己的一套错误代码。
void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
	switch (id) {
		case IDCANCEL:
			EndDialog(hwnd, id);
			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
			// LANG_NEUTRAL, SUBLANG_NEUTRAL为两个特定的常量,用来生成语言标识符
			// 这两个常量联合到一起讲生成一个0值,即操作系统的默认语言
			DWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);

			// FORMAT_MESSAGE_FROM_SYSTEM告诉FormatMessage我们希望获得一个系统定义的错误码对应的字符串
			// FORMAT_MESSAGE_ALLOCATE_BUFFER要求该函数分配一块足以容纳错误文本的内存,该块内存在hlocal中返回
			// FORMAT_MESSAGE_IGNORE_INSERTS允许我们获得含有%占位符的消息,以提供更多上下文相关信息
			// 第二个参数Pointer to the location of the message definition. 
			// 第三个参数指出想要查找的错误代码
			// 第四个参数指出要用什么语言来显示文本描述
			// 第五个参数Pointer to a buffer for the formatted (and null-terminated) message
			// 第六个参数If the FORMAT_MESSAGE_ALLOCATE_BUFFER flag is not set, this parameter specifies 
			// the maximum number of characters that can be stored in the output buffer. 
			// If FORMAT_MESSAGE_ALLOCATE_BUFFER is set, this parameter specifies the minimum number of 
			// bytes or characters to allocate for an output buffer. 
			// 第七个参数Pointer to an array of 32-bit values that are used as insert values in the formatted message. 
			// A %1 in the format string indicates the first value in the Arguments array; 
			// a %2 indicates the second argument; and so on. 
			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;
	}
}

6.当文本框中没有输入任何内容时,不允许按下按钮



可以发现当没有任何输入时,仍然可以按下检查按钮,而显示的是错误码为0的文本。
使用下面的函数检查是否有输入,当无输入时,不允许按下按钮,即按钮禁用。

void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
	switch (id) {
		case IDCANCEL:
			EndDialog(hwnd, id);
			break;

		case IDC_ERRORCODE:
			EnableWindow(GetDlgItem(hwnd, IDOK), Edit_GetTextLength(hwndCtl) > 0);
			break;



7.设置一个初始值

即当程序开始运行时给错误码编辑框一个初始值0,并模拟按下检查按钮,显示错误码0 的消息文本。
首先定义一个消息:
#define ESM_POKECODEANDLOOKUP    (WM_USER + 100)
然后在初始化对话框函数中添加发送消息的命令:
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);
}

然后是对该消息的响应:
// 对话框过程函数
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);

			// This function puts the thread that created the specified window into the foreground and activates the window. 
			SetForegroundWindow(hwnd);

			break;
	}

	return FALSE;
}



8.设置对话框总是置顶,即在所有其他窗口的上面

void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
	switch (id) {
		case IDCANCEL:
			EndDialog(hwnd, id);
			break;

		case IDC_ERRORCODE:
			EnableWindow(GetDlgItem(hwnd, IDOK), Edit_GetTextLength(hwndCtl) > 0);
			break;

		case IDC_ONTOP:
			//Sets the size, position, and Z order of the control site. 
			SetWindowPos(hwnd, IsDlgButtonChecked(hwnd, IDC_ALWAYSONTOP) ? HWND_TOPMOST : HWND_NOTOPMOST, 
				0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
			break;
		

9.只能启动一个实例

我们可以发现上面的实现可以启动应用程序的多个实例

可以做如下改变:
int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int)
{
	HWND hwnd = FindWindow(TEXT("#32770"), TEXT("错误代码文本描述"));
	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));
	}
	return 0;
}

The FindWindow function retrieves a handle to the top-level window whose class name and window name match the specified strings. This function does not search child windows. This function does not perform a case-sensitive search.


10.最终完整代码

/*******************************************************************************
 * File:	ErrorShow.cpp
 * Author:	guzhoudiaoke@126.com
 * Time:	2013-04-15
 * 描述:	修改试验原书同名程序
 *******************************************************************************/

#include <Windows.h>
#include <Windowsx.h>
#include <tchar.h>
#include "Resource.h"

// Always compiler using Unicode.
#ifndef UNICODE
	#define UNICODE
#endif


#define ESM_POKECODEANDLOOKUP    (WM_USER + 100)


/* SetDlgMsgResult This macro maps to the SetWindowLong function.
 * SetWindowLong changes an attribute of the specified window, also sets a 32-bit (LONG) 
 * value at the specified offset into the extra window memory of a window.
 * The normal HANDLE_MSG macro in WindowsX.h does not work properly for dialog
 * boxes because DlgProc returns a BOOL instead of an LRESULT (likeWndProcs). 
 * 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))))


// Sets the dialog box icons
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)));
}


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_ERRORCODE:
			EnableWindow(GetDlgItem(hwnd, IDOK), Edit_GetTextLength(hwndCtl) > 0);
			break;

		case IDC_ONTOP:
			//Sets the size, position, and Z order of the control site. 
			SetWindowPos(hwnd, IsDlgButtonChecked(hwnd, IDC_ONTOP) ? HWND_TOPMOST : HWND_NOTOPMOST, 
				0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
			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
			// LANG_NEUTRAL, SUBLANG_NEUTRAL为两个特定的常量,用来生成语言标识符
			// 这两个常量联合到一起讲生成一个0值,即操作系统的默认语言
			DWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);

			// FORMAT_MESSAGE_FROM_SYSTEM告诉FormatMessage我们希望获得一个系统定义的错误码对应的字符串
			// FORMAT_MESSAGE_ALLOCATE_BUFFER要求该函数分配一块足以容纳错误文本的内存,该块内存在hlocal中返回
			// FORMAT_MESSAGE_IGNORE_INSERTS允许我们获得含有%占位符的消息,以提供更多上下文相关信息
			// 第二个参数Pointer to the location of the message definition. 
			// 第三个参数指出想要查找的错误代码
			// 第四个参数指出要用什么语言来显示文本描述
			// 第五个参数Pointer to a buffer for the formatted (and null-terminated) message
			// 第六个参数If the FORMAT_MESSAGE_ALLOCATE_BUFFER flag is not set, this parameter specifies 
			// the maximum number of characters that can be stored in the output buffer. 
			// If FORMAT_MESSAGE_ALLOCATE_BUFFER is set, this parameter specifies the minimum number of 
			// bytes or characters to allocate for an output buffer. 
			// 第七个参数Pointer to an array of 32-bit values that are used as insert values in the formatted message. 
			// A %1 in the format string indicates the first value in the Arguments array; 
			// a %2 indicates the second argument; and so on. 
			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("没有发现该错误码的消息文本"));
			}
		
			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);

			// This function puts the thread that created the specified window into the foreground and activates the window. 
			SetForegroundWindow(hwnd);

			break;
	}

	return FALSE;
}


int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int)
{
	HWND hwnd = FindWindow(TEXT("#32770"), TEXT("错误代码文本描述"));
	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));
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值