Windows 核心编程:1

终于开始读windows via C/C++ ,非常荣幸可以和大师Jeffrey Richter进行一次真正的心灵之旅。

[@more@]

终于开始读windows via C/C++ ,非常荣幸可以和大师Jeffrey Richter进行一次真正的心灵之旅。

1章对程序错误的处理

在开始介绍Microsoft Windows 的特性之前,必须首先了解Windows的各个函数是如何进行错误处理的。当调用一个Windows函数时,它首先要检验传递给它的的各个参数的有效性,然后再设法执行任务。如果传递了一个无效参数,或者由于某种原因无法执行这项操作,那么操作系统就会返回一个值,指明该函数在某种程度上运行失败了。表1 - 1列出了大多数Windows函数使用的返回值的数据类型。

1-1 Windows函数常用的返回值类型

数据类型

表示失败的值

VOID

该函数的运行不可能失败。Windows函数的返回值类型很少是VOID

BOOL

如果函数运行失败,那么返回值是0,否则返回的是非0值。最好对返回值进行测试,以确定它是0还是非0。不要测试返回值是否为TRUE

HANDLE

如果函数运行失败,则返回值通常是NULL,否则返回值为HANDLE,用于标识你可以操作的一个对象。注意,有些函数会返回一个句柄值INVALID_HANDLE_VALUE,它被定义为- 1。函数的Platform SDK文档将会清楚地说明该函数运行失败时返回的是NULL还是INVALID_ HANDLE_VALID

PVOID

如果函数运行失败,则返回值是NULL,否则返回PVOID,以标识数据块的内存地址

LONG / DWORD 这是个难以处理的值。返回数量的函数通常返回LONGDWORD。如果由于某种

原因,函数无法对想要进行计数的对象进行计数,那么该函数通常返回0- 1(根据

函数而定)。如果调用的函数返回了LONG / DWORD,那么请认真阅读Platform SDK

文档,以确保能正确检查潜在的错误

一个Windows函数返回的错误代码对了解该函数为什么会运行失败常常很有用。Microsoft公司编译了一个所有可能的错误代码的列表,并且为每个错误代码分配了一个32位的号码。从系统内部来讲,当一个Windows函数检测到一个错误时,它会使用一个称为线程本地存储器(thread-local storage)的机制,将相应的错误代码号码与调用的线程关联起来(线程本地存储器将在第2 1章中介绍)。这将使线程能够互相独立地运行,而不会影响各自的错误代码。当函数返回时,它的返回值就能指明一个错误已经发生。若要确定这是个什么错误请调用

DWORD GetLastError()

该函数只返回线程的3 2位错误代码。当你拥有3 2位错误代码的号码时,必须将该号码转换成更有用的某种对象。WinError.h头文件包含Microsoft公司定义的错误代码的列表。下面显示了该列表的某些内容,使你能够看到它的大概样子:

/************************************************************************

* *

* winerror.h -- error code definitions for the Win32 API functions *

* *

* Copyright (c) Microsoft Corp. All rights reserved. *

* *

************************************************************************/

#ifndef _WINERROR_

#define _WINERROR_

#if defined (_MSC_VER) && (_MSC_VER >= 1020) && !defined(__midl)

#pragma once

#endif

//

// Values are 32 bit values layed out as follows:

//

// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1

// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0

// +---+-+-+-----------------------+-------------------------------+

// |Sev|C|R| Facility | Code |

// +---+-+-+-----------------------+-------------------------------+

//

// where

//

// Sev - is the severity code

//

// 00 - Success

// 01 - Informational

// 10 - Warning

// 11 - Error

//

// C - is the Customer code flag

//

// R - is a reserved bit

//

// Facility - is the facility code

//

// Code - is the facility's status code

//

//

// Define the facility codes

//

#define FACILITY_WINDOWSUPDATE 36

#define FACILITY_WINDOWS_CE 24

#define FACILITY_WINDOWS 8

#define FACILITY_URT 19

#define FACILITY_UMI 22

#define FACILITY_SXS 23

#define FACILITY_STORAGE 3

#define FACILITY_STATE_MANAGEMENT 34

#define FACILITY_SSPI 9

#define FACILITY_SCARD 16

#define FACILITY_SETUPAPI 15

#define FACILITY_SECURITY 9

#define FACILITY_RPC 1

#define FACILITY_WIN32 7

#define FACILITY_CONTROL 10

#define FACILITY_NULL 0

#define FACILITY_METADIRECTORY 35

#define FACILITY_MSMQ 14

#define FACILITY_MEDIASERVER 13

#define FACILITY_INTERNET 12

#define FACILITY_ITF 4

#define FACILITY_HTTP 25

#define FACILITY_DPLAY 21

#define FACILITY_DISPATCH 2

#define FACILITY_DIRECTORYSERVICE 37

#define FACILITY_CONFIGURATION 33

#define FACILITY_COMPLUS 17

#define FACILITY_CERT 11

#define FACILITY_BACKGROUNDCOPY 32

#define FACILITY_ACS 20

#define FACILITY_AAF 18

//

// Define the severity codes

//

//

// 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.

如你所见,每个错误都有3种表示法:一个消息ID(这是你可以在源代码中使用的一个宏,以便与GetLastError的返回值进行比较),消息文本(对错误的英文描述)和一个号码(应该避免使用这个号码,可使用消息I D)。请记住,这里只显示了WinError.h头文件中的很少一部分内容,整个文件的长度超过21000行。当Windows函数运行失败时,应该立即调用GetLastError函数。如果调用另一个Windows函数,它的值很可能被改写。注意GetLastError能返回线程产生的最后一个错误。如果该线程调用的Windows函数运行成功,那么最后一个错误代码就不被改写,并且不指明运行成功。有少数Windows函数并不遵循这一规则,它会更改最后的错误代码;但是Platform SDK文档

通常指明,当函数运行成功时,该函数会更改最后的错误代码。

Windows OS 许多Windows OS的函数实际上是用Microsoft公司的16Windows产品产生的1 6位代码来实现的。这种比较老的代码并不通过GetLastError之类的函数来报告错误,而且Microsoft公司并没有在Windows OS中修改16位代码,以支持这种错误处理方式。对于我们来说,这意味着Windows OS中的许多Win32函数在运行失败时不能设置最后的错误代码。该函数将返回一个值,指明运行失败,这样你仅能够发现该函数确实已经运行失败,但是你无法确定运行失败的原因。

有些Windows函数之所以能够成功运行,其中有许多原因。例如,创建指明的事件内核对象之所以能够取得成功,是因为你实际上创建了该对象,或者因为已经存在带有相同名字的事件内核对象。你应搞清楚成功的原因。为了将该信息返回, Microsoft公司选择使用最后错误代码机制。这样,当某些函数运行成功时,就能够通过调用GetLastError函数来确定其他的一些信息。对于具有这种行为特性的函数来说, Platform SDK文档清楚地说明了GetLastError函数可以这样使用。请参见该文档,找出CreateEvent函数的例子。

进行调试的时候,监控线程的最后错误代码是非常有用的。在Microsoft Visual studio 2005中,Microsoft的调试程序支持一个非常有用的特性,即可以配置Watch窗口,以便始终都能显示线程的最后错误代码的号码和该错误的英文描述。

通过选定Watch窗口中的一行,并键入“@ err, hr”,就能够做到这一点。观察图1 - 1

diskpicpreview.cgi?UserNumber=8613751132180&SizeType=32&IsCut=0&FileGuId=3nw3vmmhgd81b&UserTag=aLL%2b5hdWwKwBsVaSk2wxmQ%3d%3d

Visual studio还配有一个小的实用程序,称为Error Lookup。可以使用Error Lookup如果在编写的应用程序中发现一个错误,可能想要向用户显示该错误的文本描述。Windows提供了一个函数,可以将错误代码转换成它的文本描述。该函数称为

FormatMessage,显示如下:

#undef FormatMessage

__inline

DWORD

FormatMessage(

DWORD dwFlags,

LPCVOID lpSource,

DWORD dwMessageId,

DWORD dwLanguageId,

LPTSTR lpBuffer,

DWORD nSize,

va_list *Arguments

)

{

#ifdef UNICODE

return FormatMessageW(

#else

return FormatMessageA(

#endif

dwFlags,

lpSource,

dwMessageId,

dwLanguageId,

lpBuffer,

nSize,

Arguments

);

}

FormatMessage函数的功能实际上是非常丰富的,在创建向用户显示的字符串信息时,它是首选函数。该函数之所以有这样大的作用,原因之一是它很容易用多种语言进行操作。该函数能够检测出用户首选的语言(在Regional Settings Control Panel小应用程序中设定),并返回相应的文本。当然,首先必须自己转换字符串,然后将已转换的消息表资源嵌入你的. e x e文件或D L L模块中,然后该函数会选定正确的嵌入对象。ErrorShow示例应用程序(本章后面将加以介绍)展示了如何调用该函数,以便将Microsoft公司定义的错误代码转换成它的文本描述。有些人常常问我,Microsoft公司是否建立了一个主控列表,以显示每个Windows函数可能返回的所有错误代码。可惜,回答是没有这样的列表,而且Microsoft公司将永远不会建立这样的一个列表。

因为在创建系统的新版本时,建立和维护该列表实在太困难了。建立这样一个列表存在的问题是,你可以调用一个Windows函数,但是该函数能够在内部调用另一个函数,而这另一个函数又可以调用另一个函数,如此类推。由于各种不同的原因,这些函数中的任何一个函数都可能运行失败。有时,当一个函数运行失败时,较高级的函数对它进行恢复,并且仍然可以执行你想执行的操作。为了创建该主控列表, Microsoft公司必须跟踪每个函数的运行路径,并建立所有可能的错误代码的列表。这项工作很困难。而且,当创建系统的新版本时,这些函数的运行路径还会改变。

1.1 定义自己的错误代码

前面已经说明Windows函数是如何向函数的调用者指明发生的错误,你也能够将该机制用于自己的函数。比如说,你编写了一个希望其他人调用的函数,你的函数可能因为这样或那样的原因而运行失败,你必须向函数的调用者说明它已经运行失败。若要指明函数运行失败,只需要设定线程的最后的错误代码,然后让你的函数返回FALSEINVALID_HANDLE_VALUENULL或者返回任何合适的信息。若要设定线程的最后错误代码,只需调用下面的代码:

Void SetLastError(DWORD dwErrorCode);

请将你认为合适的任何32位号码传递给该函数。尝试使用WinError.h中已经存在的代码,只要该代码能够正确地指明想要报告的错误即可。如果你认为WinError.h中的任何代码都不能正确地反映该错误的性质,那么可以创建你自己的代码。错误代码是个3 2位的数字,划分成表

1-2 所示的各个域。

1-2 错误代码的域

31-30

29

28

27-16

15-0

内容

含义

严重性

0 =成功

1 =供参考

2=警告

3=错误

Microsoft客户

0=Microsoft公司定义代码

1=客户定义代码

保留

必须是0

备代码

Microsoft定义

异常代码

Microsoft客户定义

这些域将在第2 4章中详细讲述。现在,需要知道的重要域是第29位。Microsoft公司规定,他们建立的所有错误代码的这个信息位均使用0。如果创建自己的错误代码,必须使29位为1。这样,就可以确保你的错误代码与Microsoft公司目前或者将来定义的错误代码不会发生冲突。

1.2 ErrorShow示例应用程序

ErrorShow应用程序“01-ErrorShow.exe

diskpicpreview.cgi?UserNumber=8613751132180&SizeType=32&IsCut=0&FileGuId=ael86nhf97ks

可以将任何错误代码键入该编辑控件。当单击Look up

钮时,在底部的滚动窗口中就会显示该错误的文本描述。该

应用程序唯一令人感兴趣的特性是如何调用FormatMessage函数。下面是使用该函数的方法:

// 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);

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

第一个代码行用于从编辑控件中检索错误代码的号码。然后,建立一个内存块的句柄并将它初始化为NULL

FormatMessage函数在内部对内存块进行分配,并将它的句柄返回给我们。

当调用FormatMessage函数时,传递了FORMAT_MESSAGE_FROM_SYSTEM标志。该标志告诉FormatMessage函数,我们想要系统定义的错误代码的字符串。

还传递了FORMAT_MESSAGE_ALLOCATE_BUFFER标志,告诉该函数为错误代码的文本描述分配足够大的内存块。

该内存块的句柄将在hlocal变量中返回。第三个参数指明想要查找的错误代码的号码,第四个参数指明想要文本描述使用什么语言。

如果FormatMessage函数运行成功,那么错误代码的文本描述就位于内存块中,将它拷贝到对话框底部的滚动窗口中。

如果FormatMessage函数运行失败,设法查看NetMsg.dll模块中的消息代码,以了解该错误是否与网络有关。使用NetMsg.dll 模块的句柄,再次调用FormatMessage函数。你会看到,每个DLL(或. e x e)都有它自己的一组错误代码,可以使用Message CompilerM C . e x e)将这组错误代码添加给该模块,并将一个资源添加给该模块。这就是Visual StudioError Lookup工具允许你用Modules对话框进行的操作。以下是清单1 - 1E r r o r S h o w示例应用程序。

清单1-1 ErrorShow示例应用程序:

/******************************************************************************

Module: ErrorShow.cpp

Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre

******************************************************************************/

#include "..CommonFilesCmnHdr.h" /* See Appendix A. */

#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);

// 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);

Free

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/16916129/viewspace-1014146/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/16916129/viewspace-1014146/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值