[分享]VC实时查看GetLastError()返回值及信息

Chivalry 2010-06-02 09:21:03
今天刚看到的,感觉挺有意思,跟大家共享一下

查看GetLastError返回值
在vc的监视窗口里输入@err,hr,即可看到LastError及其解释。

举个栗子:
CFile file;
//打开一个不存在的文件
file.Open("C:\\NotFound.txt", CFile::modeRead);
file.Close();


单步执行过file.Open以后,watch窗口的@err,hr就会提示0x00000002,系统找不到指定的文件
...全文
880 52 打赏 收藏 转发到动态 举报
写回复
用AI写文章
52 条回复
切换为时间正序
请发表友善的回复…
发表回复
xladykiller 2010-06-08
  • 打赏
  • 举报
回复
以前看windows核心编程知道的。
youhun1990 2010-06-08
  • 打赏
  • 举报
回复
windows 核心编程里面就专门讲了。。
zyq5945 2010-06-05
  • 打赏
  • 举报
回复
自己仿造别人的写的一个调试代码,在VS2008上能用,要打印错误时
DBGMSG();

代码如下
void DebugMsg(LPCSTR pszFormat, ...)
{
/*
char buf[1024];
sprintf_s(buf, "[%s](%lu): ", m_szServiceName, GetCurrentThreadId());
va_list arglist;
va_start(arglist, pszFormat);
vsprintf(&buf[strlen(buf)], pszFormat, arglist);
va_end(arglist);
strcat(buf, "\n");
OutputDebugString(buf);
*/

char buf[3072];
char buf1[1024];
va_list arglist;
RtlZeroMemory(buf, sizeof(buf));
//sprintf_s(buf, "[%s](%lu): ", m_szServiceName, GetCurrentThreadId());
//sprintf_s(buf, "\tTID:%lu\n", GetCurrentThreadId());

va_start(arglist, pszFormat);
vsprintf_s(&buf[strlen(buf)], sizeof(buf),pszFormat, arglist);
va_end(arglist);

sprintf_s(buf1, "\tProcessID:%lu\n\tThreadID:%lu\n",
GetCurrentProcessId(), GetCurrentThreadId());
strcat_s(buf, sizeof(buf), buf1);
GetModuleFileNameA(NULL, buf1, sizeof(buf1));
strcat_s(buf, sizeof(buf), "\t");
strcat_s(buf, sizeof(buf), buf1);
strcat_s(buf, sizeof(buf), "\n");
OutputDebugStringA(buf);

}

#define DBGMSG() {DebugMsg("\tReport!!!\n\tTime:%s %s\n\tErrorCode:%d\n\tFile:%s\n\tFuntion:%s\n\tLine:%d\n", \
__DATE__, __TIME__, GetLastError(), __FILE__, __FUNCTION__, __LINE__);}
yxwsbobo 2010-06-05
  • 打赏
  • 举报
回复
刚看Windows核心编程的时候就知道这个了,虽然知道,但也没在意,后来想用这个了缺又找不到了 悲剧。。。
lx_616_at_yeah_net 2010-06-05
  • 打赏
  • 举报
回复
谢谢分享~
arong1234 2010-06-05
  • 打赏
  • 举报
回复
支持!我一直用这种方法。错误信息在IDE里看,永远只是临时性的,一个合格的程序应该能自己汇报错误,减少需要进IDE调试的几率。
[Quote=引用 35 楼 wizardk 的回复:]
It's better to control it by your code. Get the system error code and meaning, and then you can see it any time any where.

code inherited from msdn:

C/C++ code

void ErrorHappen(LPTSTR lpszFun……
[/Quote]
悟迟 2010-06-05
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 swlyzju 的回复:]

暂时没发现这个东西有什么用
[/Quote]有用的,
Chivalry 2010-06-04
  • 打赏
  • 举报
回复
VS2008直接输入err,hr或者$err,hr呢?
zxdlms 2010-06-04
  • 打赏
  • 举报
回复
mark一下!
Eleven 2010-06-04
  • 打赏
  • 举报
回复
看来Windows核心编程一书评价很高嘛。。。
昵称很不好取 2010-06-04
  • 打赏
  • 举报
回复
IT挖矿工 2010-06-04
  • 打赏
  • 举报
回复
纯粹接分的
小楫轻舟 2010-06-03
  • 打赏
  • 举报
回复
在VS2008里没试成功?
wateryh 2010-06-03
  • 打赏
  • 举报
回复
试了试,还真管用
vcrun 2010-06-03
  • 打赏
  • 举报
回复
wangli820 2010-06-03
  • 打赏
  • 举报
回复
@err,hr
wangli820 2010-06-03
  • 打赏
  • 举报
回复
@err,hr
汪宁宇 2010-06-03
  • 打赏
  • 举报
回复
调试是时候,下面这个toolbar每项需要结合起来使用,有很大的帮助:
morris88 2010-06-03
  • 打赏
  • 举报
回复
bragi523 2010-06-03
  • 打赏
  • 举报
回复
jf了
加载更多回复(29)
第一部分 程序员必读 第一章 对程序错误的处理 在我们开始介绍Microsoft Windows应该提供的许多特性之前,我们首先必须了解Windows的各个函数是如何进行错误处理的。 当你调用一个Windows函数时,它首先要检验你传递给它的的各个参数的有效性,然后再设法执行它的任务。如果你传递了一个无效参数,或者由于某种原因它无法执行这项操作,那么该函数就会返回一个,指明该函数在某种程度上运行失败了。表1-1列出了大多数Windows函数使用的数据类型的返回。 表1-1 Windows函数常用的返回类型 数据类型 表示失败的 VOID 该函数的运行不可能失败。Windows函数的返回类型很少 是VOID。 BOLL 如果函数运行失败,那么返回是0,否则返回的是非0。最 好对返回进行测试,以确定它是0还是非0。如果它是TRUE ,则不要测试返回。 HANDLE 如果函数运行失败,则返回通常是NULL,否则返回为 HANDLE,,用于标识你可以操作的一个对象。对于这个返回 ,你应该小心处理,因为有些函数会返回一个句柄 INVALID_HANDLE_VALUE,它被定义为-1。该函数的 Platform SDK资料将会清楚地说明该函数是返回NULL还 是INVALID_HANDLE_VALID,以便指明函数运行已经失败。 PVOID 如果函数运行失败,则返回是NULL,否则返回PVOID,以 标识数据块的内存地址。 LONG/DWORD 这是个难以处理的返回数量的函数通常返回LONG 或DWORD。如果由于某种原因,函数无法对你想要进行计数 的对象进行计数,那么该函数通常返回0或-1(根据该函数而定) 。如果你调用的函数返回了LONG/DWORD,那么请认真阅 读Platform SDK资料,以确保你能正确检查潜在的错误。 当一个Windows函数返回一个错误代码时,它常常可以用来了解函数为什么会运行失败。Microsoft公司编译了一个所有可能的错误代码的列表,并且为每个错误代码分配了一个32位的号码。 从系统内部来讲,当一个Windows函数检测到一个错误时,它会使用一个称为线程本地存储器的机制,将相应的错误代码号码与调用的线程关联起来。(“线程本地存储器”将在第21章中介绍)。这将使线程能够互相独立地运行,而不会影响各自的错误代码。当函数返回给你时,它的返回就能指明一个错误已经发生。若要确定这是个什么错误,请调用GetLastError函数: 见原书P4的程序(1) 该函数只返回线程的32位错误代码。 当你拥有32位错误代码的号码时,你必须将该号码转换成更有用的某种对象。WinError.h头文件包含了Microsoft公司定义的错误代码的列表。下面我显示了该列表的某些内容,使你能够看到它的大概样子: 见原书P4的程序(2)和P5的程序 你可以看到,每个错误都有3种表示法:即一个消息ID(这是你可以在源代码中使用的一个宏,以便与GetLastError返回进行比较),消息文本(对错误的英文描述)和一个号码(你应该避免使用这个号码,而应该使用消息ID)。请记住,我只选择了WinError.h头文件中的很少一部分内容来向你进行展示,整个文件的长度超过21000行。 当Windows函数运行失败时,你应该立即调用GetLastError函数,否则,如果你调用另一个Windows函数,它的很可能被改写。 说明 GetLastError返回线程产生的最后一个错误。如果该线程调用的Windows 函数运行成功,那么最后一个错误代码就不被改写,并且不指明运行成功。有少 数Windows函数并不遵循这一规则,并且它会更改最后的错误代码,但是Platform SDK资料通常指明,当函数运行成功时,该函数会更改最后的错误代码。 Windows 98 许多Windows 98的函数实际上是用Microsoft公司的16位Windows 3.1产 品产生的16位代码来实现的。这种比较老的代码并不通过GetLastError之类函 数来报告错误,而且Microsoft公司并没有在Windows 98中修改16位代码,以 支持这种错误处理方式。对于我们来说,这意味着Windows 98中的许多Win32 函数在运行失败时不能设置最后的错误代码。该函数将返回一个,指明运行失 败,这样你就能够发现该函数确实已经运行失败。但是你无法确定运行失败的原 因。 有些Windows函数之所以能够成功运行,那是若干个原因产生的结果。例如,创建指明的事件内核对象之所以能够取得成功,原因是你实际上创建了该对象,或者是因为已经存在带有相同名字的事件内核对象。你的应用程序必须知道成功的原因。为了将该信息返回给你,Microsoft公司选择使用最后错误代码机制。这样,当某些函数运行成功时,你就能够通过调用GetLadtError函数来确定其他的一些信息。对于具有这种行为特性的函数来说,Platform SDK资料清楚地说明了GetLastError函数可以这样来使用。请参见该资料,以便找出CreateEvent函数的例子。 当你进行调试的时候,我发现监控线程的最后错误代码是非常有用的。在Microsoft Visual studio 6.0中,Microsoft的调试程序支持一个非常有用的特性,即你可以配置Watch窗口,以便始终都能向你显示线程的最后错误代码的号码和该错误的英文描述。通过选定Watch窗口中的一行,并键入“@err,hr",你就能够做到这一点。观察图1-1,你会看到我已经调用了CreateFile函数。该函数返回INVALID_HANDLE_VALUE(-1)的HANDLE,表示它未能打开指定的文件。但是Watch窗口向我们显示最后错误代码(即如果我调用GetLastErro函数,该函数返回的错误代码)是0x00000002。该Watch窗口又进一步指明错误代码2是指“系统不能找到指定的文件。”你会发现它与WinError.h头文件中的错误代码2所指的字符串是相同的。 图1-1 在Visual Studio 6.0的Watch窗口中键入 “@err,hr",你就可以查看当前线程的最后错误代码。 Visual studio还配有一个小的实用程序,称为Error Lookup。你可以使用Error Lookup将错误代码的号码转换成它的文本描述。 见P7的Error Lookup插图 如果我在我编写的应用程序中发现一个错误,我可能想要向用户显示该错误的文本描述。Windows提供了一个函数,可以将错误代码转换成它的文本描述。该函数称为FormatMessage。请看下面的代码: 见原书P8的程序(1) FormatMessage函数的功能实际上是非常丰富的,在创建向用户显示的字符串信息时,它是人们首选的函数。该函数之所以有这样大的作用,原因之一是它很容易用多种语言来进行操作。该函数能够检测出用户首选的语言(在Regional Settings Control Panel小应用程序中设定),并返回相应的文本。当然,你首先必须自己转换字符串,然后将已转换的消息表资源嵌入你的.exe文件或DLL模块,不过,这时该函数会选定正确的嵌入对象。ErrorShow示例应用程序(本章后面将加以介绍)展示了如何调用该函数,以便将Microsoft公司定义的错误代码转换成它的文本描述。 有些人常常问我,Microsoft公司是否建立了一个主控列表,以显示每个Windows函数可能返回的所有错误代码。可惜,答案是没有这样的列表,而且Microsoft公司将永远不会建立这样的一个列表。因为在创建系统的新版本时,建立和维护该列表实在太困难了。 建立这样一个列表时存在的问题是,你可以调用一个Windows函数,但是该函数能够在内部调用另一个函数,而这另一个函数又可以调用另一个函数,如此类推。由于各种不同的原因,这些函数中的任何一个函数都可能运行失败。有时,当一个函数运行失败时,较高级的函数对它进行恢复,并且仍然可以执行你想执行的操作。为了创建该主控列表,Microsoft公司必须跟踪每个函数的运行路径,并建立所有可能的错误代码的列表。这项工作很困难。当创建系统的新版本时,这些函数的运行路径就会改变。 1.1 你也能够定义自己的错误代码 好了,我已经说明Windows函数是如何向函数的调用者指明发生的错误。Microsoft公司也使你能够将该机制用于你自己的函数。比如说,你编写了一个你希望其他人调用的函数。你的函数可能因为这样或那样的原因而运行失败,你必须向函数的调用者说明它已经运行失败。 若要指明函数运行失败,你只需要设定线程的最后的错误代码,然后让你的函数返回FALSE,INVALID_HANDLE_VALUE,NULL,或者返回任何合适的信息。若要设定线程的最后错误代码,你只需要调用下面的代码: 见原书P8的程序(2) 请将你认为合适的任何32位号码传递给该函数。我设法使用WinError.h中已经存在的代码,只要该代码能够正确地指明我想要报告的错误即可。如果你认为WinError.h中的任何代码都不能正确地反映该错误的性质,那么你可以创建你自己的代码。错误代码是个32位的数字,它可以划分成下表所示的各个域。 位 31-30 29 28 27-16 15-0 内容 严重性 Microsoft/ 保留 设备代码 异常代码 客户 含义 0=成功 0=Microsoft 必须是0 由Microsoft 由Microsoft/ 1=供参考 公司定义的 公司定义 客户定义 2=警告 代码 3=错误 1=客户定义 的代码 这些域将在第24章中详细讲述。现在,你需要知道的重要域是第29位的信息。Microsoft公司规定,他们建立的所有错误代码的这个信息位均使用0。如果你创建自己的错误代码,你必须在这个信息位中输入1。这样,就可以确保你的错误代码与Microsoft公司目前或者将来定义的错误代码不会发生冲突, 1.2 ErrorShow示例应用程序 ErrorShow应用程序“01 ErrorShow.exe"(在图1-2中列出)展示了如何获取错误代码的文本描述的方法。该应用程序的源代码和资源文件位于本书所附光盘上的01-ErrorShow目录下。一般来说,该应用程序用于显示调试程序的Watch窗口和Error Lookup程序是如何运行的。当你启动该程序时,就会出现下面这个窗口。 见原书P9的插图 你可以将任何错误代码键入该编辑控件。当你单击Look Up按钮时,在底部的滚动窗口中就会显示该错误的文本描述。该应用程序唯一令人感兴趣的特性是如何调用FormatMessage函数。下面是我使用该函数的方法: 见原书P10的程序(1) 第一个代码行用于从编辑控件中检索错误代码的号码。然后,建立一个内存块的句柄并将它初始化为NULL。FormatMessage函数在内部对内存块进行分配,并将它的句柄返回给我们。 当调用FormatMessage函数时,我传递了FORMAT_MESSAGE_FROM_SYSTEM标志。该标志告诉FormatMessage函数,我们想要系统定义的错误代码的字符串。我还传递了FORMAT_MESSAGE_ALLOCATE_BUFFER标志,告诉该函数为错误代码的文本描述分配足够大的内存块。该内存块的句柄将在hlocal变量中返回。第三个参数指明我们想要查找的错误代码的号码,第四个参数指明我们想要文本描述使用什么语言。 如果FormatMessage函数运行成功,那么错误代码的文本描述就位于内存块中,我将它拷贝到对话框底部的滚动窗口中。如果FormatMesage函数运行失败,我设法查看NetMsg.dll模块中的消息代码,以了解该错误是否与网络有关。使用NetMsg.dll模块的句柄,我再次调用FormatMessage函数。你会看到,每个DLL(或.exe)都有它自己的一组错误代码,你可以使用Message Compiler(MC.exe)将这组错误代码添加给该模块,并将一个资源添加给该模块。这就是Visual Studio的Error Lookup工具允许你用Modules对话框进行的操作。 图1-2 ErrorShow示例应用程序 见原书P11—16 第2章 UNICODE 随着Microsoft公司的Windows操作系统在全世界日益广泛的流行,对于我们这些软件开发人员来说,将我们的目标瞄准国际上的各个不同市场,已经成为一个越来越重要的问题。美国的软件版本比国际版本提前6个月推向市场,这曾经是个司空见惯的现象。但是,由于各国对Windows操作系统提供了越来越多的支持,因此就更加容易为国际市场生产各种应用软件,从而缩短了软件的美国版本与国际版本推出的时间间隔。 Windows操作系统始终不逾地提供各种支持,以帮助软件开发人员进行应用程序的本地化工作。应用软件可以从各种不同的函数中获得特定国家的信息,并可观察控制面板的设置,以确定用户的首选项。Windows甚至支持不同的字体,以适应我们的应用的需要。 我之所以将这一章放在本书的开头,是因为我考虑到Unicode是开发任何应用程序时要采用的基本步骤。关于Unicode的问题,我在本书的每一章中几乎都要讲到,而且本书中给出的所有示例应用程序都是“用Unicode实现的”。如果你为Microsoft Windows 2000或Microsoft Windows CE开发应用程序,你应该使用Unicode进行开发。如果你为Microsoft Windows 98开发应用程序,你必须对某些问题作出决定。本章也要讲述Windows 98的有关问题。 2.1 字符集 软件的本地化要解决的真正问题,实际上就是如何来处理不同的字符集。多年来,我们许多人一直将文本串作为一系列单字节字符来进行编码,并在结尾处放上一个零。对于我们来说,这已经成了习惯。当我们调用strlen函数时,它在以0结尾的单字节字符数组中返回字符的数目。 问题是,有些文字和书写规则(比如日文中的汉字就是个典型的例子)的字符集中的符号太多了,因此单字节(它提供的符号最多不能超过256个)是根本不敷使用的。为此我们创建了双字节字符集(DBCS),以支持这些文字和书写规则。 2.1.1 单字节与双字节字符集 在双字节字符集中,字符串中的每个字符可以包含一个字节,也可以包含两个字节。例如,日文中的汉字,如果第一个字符在0x81与0x9F之间,或者在0xE0与0xFC之间,那么你就必须观察下一个字节,才能确定字符串中的这个完整的字符。如果要使用双字节字符集,对于程序员来说简直是个很大的难题,因为有些字符只有一个字节宽,而有些字符则是两个字节宽。 如果只是调用strlen函数,那么你无法真正了解字符串中究竟有多少字符,它只能告诉你到达结尾的0之前有多少个字节。ANSI的C运行期库中没有配备相应的函数,使你能够对双字节字符集进行操作。但是,Microsoft Visual C++的运行期库却包含许多函数,如_mbslen,它可以用来操作多字节(既包括单字节也包括双字节)字符串。 为了帮助你对DBCS字符串进行操作,Windows提供了下面的一组帮助函数。 函数 描述 PTSTR CharNext 返回字符串中的下一个字符的地址 (PCTSTR pszCurrentChar); PTSTR CharPrev 返回字符串中的上一个字符的地址 (PCTSTR pszStart, PCTSTR pszCurrentChar); BOOL IsDBCSLendByte 如果该字节是DBCS字符的第一个字节,则返 (BYTE bTestChar); 回TRUE 2.1.2 Unicode:宽字节字符集 Unicode是Apple和Xerox公司于1988年建立的一个技术标准。1991年,成立了一个集团机构负责Unicode的开发和推广应用。该集团由Apple、Compaq、HP、IBM、Microsoft、Oracle、Silicon Graphics、Sybase、Unisys和Xerox等公司组成。(若要了解该集团的全部成员,请通过网址www.Unicode.org查找。)该集团公司负责维护Unicode标准。Unicode的完整描述可以参阅AddisonWesley出版的《Unicode Standard》一书。(该书可以通过网址www.Unicode.org订购。) Unicode提供了一种简单而又一致的表示字符串的方法。Unicode字符串中的所有字符都是16位的字符(两个字节)。它没有专门的字节来指明下一个字节是属于同一个字符的组成部分,还是一个新字符。这意味着你只需要对指针进行递增或递减,就可以遍历字符串中的各个字符。你不再需要调用CharNext,CharPrev和IsDBCSLeadByte之类的函数。 由于Unicode用一个16位的来表示每个字符,因此总共可以得到65000个字符,这样,它就能够对世界各国的书面文字中的所有字符进行编码。这远远超过了单字节字符集的256个字符的数目。 目前,已经为阿拉伯文、中文拼音、西里尔字母(俄文)、希腊文、西伯莱文、日文、韩文和拉丁文(英文)字母定义了Unicode代码点1。这些字符集中还包含了大量的标点符号、数学符号、技术符号、箭头、装饰标志、区分标志和其他许多字符。如果你将所有这些字母和符号加在一起,总计约达35000个不同的代码点,这样,总计的65000个代码点中,大约还有一半可供将来扩充时使用。 这65536个字符可以分成不同的区域。下面这个表显示了一部分这样的区域以及分配给这些区域的字符。 16位代码 字符 16位代码 字符 0000-007F ASCII 0300-036F 通用区分标志 0080-00FF 拉丁文1字符 0400-04FF 西里尔字母 0100-017F 欧洲拉丁文 0530-058F 亚美尼亚文 0180-01FF 扩充拉丁文 0590-05FF 西伯莱文 0250-02AF 标准拼音 0600-06FF 阿拉伯文 02B0-02FF 修改型字母 0900-097F 梵文 注1. 代码点是指字符集中的一个符号的位置 目前尚未分配的代码点大约还有29000个,不过它们是保留供将来使用的。另外,大约有6000个代码点是保留供你个人使用的。 2. 2 为何应该使用Unicode 当你开发应用程序时,你当然应该考虑利用Unicode的优点。即使现在你不打算对你的应用程序进行本地化,开发时将Unicode放在心上,肯定可以简化将来的代码转换工作。此外,Unicode还具备下列功能: * 可以很容易地在不同语言之间进行数据交换 * 使你能够分配支持所有语言的单个二进制.exe文件或DLL文件 * 提高你的应用程序的运行效率(本章后面还要详细介绍) 2.3 Windows 2000与Unicode Windows 2000是使用Unicode从头进行开发的,用于创建窗口、显示文本、进行字符串操作等的所有核心函数都需要Unicode字符串。如果你调用任何一个Windows函数并给它传递一个ANSI字符串,那么系统首先要将字符串转换成Unicode,然后将Unicode字符串传递给操作系统。如果你希望函数返回ANSI字符串,系统就会首先将Unicode字符串转换成ANSI字符串,然后将结果返回给你的应用程序。所有这些转换操作都是在你看不见的情况下发生的。当然,进行这些字符串的转换需要占用系统的时间和内存开销。 例如,如果你调用CreateWindowEx函数,并传递类名字和窗口标题文本的非Unicode字符串,那么CreateWindowEx必须分配内存块(在你的进程的默认堆中),将非Unicode字符串转换成Unicode字符串,并将结果存储在分配到的内存块中,然后调用Unicode版本的CreateWindowEx函数。 对于用字符串填入缓存的函数来说,系统必须首先将Unicode字符串转换成非Unicode字符串,然后你的应用程序才能处理该字符串。由于系统必须执行所有这些转换操作,因此你的应用程序需要更多的内存,并且运行的速度比较慢。通过从头开始用Unicode来开发应用程序,你就能够使你的应用程序更加有效地运行。 2. 4 Windows 98与Unicode Windows 98不是一种全新的操作系统。它继承了16位Windows操作系统的特性,它不是用来处理Unicode。如果要增加对Unicode的支持,其工作量非常大,因此在该产品的特性列表中没有包括这个支持项目。由于这个原因,Windows 98象它的前任产品一样,几乎都是使用ANSI字符串来进行所有的内部操作的。 你仍然可以编写用于处理Unicode字符和字符串的Windows应用程序,不过,使用Windows函数要难得多。例如,如果你想要调用CreateWindowEx函数并将ANSI字符串传递给它,这个调用的速度非常快,不需要从你进程的默认堆栈中分配缓存,也不需要进行字符串转换。但是,如果你想要调用CreateWindowEx函数并将Unicode字符串传递给它,你就必须明确分配缓存,并调用函数,以便执行从Unicode到ANSI字符串的转换操作。然后你可以调用CreateWindowEx,传递ANSI字符串。当CreateWindowEx函数返回时,你就能释放临时缓存。这比使用Windows 2000上的Unicode要麻烦得多。在本章的后面部分中,我要介绍如何在Windows 98下进行这些转换。 虽然Unicode函数的大多数代码在Windows 98中不起任何作用,但是有少数Unicode函数确实拥有非常有用的实现代码。这些函数是: 见原书的P21 可惜的是,这些函数中有许多函数在Windows 98中会出现各种各样的错误。有些函数无法使用某些字体,有些函数会破坏内存堆栈,有些函数会使打印机驱动程序崩溃,如此等等。如果你要使用这些函数,你必须对它们进行大量的测试。即使这样,你可能仍然无法解决问题。因此你必须向用户说明这些情况。 2. 5 Windows CE与Unicode Windows CE操作系统是为小型设备开发的,这些设备的内存很小,并且不带磁盘存储器。你可能认为,由于Microsoft公司的主要目标是建立一种尽可能小的操作系统,因此它会使用ANSI作为自己的字符集。但是Microsoft公司并不是鼠目寸光。他们懂得,采用Windows CE的设备要在世界各地销售,他们希望降低软件开发成本,这样就能更加容易地开发应用程序。为此,Windows CE本身就是使用Unicode的一种操作系统。 但是,为了使Windows CE尽量做得小一些,Microsoft公司决定完全不支持ANSI Windows函数。因此,如果你要为Windows CE开发应用程序,你必须懂得Unicode,并且在整个应用程序中使用Unicode。 2. 6 需要注意的问题 下面让我们进一步明确一下“Microsoft公司对Unicode支持的情况”: * Windows 2000既支持Unicode,也支持ANSI,因此你可以为它们当中的任何一种开发应用程序 * Windows 98 只支持ANSI,你只能为ANSI开发应用程序 * Windows CE只支持Unicode,你只能为Unicode开发应用程序 虽然Microsoft公司试图让软件开发人员能够非常容易地开发在这3种平台上运行是软件,但是Unicode与ANSI之间的差异使得事情变得困难起来,并且这种差异通常是我遇到的最大的问题之一。请不要误解,Microsoft公司坚定地支持Unicode,并且我也坚决鼓励你使用它。不过你应该懂得,你可能遇到一些问题,需要一定的时间来解决这些问题。我建议你尽可能使用Unicode。如果你运行Windows 98,那么只有在必要时才要转换到ANSI。 不过,还有另一个小问题你应该了解,那就是COM。 2.7 对COM的简单说明 当Microsoft公司将COM从16位Windows转换成Win32时,公司作出了一个决定,即,需要字符串的所有COM接口方法都只能接受Unicode字符串。这是个了不起的决定,因为COM通常用于使不同的组件能够互相之间进行通信,而Unicode则是传递字符串的最佳手段。 如果你为Windows 2000或Windows CE开发应用程序,并且也使用COM,那么你将会如虎添翼。在你的整个源代码中使用Unicode,将使与操作系统进行通信和与COM对象进行通信的操作变成一件轻而易举的事情。 如果你为Windows 98开发应用程序,并且也使用COM,那么你将会遇到一些问题。COM要求你使用Unicode字符串。操作系统的大多数函数要求你使用ANSI字符串。那是多么难办的事情啊!我曾经从事过若干个项目的开发,在这些项目中,我编写了许多代码,仅仅是为了来回进行字符串的转换。 2. 8 如何编写Unicode源代码 Microsoft公司为Unicode设计了Windows API,这样,它可以尽量减少对你的代码的影响。实际上,你可以编写单个源代码文件,以便使用或者不使用Unicode来对它进行编译。你只需要定义两个宏(UNICODE和_UNICODE),就可以修改然后重新编译该源文件。 2. 8.1 C运行期库对Unicode的支持 为了利用Unicode字符串,因此定义了一些数据类型。标准的C头文件String.h已经作了修改,以便定义一个名字为wchar_t的数据类型,它是一个Unicode字符的数据类型: 见原书P23的程序(1) 例如,如果你想要创建一个缓存,用于存放最多为99个字符的Unicode字符串和一个结尾为零的字符,你可以使用下面这个语句: 见原书P23的程序(2) 该语句创建了一个由100个16位组成的数组。当然,标准的C运行期字符串函数,如strcpy、strchr和strcat等,只能对ANSI字符串进行操作,它们不能正确地处理Unicode字符串。因此,ANSI C也拥有一组补充函数。图2-1显示了一些标准的ANSI C字符串函数,后面是它们的等价Unicode函数。 图2-1 标准的ANSI C字符串函数和它们的等价Unicode函数 见原书P23的程序(3)和P24的程序 请注意,所有的Unicode函数均以wcs开头,wcs是宽字符串的英文缩写。若要调用Unicode函数,只需用前缀wcs来取代任何ANSI字符串函数的前缀str即可。 说明 大多数软件开发人员可能已经不记得这样一个非常重要的问题了,那就 是Microsoft公司提供的C运行期库与ANSI的标准C运行期库是一致的。 ANSI C规定,C运行期库支持Unicode字符和字符串。这意味着你始终都可 以调用C运行期函数,以便对Unicode字符和字符串进行操作,即使你是在 Windows 98上运行,也可以调用这些函数。换句话说,wcscat,wcslen和wcstok 等函数都能够在Windows 98上很好地运行,这些都是你必须关心的操作系统函数。 对于包含了对str函数或wcs函数进行显式调用的代码来说,你无法非常容易地同时为ANSI和Unicode对这些代码进行编译。在本章前面部分的内容中,我说过可以创建同时为ANSI和Unicode进行编译的单个源代码文件。若要建立这种双重功能,你必须包含Tchar.h文件,而不是包含String.h文件。 Tchar.h文件的唯一作用是帮助你创建ANSI/Unicode通用源代码文件。它包含你应该用在源代码中的一组宏,而不应该直接调用str函数或者wcs函数。如果你在编译源代码文件时定义了_UNICODE,这些宏就会引用wcs这组函数。如果你没有定义_UNICODE,那么这些宏将引用str这组宏。 例如,在Tchar.h中有一个宏称为_tcscpy。如果在你包含该头文件时没有定义_UNICODE,那么_tcscpy就会扩展为ANSI的strcpy函数。但是如果定义了_UNICODE,_tcscpy将扩展为Unicode的wcscpy函数。拥有字符串参数的所有C运行期函数都在Tchar.h文件中定义了一个通用宏。如果你使用通用宏,而不是ANSI/Unicode的特定函数名,你就能够顺利地创建可以为ANSI或Unicode进行编译的源代码。 但是,除了使用这些宏之外,还有一些操作你是必须进行的。Tchar.h文件包含了一些其他的宏。 若要定义一个ANSI/Unicode通用的字符串数组,请使用下面的TCHAR数据类型。如果定义了_UNICODE,TCHAR将声明为下面的形式: 见原书P25的程序(1) 如果没有定义_UNICODE,则TCHAR将声明为下面的形式: 见原书P25的程序(2) 使用该数据类型,你可以象下面这样分配一个字符串: 见原书P25的程序(3) 你也可以创建对字符串的指针: 见原书P25的程序(4) 不过上面这行代码存在一个问题。按照默认设置,Microsoft公司的C++编译器能够编译所有的字符串,就象它们是ANSI字符串,而不是Unicode字符串。因此,如果没有定义_UNICODE,该编译器将能正确地编译这一行代码。但是,如果定义了_UNICODE,就会产生一个错误。若要生成一个Unicode字符串而不是ANSI字符串,你必须将该代码行改写为下面的样子: 见原书P25的程序(5) 原义字符串前面的大写字母L,用于告诉编译器该字符串应该作为Unicode字符串来编译。当编译器将字符串置于程序的数据部分中时,它在每个字符之间分散插入零字节。这种变更带来的问题是,现在只有当定义了_UNICODE时,程序才能成功地进行编译。我们需要另一个宏,以便有选择地在原义字符串的前面加上大写字母L。这项工作由_TEXT宏来完成,_TEXT宏也在Tchar.h文件中做了定义。如果定义了_UNICODE,那么_TEXT定义为下面的形式: 见原书P25的程序(6)
此文件是MyFll.fll文件不可分割的一部分,包含对MyFll函数库全部说明及示例。 MyFll是专为Microsoft Visual FoxPro设计的扩展库,部分函数来自任明汉(RMH myF1论坛)前辈提供的myDll代码翻译而成,由于很多功能利用VFP处理比较烦锁,或无法实现,使用此库可有效解决VFP的弱项。部分与Win32Api同名的函数,是Win32API的封装。部分函数取自网上开源算法,在相关函数中均有注明。 此源代码使用VC6 sp6开发,采用VFP9 Sp2的库文件,理论上可以应用于VFP6 7 8 9版本。 此库在第一次打开时自动添加智能感应功能已关闭。请在打开后,使用FllAddFoxCode()向智能感应库添加所有的函数原型(需要VFP7以上),此库存所有函数使用VFP推荐的命名规则,一般可以根据函数名和知能感应的提示参数来推测参数及返回。 开发环境可以使用FllHelp()函数来检查函数信息: 取得此版本函数个数:FllHelp() 取得第x个函数的信息:?FllHelp(x) 显示提示信息:?FllHelp(x,.T.) 显示指定函数的信息:FllHelp("SendMessage",.T.) 显示Fll的版:?FllGetVersion() 添加智能感应代码:FllAddFoxCode() 版本信息:由于不定期更新,请及时核对版本(右键属性),版本为X.X.X.X,第一位为主版本号,第二位为函数个数,第三位为发布的年份,第四位为日期。当前版本为:1.179.9.811 版权:你可以自由使用、散发此函数库及此帮助,包括应用于你的商业软件中。在转发时应注意帮助文件于函数库和帮助同在。不得对软件进行破解、反编译等破性或逆向工程。MyFll作者不承担可能由于技术原因或失误给你带来的错误或损失。发现错误可以与作者取得联系共同改进。 感谢:我的帮助论坛http://www.myf1.net/bbs 梅子论坛 http://www.meizvfp.com/bbs 感谢:各位为Myfll做测试、编写帮助的热心网友。 作者:木瓜 ljyit@163.com 函数列表: 硬件相关函数: GetDiskSerial        读取指定硬盘的序列号 GetCpuId           读取CPU的序列号 GetMAC            读取网卡的MAC地址 GetVolumeNumber       读取指写磁盘的卷标 IsDiskInDrive        检查指定磁盘是否就绪 GetPort           读取系统的串口、并口 ComOpen           打开串口 ComWrite           向串口输出信息 ComRead            从串口读取信息 ComClose           关闭串口 GetGUID            获取全球唯一ID AEthernet          枚举网卡的所有信息 加密解密函数: MD5File           计算一个文件的MD5效验和 MD5String          计算一个字符串的MD5效验和 CRC32File          计算一个文件的CRC32效验和 CRC32String         计算一个字符串的CRC32效验和 des             采用DES算法加密或解密一个字符串,长度为8位 des16            采用DES算法加密或解密一个字符串,长度为16位 des24            采用DES算法加密或解密一个字符串,长度为24位 EnDeString          双向加密解密字符串的函数 Encrypt           双向加密解密字符串 RSACalc           RSA计算函数 RSAGen            生成随机RSA密钥函数 RSACmp            比较两个十六进制是否相等 URLEncode          URL编码 URLDecode          URL解码 QPEncode           Quoted-Printable QP编码 QPDecode           Quoted-Printable QP解码 压缩解压函数: Zip              压缩文件 UnZip             解压文件 UnZipFile           解压单个文件           ZipInfo            测试一个文件是否在压缩文件中存在 ZipFileToStr         将压缩文件中的文件解压到变量 ZipAFile           将zip文件中的文件信息生成的数组 CompressString        压缩一个字符串 DeCompressString       解压字符串 数据库: CursorToStr         将Cursor生成变量 ChangesToStr         将Cursor的变动情况生成变量         StrToCursor         将变量还原为Cursor AppendFromStr        将变量中的表追加到指定表中 ACursorList         将变量中的表信息生成数组 ACursorFields        将变量中的指定表的字段信息生成数组 CRC32Record         计算一条记录的CRC32 SQLCallBack         SQL回调函数载入 SQLCallReset         SQL回调函数卸载 PackMDB           压缩修复Access数据库 ReadMemo           读取vfp中大于16M的备注字段 网络相关函数: DownFile           从internet上下载文件到本地 DownFileX           线程方式从网上下载一个文件 HttpGetFileSize       读取internet上的文件大小 HttpPostData         向http服务器Post数据 HttpOpen           打开一个Internet句柄 HttpAddParms         为Http句柄添加一个参数 HttpSend           发送Http句柄中的Post数据 HttpClose          关闭Http句柄 FtpDownFile          从FTP服务器下载文件 FtpUploadFile        向FTP服务器上传文件 FtpConnect          连接FTP服务器 FtpDisconnect        断开FTP服务器连接 FtpAFile          枚举Ftp中的所有文件 FtpCreateDir        在FTP服务器上创建一个文件夹         FtpSetCurrentDir      设置当前的文件夹 FtpDeleteDir        删除FTP服务器上的文件夹 FtpDelFile         删除FTP服务器上的文件 FtpRename          重命名FTP服务器上的文件 FtpFOpen          打开FTP服务器上的文件 FtpFClose         关闭FTP服务器上的文件 FtpFRead          读取FTP服务器上的文件 FtpFWrite          写入FTP服务器上的文件 FtpSetPasv          设置FTP被动工作模式 ShareAdd           在局域网共享文件夹 ShareDel           删除共享文件夹的共享 ShareMapDrive         映射网络驱动器 ShareDelDrive         删除映射的网络驱动器 GetLocalIP          读取本机IP DomainToIP          转换域名为IP地址 DialUp            建立拨号连接 DialDown           断开拨号连接 GetSqlServer         列出所有网络上的SQL Server IpToMAC           根据IP得到MAC地址 邮件函数: SmtpCreate          创建一个SMTP发送邮件的句柄 SmtpNewMail         在内存中创建邮件内容,等待发送 SmtpSend           发送邮件 SmtpGetLastError       检测邮件最后的错误 Pop3Create          创建一个POP3接收邮件的句柄 Pop3Close          关闭句柄 Pop3AMailList        枚举POP3服务器上的邮件数量 Pop3DeleteMail        删除POP3邮件服务器上的邮件 Pop3GetMail         下载一封邮件 Pop3GetMailHeader      下载邮件头 Pop3DeleteMail        删除服务器上的邮件 打印相关函数: PaperAdd           添加自定义纸张并返回ID PaperDel           删除自定义纸张 PaperInfo          读取所有纸张信息或指定纸张信息 APaper            枚举所有纸张生成数组 GetDefaultPrinter      取得默认打印机的名称 SetDefaultPrinter      设置默认打印机 PrinterOpen         启用一个打印任务 PrinterOutPage        打印页对像 PrinterClose         结束打印 常用转换函数: ToPY             生成指写字符串的拼音首字 NToC             数字转人民币大写 hzbh             计算指定字符串的汉字笔划 StringToDword        将高底位存放的字符串,转换为字符型 DwordToString        将整型数转换为字符型数 NumConver          进制转换函数,能够将一个数字转换为2至36进制的字符串 ConverNum           将指定进制的字符串转换为数型 GB2312ToBIG5         简体转繁体 BIG5ToGB2312         繁体转简体 FormToBmp          将表单保存为BMP图片 RectToBmp           指定屏幕区域保存为bmp图片 StrReverse          指定屏幕区域保存为bmp图片 ImageConver         转换图片格式 FTrim            删除字符串中除指定字符以外的字符 Thumbnail          缩放图片 注册表读写函数: regRead           读取注册表中的设置 regWrite           向注册表中写入设置 regDelKey          删除注册表中的分支 regDelValue         删除注册表中的设置 ini文件读写函数: iniRead           从ini文件中读取设置 iniWrite           向ini文件中写入一个设置 iniSet            设置默认的数据段和ini文件 iniSetSection        设置默认的数据段 iniSetFileName        设置默认的ini文件 系统托盘 SysTrayAdd          向系统托盘添加一个图标 SysTrayEdit         修改系统托盘的图标 SysTrayDel          删除系统托盘中的图标 HotKeyAdd          向系统注册一个热键 HotKeyDel          删除向系统注册的热键 SysTrayShowMessage      显示托盘消息 文件操作 CopyFiles          复制文件 MoveFiles          移动文件 DeleteFiles         删除文件 CreateShortcut        创建快捷方式 GetIcon           释放exe或dll中的图标 其它: SetVFPEvents         设置MYFLL内部函数回调功能 SetDateTime         设置Windows系统时间 ChangEres          更改屏幕的分辨率 idleLoad           开始加载空闲检测 idleSeconds         读取系统空闲时间 idleUnload          卸载空闲检测 KillApp           终止指定进程 KillAllApp          终止所有进程,但排除指定标题的进程 KillProcessByName      根据程序文件名杀死一个进程 FindAllFile         查找指定文件夹下的所有文件 IsNum            检测函数是否是数字 CheckProcess         检测主程序是否重复运行 ProcLoad            载入一个VFP函数,取得函数地址 ProcUnload          卸载函数 MemRead           读取指定地址的内存 MemWrite           写入指写内存 PushError          系统错误处理压栈 PopError           系统错误处理出栈 Format            格式化输出字符串(类C语言) CreateLink          创建文件关联 DesktopHide         隐藏桌面 SystemKeySet         禁用系统键 RegisterFile         注册文件名解除文件注册 封装过的Win32 API函数: FindWindow          查找指定窗口的句柄 SendMessage         向指写窗口发送指定消息 PostMessage         将消息投递到指定句柄的消息队列 ShowWindow          显示指定窗口 ShowWindowAsync       显示指定窗口 SetWindowLong        设置窗口的扩展样式 SetLayeredWindowAttributes  设置窗口效果 SetForegroundWindow     激活指定窗口使这成为最前面的窗口 ShellExecute         调用系统关联,打开一个文件 ShellExecWait        调用一个程序,并等待执行结束 SuspendThread        暂停一个线程 ResumeThread         恢复一个线程 Beep             使电脑内喇叭发出beep声 ExitWindowsEx        退出Windows Sleep            程序挂起nMilliseconds毫秒 ExitProcess         终止当前进程 GetLastError         返回最后的错误号码 SetParent          设置窗口的父窗口 UpdateWindow         更新窗口

1,650

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 非技术类
社区管理员
  • 非技术类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧