4.Windows编程中使用的各种数据类型
大量类型自定义
一个Windows程序包括表头文件windows.h。该文件包括许多其它表头文件,包括windef.h,该文件中有许多在Windows中使用的基本型态定义,而且它本身也包括winnt.h。winnt.h处理基本的Unicode支持。
winnt.h的前面包含C的表头文件ctype.h,这是C的众多表头文件之一,包括wchar_t的定义。winnt.h定义了新的数据型态,称作CHAR和WCHAR:
typedef char CHAR ;
typedef wchar_t WCHAR ; // wc
当您需要定义8位字符或者16位字符时,推荐您在Windows程序中使用的数据型态是CHAR和WCHAR。WCHAR定义后面的注释是匈牙利标记法的建议:一个基于WCHAR数据型态的变量可在前面附加上字母wc以说明一个宽字符。
8位数据
winnt.h表头文件进而定义了可用做8位字符串指针的六种数据型态和四个可用做const 8位字符串指针的数据型态。(一个基础类型被定义成不同标识符,看起来又多又乱)
typedef CHAR * PCHAR, * LPCH, * PCH, * NPSTR, * LPSTR, * PSTR ;
typedef CONST CHAR * LPCCH, * PCCH, * LPCSTR, * PCSTR ;
好了,在第一个Windows窗口程序中
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)
程序进入点的第三个参数前的类型PSTR就是其实就是一个char指针,用来以字符串形式接受命令行参数,前缀N和L表示「near」和「long」,指的是16位Windows中两种大小不同的指标。在Win32中near和long指标没有区别。
16位数据
类似地,WINNT.H定义了六种可作为16位字符串指针的数据型态和四种可作为const 16位字符串指针的数据型态:
typedef WCHAR * PWCHAR, * LPWCH, * PWCH, * NWPSTR, * LPWSTR, * PWSTR ;
typedef CONST WCHAR * LPCWCH, * PCWCH, * LPCWSTR, * PCWSTR ;
至此,我们有了数据型态CHAR(一个8位的char)和WCHAR(一个16位的wchar_t),以及指向CHAR和WCHAR的指针。与TCHAR.H一样,WINNT.H将TCHAR定义为一般的字符类型。如果定义了标识符UNICODE(没有底线),则TCHAR和指向TCHAR的指标就分别定义为WCHAR和指向WCHAR的指标;如果没有定义标识符UNICODE,则TCHAR和指向TCHAR的指针就分别定义为char和指向char的指针:
#ifdef UNICODE
typedef WCHAR TCHAR, * PTCHAR ;
typedef LPWSTR LPTCH, PTCH, PTSTR, LPTSTR ;
typedef LPCWSTR LPCTSTR ;
#else
typedef char TCHAR, * PTCHAR ;
typedef LPSTR LPTCH, PTCH, PTSTR, LPTSTR ;
typedef LPCSTR LPCTSTR ;
#endif
如果已经在某个表头文件或者其它表头文件中定义了TCHAR数据型态,那么WINNT.H和WCHAR.H表头文件都能防止其重复定义。不过,无论何时在程序中使用其它表头文件时,都应在所有其它表头文件之前包含WINDOWS.H。(否则在包含其他外部库时,会产生各种错误)
再谈MessageBox()
在16位Windows中,MessageBox函数位于动态链接库USER.EXE,MessageBox()函数定义如下
int WINAPI MessageBox (HWND, LPCSTR, LPCSTR, UINT) ;
注意,LPCSTR是8位的。 当编译连结一个Win16程序时,Windows并不处理MessageBox调用。Windows将该程序的调用与USER.exe中的MessageBox函数动态链接起来。
32位的Windows(即所有版本的Windows NT,以及Windows 95和Windows 98,当然还有现在的64位)除了含有与16位兼容的USER.EXE以外,还含有一个称为USER32.DLL的动态链接库,该动态链接库含有32位使用者接口函数的进入点,包括32位的MessageBox。
在USER32.DLL中,没有32位MessageBox函数的进入点。实际上,有两个进入点,一个名为MessageBoxA(ASCII版),另一个名为MessageBoxW(宽字符版)。用字符串作参数的每个Win32函数都在操作系统中有两个进入点!幸运的是,您通常不必关心这个问题,程序中只需使用MessageBox。
如果需要同时使用并分别匹配ASCII和宽字符函数呼叫,那么您可在Windows程序中明确地使用MessageBoxA和MessageBoxW函数。但大多数程序写作者将继续使用MessageBox。根据是否定义了UNICODE,MessageBox将与MessageBoxA或MessageBoxW一样。
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif
这样,如果定义了UNICODE标识符,那么程序中所有的MessageBox函数呼叫实际上就是MessageBoxW函数;否则,就是MessageBoxA函数。
Windows的字符串函数
下面是Windows定义的一组字符串函数,这些函数用来计算字符串长度、复制字符串、连接字符串和比较字符串:
ILength = lstrlen (pString) ;
pString = lstrcpy (pString1, pString2) ;
pString = lstrcpyn (pString1, pString2, iCount) ;
pString = lstrcat (pString1, pString2) ;
iComp = lstrcmp (pString1, pString2);
iComp = lstrcmpi (pString1, pString2);//比较字符串,不区分大小写
在Windows中使用printf
在vs中新建一个Windows桌面项目时,常用的命令行打印printf是无效的,(没有进入点main,项目属性下链接器选项的子系统为窗口)。但在前面加上这一句就可以了,printf后记得使用getchar()等函数中断哦。
#pragma comment( linker, "/subsystem:\"console\" /entry:\"WinMainCRTStartup\"")
可以使用sprintf及sprintf系列中的其它函数来显示文字。这些函数除了将内容格式化输出到函数第一个参数所提供的字符串缓冲区以外,其功能与printf相同。然后便可对该字符串进行操作(例如将其传给MessageBox)。真麻烦
sprintf函数定义如下:
int sprintf (char * szBuffer, const char * szFormat, ...) ;
格式化消息框
vsprintf是sprintf的一个变形,它的声明如下,也是将格式化内容输入到str字符串
int vsprintf(char *str, const char *format, va_list arg)
str参数是保存结果的字符缓冲区 ,format是格式化字符串,arg是指向可变参数数列表的指针
使用vsprintf重写sprintf
首先介绍几个宏:
va_srart
启动对可变参数实参的访问,其定义如下
void va_start( std::va_list ap, parm_n );
va_start 宏允许访问具名参数 parm_n 之后的可变参数。
va_list
提供适用于va_start va_argva_end所需信息的完整对象类型,定义如下
typedef char* va_list; //在我的环境中
//在C++参考手册上该类型是未定义的
//typedef /* unspecified */ va_list
va_arg
获取va_list宏的每个参数, va_arg 宏的每次调用都修改 ap ,令它指向下个可变参数。
T va_arg( std::va_list ap, T );
T是可变参数的类型
va_end
对va_start初始化的对象进行清理,可以修改va_list对象并使它不可用,定义如下:
void va_end(va_list ap);
在调用va_arg之前,ap必须通过va_start初始化,参数使用完毕后使用va_start清理
一个简单的示例:
#include<iostream>
#include<cstdarg>
double MySum(int count, ...)
{
double result = 0;
va_list ap;
va_start(ap, count);
for (int i = 0; i < count; i++)
{
result += va_arg(ap, double);
}
va_end(ap);
return result;
}
int main()
{
int count = 5;
double result = 0;
result = MySum(count, (double)1.2, (double)2, (double)2, (double)2,(double)2);//这个宏展开的时候,类型不会自动转换,要显式转换
std::cout << result << std::endl;
getchar();
return 0;
}
格式化消息框
书上使用了_vsntprintf宏函数,在定义了UNICODE时展开为_vsnwprintf,定义如下:
int _vsnwprintf(wchar_t *buffer, size_t count, const wchar_t *format, va_list argptr);
使用指定参数列表的指针argptr,以format格式写入buffer缓存,count是写入的最大字符数,返回值是写入的字符数
大概不用sprintf或者vsprintf是因为可以指定字符数,更加安全
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
int CDECL MessageBoxPrintf(const TCHAR* szCaption, const TCHAR* szFormat, ...)
{
TCHAR szBuffer[1024]={0};
va_list pArgList;
// The va_start macro (defined in STDARG.H) is usually equivalent to:
// pArgList = (char *) &szFormat + sizeof (szFormat) ; 以单字节读取szFormat的首地址,然后向后偏移szFormat的内存长度得到参数列表的首地址
va_start(pArgList, szFormat);
// The last argument to wvsprintf points to the arguments
_vsntprintf(szBuffer, sizeof(szBuffer) / sizeof(TCHAR),szFormat, pArgList);
// The va_end macro just zeroes out pArgList for no good reason
va_end(pArgList);
return MessageBox(NULL, szBuffer, szCaption, 0);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
int cxScreen, cyScreen;
cxScreen = GetSystemMetrics(SM_CXSCREEN);//获取屏幕横向尺寸
cyScreen = GetSystemMetrics(SM_CYSCREEN);//获取屏幕纵向尺寸
MessageBoxPrintf(TEXT("ScrnSize"),
TEXT("The screen is %i pixels wide by %i pixels high."),
cxScreen, cyScreen);
return 0;
}
运行结果

这就等于是窗口版的printf了

浙公网安备 33010602011771号