加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 Windows核心编程

Windows核心编程.pdf

Windows核心编程

tanbobo1935
2011-10-25 0人阅读 举报 0 0 暂无简介

简介:本文档为《Windows核心编程pdf》,可适用于IT/计算机领域

下载第章对程序错误的处理在开始介绍MicrosoftWindows的特性之前必须首先了解Windows的各个函数是如何进行错误处理的。当调用一个Windows函数时它首先要检验传递给它的的各个参数的有效性然后再设法执行任务。如果传递了一个无效参数或者由于某种原因无法执行这项操作那么操作系统就会返回一个值指明该函数在某种程度上运行失败了。表列出了大多数Windows函数使用的返回值的数据类型。表Windows函数常用的返回值类型数据类型表示失败的值VOID该函数的运行不可能失败。Windows函数的返回值类型很少是VOIDBOOL如果函数运行失败那么返回值是否则返回的是非值。最好对返回值进行测试以确定它是还是非。不要测试返回值是否为TRUEHANDLE如果函数运行失败则返回值通常是NULL否则返回值为HANDLE用于标识你可以操作的一个对象。注意有些函数会返回一个句柄值INVALIDHANDLEVALUE它被定义为。函数的PlatformSDK文档将会清楚地说明该函数运行失败时返回的是NULL还是INVALIDHANDLEVALIDPVOID如果函数运行失败则返回值是NULL否则返回PVOID以标识数据块的内存地址LONGDWORD这是个难以处理的值。返回数量的函数通常返回LONG或DWORD。如果由于某种原因函数无法对想要进行计数的对象进行计数那么该函数通常返回或(根据函数而定)。如果调用的函数返回了LONGDWORD那么请认真阅读PlatformSDK文档以确保能正确检查潜在的错误一个Windows函数返回的错误代码对了解该函数为什么会运行失败常常很有用。Microsoft公司编译了一个所有可能的错误代码的列表并且为每个错误代码分配了一个位的号码。从系统内部来讲当一个Windows函数检测到一个错误时它会使用一个称为线程本地存储器(threadlocalstorage)的机制将相应的错误代码号码与调用的线程关联起来(线程本地存储器将在第章中介绍)。这将使线程能够互相独立地运行而不会影响各自的错误代码。当函数返回时它的返回值就能指明一个错误已经发生。若要确定这是个什么错误请调用GetLastError函数:该函数只返回线程的位错误代码。当你拥有位错误代码的号码时必须将该号码转换成更有用的某种对象。WinErrorh头文件包含了Microsoft公司定义的错误代码的列表。下面显示了该列表的某些内容使你能够看到它的大概样子:第一部分程序员必读如你所见每个错误都有种表示法:一个消息ID(这是你可以在源代码中使用的一个宏以便与GetLastError的返回值进行比较)消息文本(对错误的英文描述)和一个号码(应该避计计第一部分程序员必读下载免使用这个号码可使用消息ID)。请记住这里只显示了WinErrorh头文件中的很少一部分内容整个文件的长度超过行。当Windows函数运行失败时应该立即调用GetLastError函数。如果调用另一个Windows函数它的值很可能被改写。注意GetLastError能返回线程产生的最后一个错误。如果该线程调用的Windows函数运行成功那么最后一个错误代码就不被改写并且不指明运行成功。有少数Windows函数并不遵循这一规则它会更改最后的错误代码但是PlatformSDK文档通常指明当函数运行成功时该函数会更改最后的错误代码。Windows许多Windows的函数实际上是用Microsoft公司的位Windows产品产生的位代码来实现的。这种比较老的代码并不通过GetLastError之类的函数来报告错误而且Microsoft公司并没有在Windows中修改位代码以支持这种错误处理方式。对于我们来说这意味着Windows中的许多Win函数在运行失败时不能设置最后的错误代码。该函数将返回一个值指明运行失败这样你就能够发现该函数确实已经运行失败但是你无法确定运行失败的原因。有些Windows函数之所以能够成功运行其中有许多原因。例如创建指明的事件内核对象之所以能够取得成功是因为你实际上创建了该对象或者因为已经存在带有相同名字的事件内核对象。你应搞清楚成功的原因。为了将该信息返回Microsoft公司选择使用最后错误代码机制。这样当某些函数运行成功时就能够通过调用GetLadtError函数来确定其他的一些信息。对于具有这种行为特性的函数来说PlatformSDK文档清楚地说明了GetLastError函数可以这样使用。请参见该文档找出CreateEvent函数的例子。进行调试的时候监控线程的最后错误代码是非常有用的。在MicrosoftVisualstudio中Microsoft的调试程序支持一个非常有用的特性即可以配置Watch窗口以便始终都能显示线程的最后错误代码的号码和该错误的英文描述。通过选定Watch窗口中的一行并键入“err,hr”就能够做到这一点。观察图你会看到已经调用了CreateFile函数。该函数返回INVALIDHANDLEVALUE()的HANDLE表示它未能打开指定的文件。但是Watch窗口向我们显示最后错误代码(即如果调用GetLastError函数该函数返回的错误代码)是x。该Watch窗口又进一步指明错误代码是指“系统不能找到指定的文件。”你会发现它与WinErrorh头文件中的错误代码所指的字符串是相同的。图在VisualStudio的Watch窗口中键入“err,hr”就可以查看当前线程的最后错误代码第章对程序错误的处理计计下载Visualstudio还配有一个小的实用程序称为ErrorLookup。可以使用ErrorLookup将错误代码的号码转换成相应文本描述(见图)。如果在编写的应用程序中发现一个错误可能想要向用户显示该错误的文本描述。Windows提供了一个函数可以将错误代码转换成它的文本描述。该函数称为FormatMessage显示如下:FormatMessage函数的功能实际上是非常丰富的在创建向用户显示的字符串信息时它是首选函数。该函数之所以有这样大的作用原因之一是它很容易用多种语言进行操作。该函数能够检测出用户首选的语言(在RegionalSettingsControlPanel小应用程序中设定)并返回相应的文本。当然首先必须自己转换字符串然后将已转换的消息表资源嵌入你的exe文件或DLL模块中然后该函数会选定正确的嵌入对象。ErrorShow示例应用程序(本章后面将加以介绍)展示了如何调用该函数以便将Microsoft公司定义的错误代码转换成它的文本描述。有些人常常问我Microsoft公司是否建立了一个主控列表以显示每个Windows函数可能返回的所有错误代码。可惜回答是没有这样的列表而且Microsoft公司将永远不会建立这样的一个列表。因为在创建系统的新版本时建立和维护该列表实在太困难了。建立这样一个列表存在的问题是你可以调用一个Windows函数但是该函数能够在内部调用另一个函数而这另一个函数又可以调用另一个函数如此类推。由于各种不同的原因这些函数中的任何一个函数都可能运行失败。有时当一个函数运行失败时较高级的函数对它进行恢复并且仍然可以执行你想执行的操作。为了创建该主控列表Microsoft公司必须跟踪每个函数的运行路径并建立所有可能的错误代码的列表。这项工作很困难。而且当创建系统的新版本时这些函数的运行路径还会改变。定义自己的错误代码前面已经说明Windows函数是如何向函数的调用者指明发生的错误你也能够将该机制用于自己的函数。比如说你编写了一个希望其他人调用的函数你的函数可能因为这样或那样的原因而运行失败你必须向函数的调用者说明它已经运行失败。若要指明函数运行失败只需要设定线程的最后的错误代码然后让你的函数返回FALSE、INVALIDHANDLEVALUE、NULL或者返回任何合适的信息。若要设定线程的最后错误代码只需调用下面的代码:请将你认为合适的任何位号码传递给该函数。尝试使用WinErrorh中已经存在的代码计计第一部分程序员必读下载图ErrorLookup窗口只要该代码能够正确地指明想要报告的错误即可。如果你认为WinErrorh中的任何代码都不能正确地反映该错误的性质那么可以创建你自己的代码。错误代码是个位的数字划分成表所示的各个域。表错误代码的域位~~~内容严重性Microsoft客户保留设备代码异常代码含义=成功=Microsoft公司定义的代码必须是由Microsoft由Microsoft公司定义客户定义=供参考=客户定义的代码=警告=错误这些域将在第章中详细讲述。现在需要知道的重要域是第位。Microsoft公司规定他们建立的所有错误代码的这个信息位均使用。如果创建自己的错误代码必须使位为。这样就可以确保你的错误代码与Microsoft公司目前或者将来定义的错误代码不会发生冲突。ErrorShow示例应用程序ErrorShow应用程序“ErrorShowexe”(在清单中列出)展示了如何获取错误代码的文本描述的方法。该应用程序的源代码和资源文件位于本书所附光盘上的ErrorShow目录下。一般来说该应用程序用于显示调试程序的Watch窗口和ErrorLookup程序是如何运行的。当启动该程序时就会出现如图所示的窗口。可以将任何错误代码键入该编辑控件。当单击Lookup按钮时在底部的滚动窗口中就会显示该错误的文本描述。该应用程序唯一令人感兴趣的特性是如何调用FormatMessage函数。下面是使用该函数的方法:第一个代码行用于从编辑控件中检索错误代码的号码。然后建立一个内存块的句柄并将它初始化为NULL。FormatMessage函数在内部对内存块进行分配并将它的句柄返回给我们。第章对程序错误的处理计计下载图ErrorShow窗口当调用FormatMessage函数时传递了FORMATMESSAGEFROMSYSTEM标志。该标志告诉FormatMessage函数我们想要系统定义的错误代码的字符串。还传递了FORMATMESSAGEALLOCATEBUFFER标志告诉该函数为错误代码的文本描述分配足够大的内存块。该内存块的句柄将在hlocal变量中返回。第三个参数指明想要查找的错误代码的号码第四个参数指明想要文本描述使用什么语言。如果FormatMessage函数运行成功那么错误代码的文本描述就位于内存块中将它拷贝到对话框底部的滚动窗口中。如果FormatMessage函数运行失败设法查看NetMsgdll模块中的消息代码以了解该错误是否与网络有关。使用NetMsgdll模块的句柄再次调用FormatMessage函数。你会看到每个DLL(或exe)都有它自己的一组错误代码可以使用MessageCompiler(MCexe)将这组错误代码添加给该模块并将一个资源添加给该模块。这就是VisualStudio的ErrorLookup工具允许你用Modules对话框进行的操作。以下是清单ErrorShow示例应用程序。清单ErrorShow示例应用程序计计第一部分程序员必读下载第章对程序错误的处理计计下载计计第一部分程序员必读下载第章对程序错误的处理计计下载计计第一部分程序员必读下载下载第章Unicode随着Microsoft公司的Windows操作系统在全世界日益广泛的流行对于软件开发人员来说将目标瞄准国际上的各个不同市场已经成为一个越来越重要的问题。美国的软件版本比国际版本提前个月推向市场这曾经是个司空见惯的现象。但是由于各国对Windows操作系统提供了越来越多的支持因此就更加容易为国际市场生产各种应用软件从而缩短了软件的美国版本与国际版本推出的时间间隔。Windows操作系统始终不逾地提供各种支持以帮助软件开发人员进行应用程序的本地化工作。应用软件可以从各种不同的函数中获得特定国家的信息并可观察控制面板的设置以确定用户的首选项。Windows甚至支持不同的字体以适应应用的需要。之所以将这一章放在本书的开头是因为考虑到Unicode是开发任何应用程序时要采用的基本步骤。本书的每一章中几乎都要讲到关于Unicode的问题而且书中给出的所有示例应用程序都是“用Unicode实现的”。如果你为MicrosoftWindows或MicrosoftWindowsCE开发应用程序你应该使用Unicode进行开发。如果你为MicrosoftWindows开发应用程序你必须对某些问题作出决定。本章也要讲述Windows的有关问题。字符集软件的本地化要解决的真正问题实际上就是如何来处理不同的字符集。多年来许多人一直将文本串作为一系列单字节字符来进行编码并在结尾处放上一个零。对于我们来说这已经成了习惯。当调用strlen函数时它在以结尾的单字节字符数组中返回字符的数目。问题是有些文字和书写规则(比如日文中的汉字就是个典型的例子)的字符集中的符号太多了因此单字节(它提供的符号最多不能超过个)是根本不敷使用的。为此出现了双字节字符集(DBCS)以支持这些文字和书写规则。单字节与双字节字符集在双字节字符集中字符串中的每个字符可以包含一个字节或包含两个字节。例如日文中的汉字如果第一个字符在x与xF之间或者在xE与xFC之间那么就必须观察下一个字节才能确定字符串中的这个完整的字符。使用双字节字符集对于程序员来说简直是个很大的难题因为有些字符只有一个字节宽而有些字符则是两个字节宽。如果只是调用strlen函数那么你无法真正了解字符串中究竟有多少字符它只能告诉你到达结尾的之前有多少个字节。ANSI的C运行期库中没有配备相应的函数使你能够对双字节字符集进行操作。但是MicrosoftVisualC的运行期库却包含许多函数如mbslen,它可以用来操作多字节(既包括单字节也包括双字节)字符串。为了帮助你对DBCS字符串进行操作Windows提供了下面的一组帮助函数(见表)。前两个函数CharNext和CharPrev允许前向或逆向遍历DBCS字符串方法是每次一个字符。第三个函数IsDBCSLeadByte,在字节返回到一个两字字节符的第一个字节时将返回TRUE。表对DBCS字符串进行操作的帮助函数函数描述PTSTRCharNext(PCTSTRpszCurrentChar)返回字符串中的下一个字符的地址PTSTRCharPrev(PCTSTRpszStart,PCTSTR返回字符串中的上一个字符的地址pszCurrentChar)BOOLIsDBCSLeadByteTRUE(BYTEbTestChar)如果该字节是DBCS字符的第一个字节则返回尽管这些函数使得我们对DBCS的操作更容易但还需要一个更好的方法让我们来看看Unicode。Unicode:宽字节字符集Unicode是Apple和Xerox公司于年建立的一个技术标准。年成立了一个集团机构负责Unicode的开发和推广应用。该集团由Apple、Compaq、HP、IBM、Microsoft、Oracle、SiliconGraphics,Inc、Sybase、Unisys和Xerox等公司组成(若要了解该集团的全部成员请通过网址wwwUnicodeorg查找)。该集团负责维护Unicode标准。Unicode的完整描述可以参阅AddisonWesley出版的《UnicodeStandard》一书(该书可以通过网址wwwUnicodeorg订购)。Unicode提供了一种简单而又一致的表示字符串的方法。Unicode字符串中的所有字符都是位的(两个字节)。它没有专门的字节来指明下一个字节是属于同一个字符的组成部分还是一个新字符。这意味着你只需要对指针进行递增或递减就可以遍历字符串中的各个字符不再需要调用CharNext、CharPrev和IsDBCSLeadByte之类的函数。由于Unicode用一个位的值来表示每个字符因此总共可以得到个字符这样它就能够对世界各国的书面文字中的所有字符进行编码远远超过了单字节字符集的个字符的数目。目前已经为阿拉伯文、中文拼音、西里尔字母(俄文)、希腊文、西伯莱文、日文、韩文和拉丁文(英文)字母定义了Unicode代码点。这些字符集中还包含了大量的标点符号、数学符号、技术符号、箭头、装饰标志、区分标志和其他许多字符。如果将所有这些字母和符号加在一起总计约达个不同的代码点这样总计多个代码点中大约还有一半可供将来扩充时使用。这个字符可以分成不同的区域。表显示了这样的区域的一部分以及分配给这些区域的字符。表区域字符位代码字符位代码字符FASCIIF通用区分标志FF拉丁文字符FF西里尔字母F欧洲拉丁文F亚美尼亚文FF扩充拉丁文FF西伯莱文AF标准拼音FF阿拉伯文BFF修改型字母F梵文目前尚未分配的代码点大约还有个不过它们是保留供将来使用的。另外大约有个代码点是保留供个人使用的。计计第一部分程序员必读下载代码点是字符集中符号的位置。为什么使用Unicode当开发应用程序时当然应该考虑利用Unicode的优点。即使现在你不打算对应用程序进行本地化开发时将Unicode放在心上肯定可以简化将来的代码转换工作。此外Unicode还具备下列功能:•可以很容易地在不同语言之间进行数据交换。•使你能够分配支持所有语言的单个二进制exe文件或DLL文件。•提高应用程序的运行效率(本章后面还要详细介绍)。Windows与UnicodeWindows是使用Unicode从头进行开发的用于创建窗口、显示文本、进行字符串操作等的所有核心函数都需要Unicode字符串。如果调用任何一个Windows函数并给它传递一个ANSI字符串那么系统首先要将字符串转换成Unicode然后将Unicode字符串传递给操作系统。如果希望函数返回ANSI字符串系统就会首先将Unicode字符串转换成ANSI字符串然后将结果返回给你的应用程序。所有这些转换操作都是在你看不见的情况下发生的。当然进行这些字符串的转换需要占用系统的时间和内存。例如如果调用CreateWindowEx函数并传递类名字和窗口标题文本的非Unicode字符串那么CreateWindowEx必须分配内存块(在你的进程的默认堆中)将非Unicode字符串转换成Unicode字符串并将结果存储在分配到的内存块中然后调用Unicode版本的CreateWindowEx函数。对于用字符串填入缓存的函数来说系统必须首先将Unicode字符串转换成非Unicode字符串然后你的应用程序才能处理该字符串。由于系统必须执行所有这些转换操作因此你的应用程序需要更多的内存并且运行的速度比较慢。通过从头开始用Unicode来开发应用程序就能够使你的应用程序更加有效地运行。Windows与UnicodeWindows不是一种全新的操作系统。它继承了位Windows操作系统的特性它不是用来处理Unicode的。如果要增加对Unicode的支持其工作量非常大因此在该产品的特性列表中没有包括这个支持项目。由于这个原因Windows像它的前任产品一样几乎都是使用ANSI字符串来进行所有的内部操作的。仍然可以编写用于处理Unicode字符和字符串的Windows应用程序不过使用Windows函数要难得多。例如如果想要调用CreateWindowEx函数并将ANSI字符串传递给它这个调用的速度非常快不需要从你进程的默认堆栈中分配缓存也不需要进行字符串转换。但是如果想要调用CreateWindowEx函数并将Unicode字符串传递给它就必须明确分配缓存并调用函数以便执行从Unicode到ANSI字符串的转换操作。然后可以调用CreateWindowEx传递ANSI字符串。当CreateWindowEx函数返回时就能释放临时缓存。这比使用Windows上的Unicode要麻烦得多。本章的后面要介绍如何在Windows下进行这些转换。虽然大多数Unicode函数在Windows中不起任何作用但是仍有少数Unicode函数确实非常有用。这些函数是:■EnumResourceLanguagesW■GetTextExtentPointW■EnumResourceNamesW■GetTextExtentPointW第章Unicode计计下载■EnumResourceTypesW■LstrlenW■ExtTextOutW■MessageBoxExW■FindResourceW■MessageBoxW■FindResourceExW■TextOutW■GetCharWidthW■WideCharToMultiByte■GetCommandLineW■MultiByteToWideChar可惜的是这些函数中有许多函数在Windows中会出现各种各样的错误。有些函数无法使用某些字体有些函数会破坏内存堆栈有些函数会使打印机驱动程序崩溃等等。如果要使用这些函数必须对它们进行大量的测试。即使这样可能仍然无法解决问题。因此必须向用户说明这些情况。WindowsCE与UnicodeWindowsCE操作系统是为小型设备开发的这些设备的内存很小并且不带磁盘存储器。你可能认为由于Microsoft公司的主要目标是建立一种尽可能小的操作系统因此它会使用ANSI作为自己的字符集。但是Microsoft公司并非鼠目寸光他们懂得WindowsCE的设备要在世界各地销售他们希望降低软件开发成本这样就能更加容易地开发应用程序。为此WindowsCE本身就是使用Unicode的一种操作系统。但是为了使WindowsCE尽量做得小一些Microsoft公司决定完全不支持ANSIWindows函数。因此如果要为WindowsCE开发应用程序必须懂得Unicode并且在整个应用程序中使用Unicode。需要注意的问题下面让我们进一步明确一下“Microsoft公司对Unicode支持的情况”:•Windows既支持Unicode也支持ANSI因此可以为任意一种开发应用程序。•Windows只支持ANSI只能为ANSI开发应用程序。•WindowsCE只支持Unicode只能为Unicode开发应用程序。虽然Microsoft公司试图让软件开发人员能够非常容易地开发在这种平台上运行的软件但是Unicode与ANSI之间的差异使得事情变得困难起来并且这种差异通常是我遇到的最大的问题之一。请不要误解Microsoft公司坚定地支持Unicode并且我也坚决鼓励你使用它。不过你应该懂得你可能遇到一些问题需要一定的时间来解决这些问题。建议你尽可能使用Unicode。如果运行Windows那么只有在必要时才需转换到ANSI。不过还有另一个小问题你应该了解那就是COM。对COM的简单说明当Microsoft公司将COM从位Windows转换成Win时公司作出了一个决定即需要字符串的所有COM接口方法都只能接受Unicode字符串。这是个了不起的决定因为COM通常用于使不同的组件能够互相进行通信而Unicode则是传递字符串的最佳手段。如果你为Windows或WindowsCE开发应用程序并且也使用COM那么你将会如虎添翼。在你的整个源代码中使用Unicode将使与操作系统进行通信和与COM对象进行通信的操作变成一件轻而易举的事情。如果你为Windows开发应用程序并且也使用COM那么将会遇到一些问题。COM要计计第一部分程序员必读下载求使用Unicode字符串而操作系统的大多数函数要求使用ANSI字符串。那是多么难办的事情啊!我曾经从事过若干个项目的开发在这些项目中我编写了许多代码仅仅是为了来回进行字符串的转换。如何编写Unicode源代码Microsoft公司为Unicode设计了WindowsAPI这样可以尽量减少对你的代码的影响。实际上你可以编写单个源代码文件以便使用或者不使用Unicode来对它进行编译。只需要定义两个宏(UNICODE和UNICODE)就可以修改然后重新编译该源文件。C运行期库对Unicode的支持为了利用Unicode字符串定义了一些数据类型。标准的C头文件Stringh已经作了修改以便定义一个名字为wchart的数据类型它是一个Unicode字符的数据类型:例如如果想要创建一个缓存用于存放最多为个字符的Unicode字符串和一个结尾为零的字符可以使用下面这个语句:该语句创建了一个由个位值组成的数组。当然标准的C运行期字符串函数如strcpy、strchr和strcat等只能对ANSI字符串进行操作不能正确地处理Unicode字符串。因此ANSIC也拥有一组补充函数。清单显示了一些标准的ANSIC字符串函数后面是它们的等价Unicode函数。清单标准的ANSIC字符串函数和它们的等价Unicode函数请注意所有的Unicode函数均以wcs开头wcs是宽字符串的英文缩写。若要调用Unicode函数只需用前缀wcs来取代ANSI字符串函数的前缀str即可。注意大多数软件开发人员可能已经不记得这样一个非常重要的问题了那就是Microsoft公司提供的C运行期库与ANSI的标准C运行期库是一致的。ANSIC规定C运行期库支持Unicode字符和字符串。这意味着始终都可以调用C运行期函数以便对Unicode字符和字符串进行操作即使是在Windows上运行也可以调用这些函数。换句话说wcscat、wcslen和wcstok等函数都能够在Windows上很好地运行这些都是必须关心的操作系统函数。第章Unicode计计下载对于包含了对str函数或wcs函数进行显式调用的代码来说无法非常容易地同时为ANSI和Unicode对这些代码进行编译。本章前面说过可以创建同时为ANSI和Unicode进行编译的单个源代码文件。若要建立双重功能必须包含TCharh文件而不是包含Stringh文件。TCharh文件的唯一作用是帮助创建ANSIUnicode通用源代码文件。它包含你应该用在源代码中的一组宏而不应该直接调用str函数或者wcs函数。如果在编译源代码文件时定义了UNICODE这些宏就会引用wcs这组函数。如果没有定义UNICODE那么这些宏将引用str这组宏。例如在TCharh中有一个宏称为tcscpy。如果在包含该头文件时没有定义UNICODE,那么tcscpy就会扩展为ANSI的strcpy函数。但是如果定义了UNICODE,tcscpy将扩展为Unicode的wcscpy函数。拥有字符串参数的所有C运行期函数都在TCharh文件中定义了一个通用宏。如果使用通用宏而不是ANSIUnicode的特定函数名就能够顺利地创建可以为ANSI或Unicode进行编译的源代码。但是除了使用这些宏之外还有一些操作是必须进行的。TCharh文件包含了另外一些宏。若要定义一个ANSIUnicode通用的字符串数组请使用下面的TCHAR数据类型。如果定义了UNICODETCHAR将声明为下面的形式:如果没有定义UNICODE则TCHAR将声明为下面的形式:使用该数据类型可以像下面这样分配一个字符串:也可以创建对字符串的指针:不过上面这行代码存在一个问题。按照默认设置Microsoft公司的C编译器能够编译所有的字符串就像它们是ANSI字符串而不是Unicode字符串。因此如果没有定义UNICODE该编译器将能正确地编译这一行代码。但是如果定义了UNICODE就会产生一个错误。若要生成一个Unicode字符串而不是ANSI字符串必须将该代码行改写为下面的样子:字符串(literalstring)前面的大写字母L用于告诉编译器该字符串应该作为Unicode字符串来编译。当编译器将字符串置于程序的数据部分中时它在每个字符之间分散插入零字节。这种变更带来的问题是现在只有当定义了UNICODE时程序才能成功地进行编译。我们需要另一个宏以便有选择地在字符串的前面加上大写字母L。这项工作由TEXT宏来完成TEXT宏也在TCharh文件中做了定义。如果定义了UNICODE那么TEXT定义为下面的形式:如果没有定义UNICODETEXT将定义为使用该宏可以改写上面这行代码这样无论是否定义了UNICODE宏它都能够正确地进行编译。如下所示:计计第一部分程序员必读下载TEXT宏也可以用于字符串。例如若要检查一个字符串的第一个字符是否是大写字母J只需编写下面的代码即可:Windows定义的Unicode数据类型Windows头文件定义了表列出的数据类型。表Uincode数据类型数据类型说明WCHARUnicode字符PWSTR指向Unicode字符串的指针PCWSTR指向一个恒定的Unicode字符串的指针这些数据类型是指Unicode字符和字符串。Windows头文件也定义了ANSIUnicode通用数据类型PTSTR和PCTSTR。这些数据类型既可以指ANSI字符串也可以指Unicode字符串这取决于当编译程序模块时是否定义了UNICODE宏。请注意这里的UNICODE宏没有前置的下划线。UNICODE宏用于C运行期头文件而UNICODE宏则用于Windows头文件。当编译源代码模块时通常必须同时定义这两个宏。Windows中的Unicode函数和ANSI函数前面已经讲过有两个函数称为CreateWindowEx一个CreateWindowEx接受Unicode字符串另一个CreateWindowEx接受ANSI字符串。情况确实如此不过这两个函数的原型实际上是下面的样子:第章Unicode计计下载CreateWindowExW是接受Unicode字符串的函数版本。函数名结尾处的大写字母W是英文wide(宽)的缩写。每个Unicode字符的长度是位因此它们常常称为宽字符。CreateWindowExA的结尾处的大写字母A表示该函数可以接受ANSI字符串。但是在我们的代码中通常只包含了对CreateWindowEx的调用而不是直接调用CreateWindowExW或者CreateWindowExA。在WinUserh文件中CreateWindowEx实际上是定义为下面这种形式的一个宏:当编译源代码模块时UNICODE是否已经作了定义将决定你调用的是哪个CreateWindowEx版本。当转用一个位的Windows应用程序时你在编译期间可能没有定义UNICODE。对CreateWindowEx函数的任何调用都会将该宏扩展为对CreateWindowExA的调用即对CreateWindowEx的ANSI版本的调用。由于位Windows只提供了CreateWindowsEx的ANSI版本因此可以比较容易地转用它的应用程序。在Windows下Microsoft的CreateWindowExA源代码只不过是一个形实替换程序层或翻译层用于分配内存以便将ANSI字符串转换成Unicode字符串。该代码然后调用CreateWindowExW并传递转换后的字符串。当CreateWindowExW返回时CreateWindowExA便释放它的内存缓存并将窗口句柄返回给你。如果要创建其他软件开发人员将要使用的动态链接库(DLL)请考虑使用下面的方法。在DLL中提供两个输出函数。一个是ANSI版本另一个是Unicode版本。在ANSI版本中只需要分配内存执行必要的字符串转换并调用该函数的Unicode版本(本章后面部分介绍这个进程)。在Windows下Microsoft的CreateWindowExA源代码是执行操作的函数。Windows提供了接受Unicode参数的所有Windows函数的进入点但是这些函数并不将Unicode字符串转换成ANSI字符串它们只返回运行失败的消息。调用GetLastError将返回ERRORCALLNOTIMPLEMENTED。这些函数中只有ANSI版本的函数才能正确地运行。如果编译的代码调用了任何宽字符函数应用程序将无法在Windows下运行。WindowsAPI中的某些函数比如WinExec和OpenFile等只是为了实现与位Windows程序的向后兼容而存在因此应该避免使用。应该使用对CreateProcess和CreateFile函数的调用来取代对WinExec和OpenFile函数的调用。从系统内部来讲老的函数完全可以调用新的函数。老的函数存在的一个大问题是它们不接受Unicode字符串。当调用这些函数时必须传递ANSI字符串。另一方面所有新的和未过时的函数在Windows中都同时拥有ANSI和计计第一部分程序员必读下载Unicode两个版本。Windows字符串函数Windows还提供了一组范围很广的字符串操作函数。这些函数与C运行期字符串函数(如strcpy和wcscpy)很相似。但是该操作系统函数是操作系统的一个组成部分操作系统的许多组件都使用这些函数而不使用C运行期库。建议最好使用操作系统函数而不要使用C运行期字符串函数。这将有助于稍稍提高你的应用程序的运行性能因为操作系统字符串函数常常被大型应用程序比如操作系统的外壳进程Explorerexe所使用。由于这些函数使用得很多因此在你的应用程序运行时它们可能已经被装入RAM。若要使用这些函数系统必须运行Windows或Windows。如果安装了InternetExplorer或更新的版本也可以在较早的Windows版本中获得这些函数。在经典的操作系统函数样式中操作系统字符串函数名既包含大写字母也包含小写字母它的形式类似这个样子:StrCat、StrChr、StrCmp和StrCpy等。若要使用这些函数必须加上ShlWApih头文件。另外如前所述这些字符串函数既有ANSI版本也有Unicode版本例如StrCatA和StrCatW。由于这些函数属于操作系统函数因此当创建应用程序时如果定义了UNICODE(不带前置下划线)那么它们的符号将扩展为宽字符版本。成为符合ANSI和Unicode的应用程序即使你不打算立即使用Unicode最好也应该着手将你的应用程序转换成符合Unicode的应用程序。下面是应该遵循的一些基本原则:•将文本串视为字符数组而不是chars数组或字节数组。•将通用数据类型(如TCHAR和PTSTR)用于文本字符和字符串。•将显式数据类型(如BYTE和PBYTE)用于字节、字节指针和数据缓存。•将TEXT宏用于原义字符和字符串。•执行全局性替换(例如用PTSTR替换PSTR)。•修改字符串运算问题。例如函数通常希望你在字符中传递一个缓存的大小而不是字节。这意味着你不应该传递sizeof(szBuffer),而应该传递(sizeof(szBuffer)sizeof(TCHAR)。另外如果需要为字符串分配一个内存块并且拥有该字符串中的字符数目那么请记住要按字节来分配内存。这就是说应该调用malloc(nCharacters*sizeof(TCHAR)),而不是调用malloc(nCharacters)。在上面所说的所有原则中这是

用户评价(3)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

文档小程序码

使用微信“扫一扫”扫码寻找文档

1

打开微信

2

扫描小程序码

3

发布寻找信息

4

等待寻找结果

我知道了
评分:

/49

Windows核心编程

仅供在线阅读

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利