当前位置: 首页 > news >正文

Windows程序设计学习笔记--第一个Windows程序以及宽字符集(了解)

Hello World程序。


1.“Hello World” c语言 版本

先是c语言的标准代码如下:

#include <stdio.h>

int main ()

{

           printf ("hello, world\n") ;

           return 0 ;

}

2."Hello World" Windows 版本

下面是Windows程序的标准代码:(摘自P先生的书)

/*------------------------------------------------------------------

HelloMsg.c -- Displays "Hello, Windows 98!" in a message box

    (c) Charles Petzold, 1998

--------------------------------------------------------------------*/

#include <windows.h>

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

                   PSTR szCmdLine, int iCmdShow)

{

MessageBox (NULL, TEXT ("Hello, Windows 98!"), TEXT ("HelloMsg"), 0);

return 0 ;

}

Windows关于「hello, world」程序的等价程序有和文字模式版本与c语言版的有完全相同的组件。 它有一个include声明、一个程序进入点、一个函数呼叫和一个return语句。


首先关于包含文件*.h:

#include <windows.h>

WINDOWS.H是主要的含入文件,它包含了其它Windows表头文件,这些表头文件的某些也包含了其它表头文件。这些表头文件中最重要的和最基本的是:

  • WINDEF.H 基本型态定义。
  • WINNT.H 支持Unicode的型态定义。
  • WINBASE.H Kernel函数。
  • WINUSER.H 使用者接口函数
  • WINGDI.H 图形设备接口函数。

其次是:程序进入点

正如在C程序中的进入点是函数main一样,Windows程序的进入点是WinMain,总是像这样出现:

int WINAPI WinMain (      HINSTANCE hInstance,HINSTANCE hPrevInstance,

           PSTR szCmdLine,int iCmdShow)


这就是第一个Windows程序。

经典的Hello World。


3.关于字符集

其实关于Unicode码的信息看了很多了。

这次重点说一下宽字符集和Windows。

关于宽字符集,最近看了《c语言核心技术》才了解的。

c语言的版本:

下面的语句定义并初始化了一个只包含一个字符的变量:

char c = 'A' ;     

变量c需要1个字节来保存,并将用十六进制数0x41初始化,这是字母A的ASCII代码。

您可以像这样定义一个指向字符串的指针:

char * p ;

您还可以像这样定义字符数组:
char a[10] ;      
在这种情况下,编译器为该数组保留了10个字节的储存空间。表达式sizeof(a)将返回10。如果数组是整体变量(即在所有函数外定义),您可使用像下面的语句来初始化一个字符数组:
char a[] = "Hello!" ;
如果您将该数组定义为一个函数的区域变量,则必须将它定义为一个static变量,如下:
static char a[] = "Hello!" ;
无论哪种情况,字符串都储存在静态程序内存中,并在末尾添加0,这样就需要7个字节的储存空间。

Windows版本:

因为假设Windows是一个32位操作系统,所以指针变量p需要用4个字节保存。您还可初始化一个指向字符串的指针:

char * p = "Hello!" ;       

像前面一样,变量p也需要用4个字节保存。该字符串保存在静态内存中并占用7个字节-6个字节保存字符串,另1个字节保存终止符号0。

宽字符

Unicode或者宽字符都没有改变char数据型态在C中的含义。char继续表示1个字节的储存空间,sizeof (char)继续返回1。理论上,C中1个字节可比8位长,但对我们大多数人来说,1个字节(也就是1个char)是8位宽。

C中的宽字符基于wchar_t数据型态,它在几个表头文件包括WCHAR.H中都有定义,像这样:

typedef unsigned short wchar_t ;      

因此,wchar_t数据型态与无符号短整数型态相同,都是16位宽。

要定义包含一个宽字符的变量,可使用下面的语句:

wchar_t c = 'A' ;       

变量c是一个双字节值0x0041,是Unicode表示的字母A。(然而,因为Intel微处理器从最小的字节开始储存多字节数值,该字节实际上是以0x41、0x00的顺序保存在内存中。如果检查Unicode文字的计算机储存应注意这一点。)

您还可定义指向宽字符串的指针:

wchar_t * p = L"Hello!" ;      

注意紧接在第一个引号前面的大写字母L(代表「long」)。这将告诉编译器该字符串按宽字符保存-即每个字符占用2个字节。通常,指针变量p要占用4个字节,而字符串变量需要14个字节-每个字符需要2个字节,末尾的0还需要2个字节。

同样,您还可以用下面的语句定义宽字符数组:

static wchar_t a[] = L"Hello!" ;   

该字符串也需要14个字节的储存空间,sizeof (a) 将返回14。索引数组a可得到单独的字符。a[1] 的值是宽字符「e」,或者0x0065。

虽然看上去更像一个印刷符号,但第一个引号前面的L非常重要,并且在两个符号之间必须没有空格。只有带有L,编译器才知道您需要将字符串存为每个字符2字节。稍后,当我们看到使用宽字符串而不是变量定义时,您还会遇到第一个引号前面的L。幸运的是,如果忘记了包含L,C编译器通常会给提出警告或错误信息。

您还可在单个字符文字前面使用L前缀,来表示它们应解释为宽字符。如下所示:

wchar_t c = L'A' ;

但通常这是不必要的,C编译器会对该字符进行扩充,使它成为宽字符。

小问题:

C语言本身和执行时期链接库函数之间的区别。编译器将字符串L"Hello!" 解释为一组16位短整数型态数据,并将其保存在wchar_t数组中。编译器还处理数组索引和sizeof操作符,因此这些都能正常工作,但在连结时才添加执行时期链接库函数,例如strlen。这些函数认为字符串由单字节字符组成。遇到宽字符串时,函数就不像我们所希望那样执行了。

宽字符集的字符串处理函数:

strlen函数的宽字符版是wcslen(wide-character string length:宽字符串长度),并且在STRING.H(其中也说明了strlen)和WCHAR.H中均有说明。strlen函数说明如下:

size_t __cdecl strlen (const char *) ;        

而wcslen函数则说明如下:

size_t __cdecl wcslen (const wchar_t *) ;        

这时我们知道,要得到宽字符串的长度可以呼叫

iLength = wcslen (pw) ; 

函数将返回字符串中的字符数6。请记住,改成宽字节后,字符串的字符长度不改变,只是位组长度改变了。

您熟悉的所有带有字符串参数的C执行时期链接库函数都有宽字符版。例如,wprintf是printf的宽字符版。这些函数在WCHAR.H和含有标准函数说明的表头文件中说明。


Then “L” Problem:

现在开始讨论字符串文字中的L问题。如果定义了_UNICODE标识符,那么一个称作__T的宏就定义如下:

#define __T(x) L##x        

这是合乎ANSI C标准的前置处理器规范。那一对井字号称为「粘贴符号(token paste)」,它将字母L添加到宏参数上。因此,如果宏参数是"Hello!",则L##x就是L"Hello!"。

如果没有定义_UNICODE标识符,则__T宏只简单地定义如下:

#define __T(x) x        

此外,还有两个宏与__T定义相同:

#define _T(x)__T(x)        
#define _TEXT(x)__T(x)        

在Win32 console程序中使用哪个宏,取决于您喜欢简洁还是详细。基本地,必须按下述方法在_T或_TEXT宏内定义字符串文字:

_TEXT ("Hello!")        

这样做的话,如果定义了_UNICODE,那么该串将解释为宽字符的组合,否则解释为8位的字符字符串。


最后:

宽字符和 Windows:

Windows NT从底层支援Unicode。这意味着Windows NT内部使用由16位字符组成的字符串。因为世界上其它许多地方还不使用16位字符串,所以Windows NT必须经常将字符串在操作系统内转换。Windows NT可执行为ASCII、Unicode或者ASCII和Unicode混合编写的程序。即,Windows NT支持不同的API函数呼叫,这些函数接受8位或16位的字符串(我们将马上看到这是如何动作的。)

相对于Windows NT,Windows 98对Unicode的支持要少得多。只有很少的Windows 98函数呼叫支持宽字符串(这些函数列在《Microsoft Knowledge Base article Q125671》中;它们包括MessageBox)。如果要发行的程序中只有一个.EXE文件要求在Windows NT和Windows 98下都能执行,那么就不应该使用Unicode,否则就不能在Windows 98下执行;尤其程序不能呼叫Unicode版的Windows函数。这样,将来发行Unicode版的程序时会处于更有利的位置,您应试着编写既为ASCII又为Unicode编译的原始码。这就是本书中所有程序的编写方式。


Windows的字符串函数

正如前面谈到的,Microsoft C包括宽字符和需要字符串参数的C语言执行时期链接库函数的所有普通版本。不过,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) ;     

这些函数与C链接库中对应的函数功能相同。如果定义了UNICODE标识符,那么这些函数将接受宽字符串,否则只接受常规字符串。宽字符串版的lstrlenW函数可在Windows 98中执行。

在Windows中使用printf

在Windows程序中不能使用printf。虽然Windows程序中可以使用大多数C的执行时期链接库-实际上,许多程序写作者更愿意使用C内存管理和文件I/O函数而不是Windows中等效的函数-Windows对标准输入和标准输出没有概念。在Windows程序中可使用fprintf,而不是printf。

还有一个好消息,那就是仍然可以使用sprintf及sprintf系列中的其它函数来显示文字。这些函数除了将内容格式化输出到函数第一个参数所提供的字符串缓冲区以外,其功能与printfI相同。然后便可对该字符串进行操作(例如将其传给MessageBox)。

实例:

格式化消息框

程序2-1所示的SCRNSIZE程序展示了如何实现MessageBoxPrintf函数,该函数有许多参数并能像printf那样编排它们的格式。


 
程序2-1  SCRNSIZE
        
SCRNSIZE.C
        
/*---------------------------------------------------------------------------
        
    SCRNSIZE.C -- Displays screen size in a message box
        
                 (c) Charles Petzold, 1998
        
----------------------------------------------------------------------------*/
        
#include <windows.h>
        
#include <tchar.h>   
        
#include <stdio.h>   
        
int CDECL MessageBoxPrintf (TCHAR * szCaption, TCHAR * szFormat, ...)
        
{
        
    TCHAR   szBuffer [1024] ;
        
    va_list pArgList ;
        

    // The va_start macro (defined in STDARG.H) is usually equivalent to:
        
    // pArgList = (char *) &szFormat + sizeof (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 ;
        
}
        


经由从GetSystemMetrics函数得到的信息,该程序以图素为单位显示了视讯显示的宽度和高度。GetSystemMetrics是一个能用来获得Windows中不同对象的尺寸信息的函数。














相关文章:

  • 云计算和大数据入门
  • Windows Azure使用必读
  • Windows Azure新功能:Hadoop和Web版的移动服务
  • 云计算的理解
  • 微软云存储SkyDrive API:将你的数据连接到任何应用、任何平台,及任何设备上...
  • 在 Windows Azure 上部署预配置 Oracle VM
  • 对于Visual Studio 2013中 Blend的HTML开发人员来说什么是新的
  • 微软云服务获政府认证 云服务或将迎来黄金期
  • SAE 搭建 WordPress
  • 自定义 WordPress 样式
  • 比较了一下基于PhoneGAP/JQ Mobile 等基于HTML5的Phone 开发框架
  • (poj1.2.1)1970(筛选法模拟)
  • 关于nmap的几个技巧
  • Erlang几种判断语句:if、case等
  • (pojstep1.3.1)1017(构造法模拟)
  • 【Leetcode】101. 对称二叉树
  • @jsonView过滤属性
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • Android 架构优化~MVP 架构改造
  • Apache Spark Streaming 使用实例
  • CSS相对定位
  • es6
  • gulp 教程
  • Java的Interrupt与线程中断
  • Java应用性能调优
  • Laravel 实践之路: 数据库迁移与数据填充
  • Redis 中的布隆过滤器
  • spark本地环境的搭建到运行第一个spark程序
  • Vultr 教程目录
  • 翻译 | 老司机带你秒懂内存管理 - 第一部(共三部)
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 基于游标的分页接口实现
  • 今年的LC3大会没了?
  • 设计模式(12)迭代器模式(讲解+应用)
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 世界上最简单的无等待算法(getAndIncrement)
  • 我从编程教室毕业
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 在electron中实现跨域请求,无需更改服务器端设置
  • elasticsearch-head插件安装
  • SAP CRM里Lead通过工作流自动创建Opportunity的原理讲解 ...
  • UI设计初学者应该如何入门?
  • ​520就是要宠粉,你的心头书我买单
  • #pragam once 和 #ifndef 预编译头
  • $refs 、$nextTic、动态组件、name的使用
  • (react踩过的坑)antd 如何同时获取一个select 的value和 label值
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (一)80c52学习之旅-起始篇
  • * 论文笔记 【Wide Deep Learning for Recommender Systems】
  • .apk 成为历史!
  • .NET BackgroundWorker
  • .NET CLR Hosting 简介
  • .Net Core webapi RestFul 统一接口数据返回格式
  • .NET MVC 验证码