windowsAPI函数实际用它的ANSI(非指ASCII)字符版本函数还是用它的宽字符版本函数取决于啥

来源:互联网 发布:图片整理软件lr 编辑:程序博客网 时间:2024/05/16 14:27

摘要小结:

对于一个windows API函数来说,其函数名结尾跟上A的函数版本为ANSI字符版本函数,A不是代表ASCII之意

char数据类型的变量不仅可以存储以ASCII码字符集编码的字符,也可以存储以多字节字符集(如简体中文字符集GBK)编码的字符如char str[20]="asdfg",str2[20]="中文汉字";

wchar数据类型的变量可以存储以UNICODE字符集编码的字符如wchar str[20]=L"asdfg",str2[20]=L"中文汉字"。

对于一个windows API函数来说,

它的ASCII版本函数(即该windows API函数的函数名结尾跟上A)的参数里有一个使用字符串的参数,那么ASCII版本函数使用的字符串只能是char数据类型定义的字符串(即单字节字符集(如,ASCII码字符集)或是多字节字符集编码的字符如char str[20]="asdfg";),而不能使用unicode字符集编码的字符串,即wchar数据类型定义的字符串如wchar str[20]=L"asdfg"。

它的宽字符(即unicode)版本函数(即该windows API函数的函数名结尾跟上W)则只能使用unicode字符集编码的字符串,即wchar数据类型定义的字符串而不能使用char数据类型定义的字符串(即单字节字符集(如,ASCII码字符集)或是多字节字符集编码的字符)。

注释:

1、确切地说是,char数据类型的变量可以存储以多字节字符集(如简体中文字符集GBK)编码的字符char数据类型的变量可以存储以ASCII码字符集编码的字符,是因为多字节字符集都是兼容ASCII码字符集的,即后者是前者中的一组成部分。

char数据类型的变量里存储的字符是具体为哪种多字节字符集(如是以简体中文字符集GBK还是以韩文字符集来编码的)编码的,则是看(Windows)操作系统中的系统区域设置里选择的是什么字符集了。例如,

假设在项目A里的一源代码里有一个字符串为charstr2[20]="中文汉字";,当前(Windows)操作系统中的系统区域设置里选择的是简体中文字符集GBK,则此时编译该项目时字符串"中文汉字"就按照简体中文字符集GBK里的编码值存储(在内存上)。编译好后,又将(Windows)操作系统中的系统区域设置里改为日语代码页946(或是在编译好后所得的exe文件所运行的进程所打开的控制台窗口的属性里的默认值中修改代码页为代码页946(或是oem 437)),则此时运行编译好后所得的exe文件,在其打开的控制台窗口上显示就不是"中文汉字"这几个字符,而是一些乱码。

2、更确切地说,无论以何种字符集来编码一字符串,该字符串的编码值都可以存储在任何数据类型的变量上,前提是要将字符串的编码值原原本本的复制到该变量上(而不经过一些数据类型间的隐式转换,即如char数据类型实质上是整型,只是读取解释为一个字符图形,即是整型Int,那其若要转换为float(形如0.39*e的5次方),则同样是5这个数值,存放在int变量里和存放在float变量里的值不一样。再例如,因为变量st的数据类型是filetime,filetime结构体中的两个变量是unsigned long类型的,而*(__int64 *)&st只是表示将(两)unsigned long类型(合并)的变量st的数据按照__int64(=longlong)来解释读取而已,并未实际将(两)unsigned long类型(合并)的变量st的数据转换成__int64(=longlong)数据类型格式的,所以__int64 d = *(__int64 *)&st;应该改为__int64 d =(__int64)st;参见:FileTime如何转换为Time_t)。所以说,无论以何种字符集来编码一字符串,该字符串的编码值既可以存储在char数据类型的变量上,也可以存储在wchar数据类型的变量上。(此处说明可以用于解:utf8编码的字符(如汉字 占三个字节)用啥字符数据类型储存 C++例如,以Unicode字符集编码的字符串“我是中国人”可以存储在char数据类型的变量上多字节字符集(如简体中文字符集GBK)编码的字符串“我是中国人”可以存储在wchar数据类型的变量上所以,wchar数据类型char数据类型的区别不在于它们能存放什么字符集编码的字符串。而wchar数据类型char数据类型的区别在于它们能正确读取显示什么字符集编码的字符串,即它们能用哪些字符集来读取解释显示存放在其上的编码值。那两者分别能用什么字符集来读取解释显示存放在其上的编码值呢?wchar数据类型只能用Unicode字符集来读取解释显示存放在其上的编码值,而char数据类型则是看(Windows)操作系统中的系统区域设置里选择的是什么字符集了,假如当前(Windows)操作系统中的系统区域设置里选择的是UTF8字符集,则char数据类型当前可以用UTF8字符集来读取解释显示存放在其上的编码值,假如当前(Windows)操作系统中的系统区域设置里选择的是简体中文字符集GBK(即一种多字节字符集),则char数据类型当前可以简体中文字符集GBK来读取解释显示存放在其上的编码值,假如当前(Windows)操作系统中的系统区域设置里选择的是ASCII字符集(即一种单字节字符集),则char数据类型当前可以ASCII字符集来读取解释显示存放在其上的编码值,即char数据类型可以根据(Windows)操作系统中的系统区域设置里选择的字符集来显示除了Unicode字符集以外的任何一种字符集,而wchar数据类型只能用Unicode字符集来读取解释显示存放在其上的编码值所以,例如,以Unicode字符集编码的字符串“我是中国人”虽然可以存储在char数据类型的变量上,但是显示时会是乱码,UTF8字符集或是多字节字符集(如简体中文字符集GBK)编码的字符串“我是中国人”虽然可以存储在wchar数据类型的变量上,但是显示时会是乱码

当然,像char str[20]="sdf中国"或是wchar str[20]="sdf中国"中要复制给数组str的值"sdf中国"是根据当前(Windows)操作系统中的系统区域设置里选择的字符集来编码的,而像char str[20]=L"sdf中国"或是wchar str[20]=L"sdf中国"中要复制给数组str的值"sdf中国"是根据Unicode字符集来编码的(因为字符串"sdf中国"前有个L这个标志,意即叫编译器用Unicode字符集来编码字符串)。同一个字符串的不同字符集编码值间的转换可以用如下两个函数:

MultiByteToWideChar与WideCharToMultiByte       

例如,

宽字符到多字节字符转换函数,函数原型如下:


int WideCharToMultiByte( 
UINT CodePage, 
DWORD dwFlags, 
LPCWSTR lpWideCharStr, 
int cchWideChar, 
LPSTR lpMultiByteStr, 
int cbMultiByte, 
LPCSTR lpDefaultChar, 
LPBOOL lpUsedDefaultChar 
);


此函数把宽字符串转换成指定的新的字符串,如ANSI,UTF8等。参数:


CodePage: 指定要转换成的字符集代码页,它可以是任何已经安装的或系统自带的字符集,你也可以使用如下所示代码页之一。 
CP_ACP 当前系统ANSI代码页 
CP_MACCP 当前系统Macintosh代码页 
CP_OEMCP 当前系统OEM代码页,一种原始设备制造商硬件扫描码 
CP_SYMBOL Symbol代码页,用于Windows 2000及以后版本,我不明白是什么 
CP_THREAD_ACP 当前线程ANSI代码页,用于Windows 2000及以后版本,我不明白是什么 
CP_UTF7 UTF-7,设置此值时lpDefaultChar和lpUsedDefaultChar都必须为NULL 
CP_UTF8 UTF-8,设置此值时lpDefaultChar和lpUsedDefaultChar都必须为NULL 

我想最常用的应该是CP_ACP和CP_UTF8了,前者将宽字符转换为ANSI,后者转换为UTF8。


dwFlags: 指定如何处理没有转换的字符, 但不设此参数函数会运行的更快一些,我都是把它设为0。 可设的值如下表所示: 
WC_NO_BEST_FIT_CHARS 把不能直接转换成相应多字节字符的Unicode字符转换成lpDefaultChar指定的默认字符。也就是说,如果把Unicode转换成多字节字符,然后再转换回来,你并不一定得到相同的Unicode字符,因为这期间可能使用了默认字符。此选项可以单独使用,也可以和其他选项一起使用。 
WC_COMPOSITECHECK 把合成字符转换成预制的字符。它可以与后三个选项中的任何一个组合使用,如果没有与他们中的任何一个组合,则与选项WC_SEPCHARS相同。 
WC_ERR_INVALID_CHARS 此选项会致使函数遇到无效字符时失败返回,并且GetLastError会返回错误码ERROR_NO_UNICODE_TRANSLATION。否则函数会自动丢弃非法字符。此选项只能用于UTF8。 
WC_DISCARDNS 转换时丢弃不占空间的字符,与WC_COMPOSITECHECK一起使用 
WC_SEPCHARS 转换时产生单独的字符,此是默认转换选项,与WC_COMPOSITECHECK一起使用 
WC_DEFAULTCHAR 转换时使用默认字符代替例外的字符,(最常见的如’?’),与WC_COMPOSITECHECK一起使用。 



3、

对于一个windows API函数来说,

它的ASCII版本函数(即该windows API函数的函数名结尾跟上A)的参数里有一个使用字符串的参数,那么ASCII版本函数使用的字符串的编码值可以是单字节字符集(如,ASCII码字符集)也可以是多字节字符集编码的也可以是UTF8字符集编码的(反正是除了Unicode字符集之外的其他字符集),只是不能使用unicode字符集编码的。它的ASCII版本函数当前具体能处理什么字符集也是要看(Windows)操作系统中的系统区域设置里选择的是什么字符集(?)。


上述这种windows API函数是有预定义的宏来做函数各版本间选择的开关的一种函数封装的形式。而类似strcpy函数以及它的宽字符版本wcscpy和多字节字符集版本_mbcscpy的一组函数区别于上述所说的windows API函数就是它没有有预定义的宏来做函数各版本间选择的开关将各个版本函数组织起来对外形成一个统一的接口函数。


对于类似strcpy函数以及它的宽字符版本wcscpy和多字节字符集版本_mbcscpy来说,

strcpy函数只能处理单字节字符集ASCII;

它的宽字符版本wcscpy只能处理Unicode字符集;

多字节字符集版本_mbcscpy来说只能处理多字节字符集(不知道能否处理如UTF8字符集)。

4、


vs2010中一个项目的属性的字符集选项的作用就是起一个开关选择的作用,即windows API函数实际使用哪个函数版本以及TCHAR数据类型实际使用哪个具体版本的数据类型(即是char还是wchar)。


5、

编译(或进程运行前的准备时期)时  对变量的初始化,也就是说可执行文件(即编译后的代码里),含有这些初始值。例如,源码里一字符串的初始值为“中国”,编译时,操作系统的字符集为gbk,则该初始值“中国”就按其在gbk中的编码值存放在可执行文件里。每个初始值根据自己书写方式不同有自己默认的数据类型  可不同于它要初始化的变量的类型。如整数值默认为int  l带为long
wchar char都是一个字符数据类型,只不过用了不同字符集来解读
运行时  才对变量赋值

初始化值是以常量(里值不可改变修改)的形式的内存空间存放在程序全局区内存部分。例如,char *P=“fffDHC”;在一函数里,函数返回值是P的值,函数外用该值依然可以访问该字符串常量,说明该常量在全局区。
淡然,没有 int *P=&5
虽然5这个常量也在代码区(或是编译时分配其到全局区),但是编译时5没有常量的地址值这个属性
参考:

编译原理

编译器编译时会对代码做各种隐式转化什么的(如初始化或是赋值的数据类型隐式转化


正文:


对于一个windows API函数最后实际是用它的ASCII版本函数(即该windows API函数的函数名结尾跟上A)还是用它的宽字符(即unicode)版本函数(即该windows API函数的函数名结尾跟上W),取决于相关设置。

相关设置有两种方式。一种影响范围为整个项目,一种影响范围为一个(含有#include"stdafx.h"的)cpp文件。

一种影响范围为整个项目:

选中一个项目,右键点击属性,跳出窗口里选择字符集来设置。

 

一种影响范围为一个(含有#include"stdafx.h"的)cpp文件:

假设当前项目-》属性-》字符集里的值为“使用unicode字符集”,而你想在项目里的某些cpp文件上使用多字节字符集编码的字符串,则你可以在stdafx.h里写上一语句:#undefUNICODE,并在那些要使用多字节字符集编码的字符串得cpp文件上写上一语句:#include"stdafx.h"即可。

那么你如何知道刚才的设置已经起作用了呢?

两种方法:

1、         如果你装了VX插件,则鼠标移到一windowsAPI函数上时会有如下显示:


2、         在windowsAPI函数上右键选“转到定义”之后看到当前不是灰色的函数版本即为起作用的函数版本:

 

假设当前项目-》属性-》字符集里的值为“使用多字节字符集”,而你想在项目里的某些cpp文件上使用unicode字符集编码的字符串,则你可以在stdafx.h里写上一语句:#defineUNICODE,并在那些要使用多字节字符集编码的字符串得cpp文件上写上一语句:#include"stdafx.h"即可。



 附加:

1、

至于像#defineUNICODE或是#undefUNICODE一定要放在stdafx.h里才起作用,而直接放在那些要使用多字节字符集编码的字符串得cpp文件上不起作用的原因暂时不知了。反正,程序员自己在cpp文件上定义的宏是可以在该cpp文件上起作用的。例如,在一cpp文件上

// #define ttttt 

//

 #ifdefttttt

 intG_r=5;//没定义宏ttttt时该句就是灰色的,有定义则不是灰的,这说明定义的宏起作用了。

 #endif


 2、

#ifdefUNICODE

#defineGetUserName GetUserNameW

#else

#defineGetUserName GetUserNameA

#endif// !UNICODE

从上述定义模板设计可以看出,对于一个windows API函数要用它的ASCII版本函数(即该windows API函数的函数名结尾跟上A)还是用它的宽字符(即unicode)版本函数(即该windows API函数的函数名结尾跟上W),是利用定义或是取消定义宏UNICODE,而定义或是取消定义宏MBCS之类的名字不起作用的。

 3、

对于在tchar.h里定义的TCHAR数据类型,上述第一种方法对其是起作用的:

如果你装了VX插件,

假设当前项目-》属性-》字符集里的值为“使用多字节字符集”,则鼠标移到TCHAR数据类型上时会有如下显示:

 

typedefchar   TCHAR;

 

假设当前项目-》属性-》字符集里的值为“使用unicode字符集”,则鼠标移到TCHAR数据类型上时会有如下显示:

 

typedefwchar_t    TCHAR;

 

 

上述第二种方法对tchar.h里定义的TCHAR数据类型是不起作用的,原因暂时不知。原因应该是

tchar.h 头文件里,使用 _UNCODE 定义而不是使用UNCODE 定义

#ifdef  _UNICODE

... ...                    

#define __T(x)  L##x             /* for UNICODE */

... ...

#else

... ...

#define __T(x)  x               /* for ANSI */

... ...
 
#endif


#define _T(x)  __T(x)

上述第二种方法只要改为定义或是取消定义宏_UNCODE即可起作用。

参见:

宏UNICODE VS 宏_UNICODE 与 TEXT() VS _T()


4、

char数据类型的变量不仅可以存储以ASCII码字符集编码的字符,也可以存储以多字节字符集(如简体中文字符集GBK)编码的字符如char str[20]="asdfg",str2[20]="中文汉字";

wchar数据类型的变量可以存储以UNICODE字符集编码的字符如wchar str[20]=L"asdfg",str2[20]=L"中文汉字"。

对于一个windows API函数来说,

它的ASCII版本函数(即该windows API函数的函数名结尾跟上A)的参数里有一个使用字符串的参数,那么ASCII版本函数使用的字符串只能是char数据类型定义的字符串(即单字节字符集(如,ASCII码字符集)或是多字节字符集编码的字符如char str[20]="asdfg";),而不能使用unicode字符集编码的字符串,即wchar数据类型定义的字符串如wchar str[20]=L"asdfg"。

它的宽字符(即unicode)版本函数(即该windows API函数的函数名结尾跟上W)则只能使用unicode字符集编码的字符串,即wchar数据类型定义的字符串而不能使用char数据类型定义的字符串(即单字节字符集(如,ASCII码字符集)或是多字节字符集编码的字符)。


不要以为当前项目-》属性-》字符集里的值为“使用unicode字符集”后,该项目里出现的字符串就都是unicode字符集编码的字符了。字符串是由哪种字符集编码的,取决于定义该字符串的数据类型是char数据类型还是wchar数据类型

当前项目-》属性-》字符集,设置该字符集的值,会对源代码的那些对象有影响?对windows API函数和TCHAR数据类型有影响,因为windows API函数和TCHAR数据类型相当于一个开关函数对象。当前项目-》属性-》字符集里的值为“使用unicode字符集”时,windows API函数实际使用它的宽字符(即unicode)版本函数(即该windows API函数的函数名结尾跟上W)TCHAR数据类型实际使用它的wchar数据类型版本;当前项目-》属性-》字符集里的值为“使用多字节字符集”时,windows API函数实际使用它的ASCII版本函数(即该windows API函数的函数名结尾跟上A)TCHAR数据类型实际使用它的char数据类型版本。所以,在源代码里,windows API函数和TCHAR数据类型两者要配套起来使用,出现windows API函数时,如果它要使用字符串就要用TCHAR数据类型定义该字符串。

例如,

TCHAR szUser[80]=T("s");//T()这个宏也是起个开关选择作用,即当前项目-》属性-》字符集里的值为“使用unicode字符集”时,T()里的字符串就是用Unicode字符集编码的;当前项目-》属性-》字符集里的值为“使用多字节字符集”时,T()里的字符串就是用多字节字符集编码的。

DWORD cbUser=80;

getUserName(szUser,&cbUser);


再例如,

假设当前项目-》属性-》字符集里的值为“使用unicode字符集”时,


CHAR szUser[80];

DWORD cbUser=80;

getUserName(szUser,&cbUser);

运行上述代码就会提示错误,

因为函数getUserName此时实际用的是getUserNameW,而该getUserNameW函数不能使用char类型的字符串,只能用wchar类型的。

注释:

CHAR szUser[80];

DWORD cbUser=80;

getUserName(szUser,&cbUser);

第二个参数cbUser是一个输入输出参数,即它既是输入参数又是输出参数。

首先,它在函数里起的作用是输入参数,作为输入参数,它的作用是告诉函数数组szUser的大小多大,故而cbuser的初始值可以小于80只要不大于数组实际大小即是安全的,虽然大于数组实际大小,函数也不一定使用那么多,所以也不一定出错函数执行时。

最后,它作为一个输出参数时,起的作用是当函数运行好后函数将用户名这个字符串的长度(即szuser实际被使用的大小)保存在参数cbuser中。

参考:

预编译头以及windowsAPI函数实际用它的ASCII版本函数还是用它的宽字符版本函数取决于啥



0 0
原创粉丝点击