文档库 最新最全的文档下载
当前位置:文档库 › Windows核心编程读书笔记

Windows核心编程读书笔记

Windows核心编程读书笔记
Windows核心编程读书笔记

第一部分程序员必读

Szq整理使用

第1章对程序错误的处理

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

VOID:无返回值型,该函数的运行不可能失败。Windows函数很少此类型BOOL:函数运行失败则返回0,否则返回非0

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文档以确保能正确检查潜在的错误。

(2)当某Windows函数运行错误时,可以通过调用DWORD GetLast Error()函数获取调用该函数的关联线程的32位错误代码。其具体的错误文本以列表形式存放于头文件中,在VC中调试时,也可以通过在Watch窗口键入“@err,hr”来获取所调用函数的运行错误代码和具体的错误文本。

(3)Windows还提供了一个函数,可以将错误代码转换成它的文本描述。该函数称为FormatMessage,该函数的格式如下:

DWORD FormatMessage(

DWORD dwFlags,

LPCVOID pSource,

DWORD dwMessageID,

DWORD dwLanguageID,

PTSTR pszBuffer,

DWORD nSize,

va_list *Argument

);

2004年11月8号

Trackback: 第一章程序员必读

定义自己的错误代码

若你编写了一个希望其他人调用的函数,你的函数可能因为这样或那样的原因而运行失败,你必须向函数的调用者说明它已经运行失败。若要指明函数运行失败,只需要设定线程的最后的错误代码,然后让你的函数返回FALSE、INVALID_HANDLE_VALUE、NULL或者返回任何合适的信息。可以用Viod SetLastError(DWORD dwErrCode)设定线程的最后错误代码;

ErrorShow示例小程序

FormatMessage函数的用法

.........

if (hloca != NULL)

{

SetDlgItemText(hwnd , IDC_ERRORTEXT , (PCTSTR) LocalLock(hlocal) ); LocalFree(hlocal);

}

else

{

SetDlgItemText(hwnd , IDC_ERRORTEXT , TEXT("Error number not found") );

}

当调用FormatMessage函数时,传递了FORMAT_MESSAGE_FROM_SYSTEM标志。该标志告诉FormatMessage函数,我们想要系统定义的错误代码的字符串。还传递FORMAT_MESSAGE_ALLOCATE_BUFFER标志,告诉该函数为错误代码的文本描述分配足够大的内存块。该内存块的句柄将hlocal变量中返回。第三个参数指明想要查找的错误代码的号码,第四个参数指明想要文本描述使用什么语言。

2004年11月9日

第二章Unicode

Unicode-宽字节字符集是是为了解决软件本地化(多语言版本化)而定制的一项技术标准。Unicode字符串中的所有字符都是16位的(两个字节),程序员只需要对指针进行递增或者递减,就可以遍历字符串中的各个字符,不在需要像单字节字符一样去判断下一字节是属于同一字符的组成部分还是一个新字符。

使用Unicode,有几个好处,可以很容易地在不同语言之间进行数据交换、使你能够分配支持所有语言的单个二进制.exe文件或DLL文件、提高应用程序的运行效率。

各Windows操作系统对Unicode的支持:

Windows 2000既支持Unicode,也支持ANSI,因此可以为任意一种开发应用程序。

Windows 98只支持ANSI,只能为ANSI开发应用程序。

Windows CE只支持Unicode,只能为Unicode开发应用程序。

因为COM通常用于使不同的组件能够互相进行通信,而Unicode则是传递字符串的最佳手段。所以需要字符串的所有COM接口方法都只能接受Unicode字符串。

如何编写Unicode源代码

C对Unicode的支持

定义一个名字为wchar_t的数据类型,它便是一个Unicode字符的数据类型。For example,如果想要创建一个缓存,用于存放最多为99个字符的Unicode字符串和一个结尾为零的字符,可以使用下面这个语句:

wchar_t szBuffer[100];

当然,C里面的字符串函数,如strcpy、strchr、strcat等,只能对ANSI字符串进行操作,不能正确处理Unicode,因此,ANSI C补充了一组函数:

ANSI: char * strchr(const char * , int);

Unicode: wchar_t * wcschr(const wchar_t * , wchar_t);

ANSI: int strcmp(const char * , const char *);

Unicode: int wcscmp(const wchar_t * , const wchar_t *);

ANSI: char * strcpy(char * , const char *);

Unicode: wchar_t * wcscpy(wchar_t * , const wchar_t *);

ANSI: size_t strlen(const char *);

Unicode size_t wcslen(const wchar_t *);

请注意,所有的Unicode函数均以wcs开头,wcs是宽字符串的英文缩写。若要调用Unicode函数,只需用前缀wcs来取代ANSI字符串函数的前缀str即可。

一般情况下,对ANSI和Unicode字符操作的函数不要写在同一个源代码文件中,这会给编译器编译带来很多麻烦(编译错误),但实在有必要的时候,也可以将他们放在同一个源代码文件中,这时候就需要包含一个头文件。

头文件的唯一作用是就是帮助创建ANSI/Unicode通用的源代码文件。它的工作机制是,通过一组宏,来决定调用的是str函数还是wcs函数。For example ,在中定义有一个宏为_tcscpy,如果在包含该头文件时没有定义_UNICODE,那

_tcscpy就想当于ANSI的strcpy,如果定义了_UNICODE,则_tcscpy想当于wcscpy 函数。

还有一个值得注意的是,使用了中的宏的时候,若要生成一个Unicode字符串而不是一个ANSI字符串,则必须在字符串前加上一个大写字符L,For example :TCHAR * szError =L"Error";

大写字母L的用意是告诉编译器,该字符串应该作为Unicode字符来编译。此举随之带来的问题是,我们还需要定义个宏来动态添加大写字母L,以适应Unicode/ANSI通用源代码文件。这个宏便是_TEXT。

TCHAR *szError = _TEXR("Error");

如上这样定义的话,就不论源代码文件中是否定义了_UNICODE,编译器都能够正确辨认并编译。此外,_TEXT宏还可以用于检验字符串的首字母。For example :

if(szError[0] ==_TEXT('J'))

{

文本串视为字符数组,而不是chars数组或字节数组。

2.将通用数据类型(如TCHAR和PTSTR)用于文本字符和字符串。

3.将显式数据类型(如BYTE和PBYTE)用于字节、字节指针和数据缓存。

4.将TEXT宏用于原义字符和字符串。

5.执行全局性替换(例如用PTSTR替换PSTR)。

6.修改字符串运算问题。例如函数通常希望你在字符中传递一个缓存的大小,而不是字节。这意味着你不应该传递sizeof(szBuffer) ,而应该传递

(sizeof(szBuffer)/sizeof(TCHAR )。另外,如果需要为字符串分配一个内存块,并且拥有该字符串中的字符数目,那么请记住要按字节来分配内存。这就是说,应该调用malloc (nCharacters *sizeof(TCHAR)), 而不是调用

malloc(nCharacters)。在上面所说的所有原则中,这是最难记住的一条原则,如果操作错误,编译器将不发出任何警告。

Windwos提供了如下一组对Unicode字符串进行操作的函数:

lstrcat 将一个字符串置于另一个字符串的结尾处

lstrcmp 对两个字符串进行区分大小写的比较

lstrcmpi 对两个字符串进行不区分大小写的比较

lstrcpy 将一个字符串拷贝到内存中的另外一个位置

lstrlen 返回字符串的长度

这些函数都是作为宏来实现的,即是说它们可作为Unicode/ANSI的通用版本,会根据是否定义了UNICODE而自动扩展。

Windows提供了两个函数,以便转换Unicode字符串的大小写字母:

PTSTR CharLower(PTSTR pszString);

PTSTR CharUpper(PTSTR pszString);

既可以转换单个字符,也可以转换以0结尾的整个字符串。若要转换整个字符串,只需要传递字符串的地址即可。若要转换单个字符,则必须像下面这样传递各个字符:

TCHAR cLowerCaseChar = CharLower(PTSTR) szString[0];

将单个字符转换成一个PTSTR,便可调用该函数,将一个值传递给它,在这个值中,较低的16位包含了该字符,较高的16位包含0。当该函数看到较高位是0时,该函数就知道你想要转换单个字符,而不是整个字符串。返回的值是个32位值,较低的16位中是已经转换的字符。

当资源编译器对你的所有资源进行编译时,输出文件是资源的二进制文件。资源(字符串表、对话框模板和菜单等)中的字符串值总是写Unicode字符串。Windows用IsTextUnicode函数来区分所打开文本是ANSI字符还Unicode字符。DWORD IsTextUnicode(CONST PVOID pvBuffer , int cb , PINT pResult);

文本文件存在的问题是,它们的内容没有严格和明确的规则,因此很难确定该文件是包含ANSI字符还是Unicode字符。IsTextUnicode使用一系列统计方法和定性方法,以便猜测缓存的内容。由于这不是一种确切的科学方法,因此IsTextUnicode有可能返回不正确的结果。

2004年11月11日

Trackback: 第三章内核对象

一个内核对象就是一个由内核分配的内存块,该内存块是一种数据结构,只能由该内核访问。它的成员负责维护该对象的各种信息,有些数据成员是所有对象类型中相同的,但大多数数据成员属于特定的对象类型。比如存取符号对象、事件对象、文件对象、文件映射对象、I/O完成端口对象、作业对象、信箱对象、互斥对象、管道对象、进程对象、信标对象、线程对象和等待计时器对象等。这些都是一个内核对象。

由于内核对象的数据结构只能被内核访问,因此应用程序无法在内存中找到这些数据结构并直接改变它们的内容,只能通过Windows提供的一组函数来对待这些结构进行操作。

内核对象的使用计数

内核对象由内核所拥有,而不是由进程所拥有。换句话说,内核对象不一定因创建它的进程的终止而撤消。内核对象的存在时间可以可以比创建该对象的进程长。

内核通过每个对象类型常用的数据成员--使用计数,来确认有多少个进程正在使用某个内核对象。对象刚创建时,该对象的使用计数被置为1,随后就随着进程的访问递增,随着进程的终止递减。如果内核对象的使用计数降为0,内核就撤消该对象,以确保在没有进程引用该对象时系统中不保留任何内核对象。

安全性

内核对象能够得到安全描述符的保护。安全描述符用于描述谁创建了该对象,谁能够访问或使用该对象,谁无权访问该对象。安全描述符通常在编写服务器应用程序时使用,如果你编写客户机端的应用程序,那么可以忽略内核对象的这个特性。

用于创建内核对象的函数几乎都有一个指向SECURITY_ATTRIBUTES结构的指

针作为其参数。大多数应用程序只是为该参数传递NULL,这样就可以创建带有默认安全性的内核对象。默认安全性意味着对象的管理小组的任何成员和对象的创建者都拥有对该对象的全部访问权,而其他所有人均无权访问该对象。但是,可以指定一个S ECURITY_ ATTRIBUTES结构,对它进行初始化,并为该参数传递该结构的地址。SECU RITY_ATTRIBUTES结构类似下面的样子:

typedef struct_SECURITY_ATTRIBUTES{

DWORD nLength;

LPVOID lpSecurityDescriptor;

BOOL bInheritHandle;

}SECURITY_ATTRIBUTES;

该结构中包含的与安全性有关的成员实际上只有一个,即lpSecurityDescriptor。Trackback:

相关文档