【白话Windows编程】第一章 看透Windows之一
第一篇 看透 Windows“看透Windows”前言终于开始Windows编程了。我们也无可避免地,开始和Windows打交道。记得在《白话C++》里,我们试图用“程序员”的眼光来重新认识什么叫电脑,认为电脑中的一切都是0和1。现在,更艰巨的任务摆在我们面前。本课程前些章节的内容就是:看透Windows。(不要和我抬杠,说Windows那么复杂,你怎么能看得透,你明白,我们的任务主要是尽量从程序的角度来看Windows这个操作系统。)从 Windows3.1 到 Windows 95、98、Me、及今天的 Windows XP,其间还有WindowsNT,Windows 2000。Windows这一操作系统,早已经遍布世界上各个角落上的电脑。所以你应该对Windows并不陌生。但是你以前不是程序员,或者说不是“Windows 程序员”。所以,你看到的Windows,一定和我看到的,不太一样。当我把自己的身份切换“最终的电脑用户”时,我也和许多人一样,骂过Windows,骂它体积太大,速度太慢,容易死机,很不安全。而当我意识到自己是一个程序员时,我就闭嘴了。我可以保证,学习所有的“看透Windows”,你就是一个标准的“Windows"程序员——因为你立可就可以写出多个真正的Windows程序。并且这些程序个个直达 Windows世界的要点。到时,你也会我一样,明白“Windows 不是一日可以写成的”。从而对Windows不再有抱怨。当然,我们的身份是双重的,你随时可以让把自己的身份强行切换到“最终用户”,然后结合你所学的程序知识,在Windows身上出点气。而且,其实我也很乐意教这些的:D你看看这张图,一会儿我们就要写这么一个程序: (图1:在Windows桌面上打个大叉)想否认Windows的价值?最直观而又无恶意的作法之一,就是在它的桌面上打个红叉吧。言归正传,我们要看透Windows什么?首要任务是:1、GUI (Graphic UserInterface 图形用户界面)在Windows 之前,我们用的是DOS操作系统。在学习《白话C++》时,其实我一直在和那个黑乎乎的窗口打交道。看一眼现在WindowsXP华丽的外表,再看一眼那个DOS窗口吧。Windows之所以成为Windows,最根基的一点就是它为用户提供了一个图形的世界。你现在所看到的一切,开始按钮,桌面上那个“我的电脑”的图标,还有一切文字,图形,它们都是画出来的。Windows一代比一代画得好,不过是为了让我看得舒服点。Windows是一个GUI操作系统。2、API (Application ProgrammingInterface 应用编程接口)GUI是给普通电脑用户看的。而这个API则仅仅给Windows的程序员准备。通过图形界面,最终用户可以通过手抓着鼠标或按键盘,来操作,控制Windows,比如点“开始”按钮,然后选择关机菜单项来关闭电脑。但如果要通过程序来实现关机该怎么办?那就需要API了。API就是程序才能“看”到“界面(Interface)”。另外的如用户可以拖一个文件到“回收站”,程序也可以“拖动”一个文件到“回收站”。只是前者通过GUI,而后者通过API。再有,你有没遇到过IE的默认主页及窗口标题被人家改了的臭事?那些经验文章教我们如何通过注册表编辑器恢复,并再三强调说:要小心啊,千万不要误操作……,对一个程序员来说,手动改注册表的讨厌之处不再于它的危险,而在于它的麻烦,累眼睛啊!好吧,程序来做,程序如何做?用API。程序员通过API来控制Windows。3、 Messages Loop(消息循环)GUI 和 API 看起来是如此的不同。似乎一个是走阳光道一个走独木桥。虽然不是全部,但在很多地方,二者其实走得很近。在Windows深处的一条繁忙的路上,来自GUI的“人物”和来自“API”的人物可能就一前一后地走着!那就这里所说“消息循环”。这里的“循环”并不是说某个消息一定就要“绕一个圈”;而是说千千万万个消息不停息地走在Windows建立的“消息通道”上,来自不同的地方,最终又走向各自的归宿……如果按“来自”哪里分,最粗的分法就是:消息可以来自程序,也可以来自用户。你理解“消息”吗?《白话C++》的第三章曾经讲到,鼠标处理的是人手移动的信息。这些信息最后打包成一个“消息”,发送Windows,告诉Windows:“那个用户双击了'我的电脑’”。鼠标和键盘之类的外设所产生的消息不过是Windows消息中很小的一部分。更多的消息是留给程序员用的,留给程序用的。想不想做这么一个调皮的程序:想像你的同事上班时双击桌面的“我的电脑”,“我的电脑”窗口刚一闪就自行关闭!他双击!双击!!再双击!!!就是打不开“他的电脑”。最后一定会向你要杀毒盘的。赶在愚人节之前“看透消息”吧。在开始本部课程之前,我假设你已经学好C++。如果没有,你应该先学习《白话C++》。另外,你应该一点点的英语基础,这有助于从程序代码中诸名变量的名字这一最基本的水平上理解代码的功能。第一章 GUI的世界(一)一切都是画出来的1.1 “一切都是画出来的”"Windows",中文翻译为“窗口”。为什么叫“窗口”?可能你知道,像下面的界面,微软公司称它为一个“窗口”: (图2:一个"Windows")上面是我双击“我的电脑”,出现的的窗口。不仅仅是Windows自带的,诸如“资源管理器”,或“IE”等应用程序的带有“窗口”,所有在Windows上跑的程序,除非它确实不需要任何可操作的界面,否则,它就至少必须一个窗口(称为主窗口)。比如我们常见的QQ: (图3: 您的QQ 可能很漂亮,但也是窗口)不仅仅这种可变大小的窗口称为窗口,一些对话框大小固定,但在程序上也叫窗口。不仅仅大大的,有内容的窗口是窗口,一个小小的按钮,在程序上也称为“窗口”。这些窗口哪来的?这些窗口是由Windows画出来的。我们举一个小小按钮,来看看。为了看得更清楚,我把按钮所在的网页位置背景设置为红色。 (图4:一个开始按钮也是一个窗口)按钮具有3维效果。其实就是画出来的。一个长方形,左、上边画各画一条白边线;而右下则最外层是一条黑色线,内层则是灰色线。而按钮的表面则是银色。这样就构成一种立体效果(如果你不信,可以在画图程序里试试)。窗口是画出来的。单就为了这么一个小小按钮的三维效果,就得至少画上6条线。一个很复杂的窗口呢?更可怕的是,窗口里的所有画像,文字,线条,就算是填充成一片空白,统统是画画的效果。有些人可能要放弃了:学习编程,不仅要学逻辑,要英文,没想到还要会画画?事情当然没有这么糟!事实让,这一切,Windows操作系统都会帮我们来画(但在DOS年代,就得由程序员自己画了)。不过,如果统统交给操作系统,那么写程序似乎也就没有什么意思了?所以操作系统在不顾劳累地画啊画的同时,仍然提供了接口来让程序员在认为合适的时候,合适的地方,画上合适的东东。1.2 第一个程序:把Windows打个大红叉!当然“合适不合适”,Windows并不太管。也管不了。所以,我们的第一个程序就是:在Windows桌面上画一个大叉。如果让比尔盖咨看到了,他可能会认为我们做的真的有那么一点点不合适。怎么在屏幕上出现那个大红叉呢?我刚问到这里,一个女学员说:“其实也不是太难”。她说对了!这就是C++Builder的功劳。本来,一切都要通过前面所说的API来打交道。但CB对相关的这些API做了很好的封装,使得我们能以极其自然(面向对象)的,简短的程序代码来实现。(我说完这些,把赞许的目光投向那位女学员,却发现她已经拿出口红,在屏幕上迅速地打了一个叉)。运行 C++ Builder 6,菜单:File | New |Appication。 CB6为我们新建一个空白的Windows程序工程(Project)。并且自动生成了一个表单(Form)。我们知道,当程序运行之后,这个表单就被称为窗口(Window)。(如果你对这些及后面一些CB相关操作不熟,请复习《白话C++》的第二章)。把这个表单拉小点,然后,从控件栏的“Standard”页中选 Button (控钮) ,放到表单上。现在效果应如下:
双击放在表单上的按钮。出现代码编辑窗口。现在的代码中应有这些内容:void __fastcallTForm1::Button1Click(TObject *Sender){}在上面的一对 { }之间填写以下内容:(行号不要键入!)//先得到屏幕的长宽:1) int ScreenWidth =Screen->Width;2) int ScreenHeight =Screen->Height;3) HDC hDC = ::GetDC(0);//得到“画布(Canvas)”4) TCanvas* ScreenCanvas = newTCanvas;5) ScreenCanvas->Handle= hDC;//设置画布的画笔(Pen)6) ScreenCanvas->Pen->Color= clRed; //画笔的颜色为红色(Red)7) ScreenCanvas->Pen->Width= 10; //画笔的粗细//开始画叉啦,需要画两笔,这是第一笔:左上到右下8) ScreenCanvas->MoveTo(0,0); //把画笔移到(Move to)坐标0,0处,即屏幕最左上角9) ScreenCanvas->LineTo(ScreenWidth,ScreenHeight); //从当前位置画一条线(Line to)到屏幕的右下角。//第二笔:右上到左下:10) ScreenCanvas->MoveTo(ScreenWidth,0);11) ScreenCanvas->LineTo(0,ScreenHeight);//释放“画布”:12) ScreenCanvas->Handle= 0;13) delete ScreenCanvas;14) ::ReleaseDC(0,hDC);保存工程。编译: Ctrl + F9。如果报错,说明你代码有误。运行:F9。窗口出现了。上面有一个按钮。如果你的CB6正在显示,把它最小化以露出Windows的桌面。然后,点按钮。本章最前头的图片效果就应该出来了。不知道你在想什么?但我想通过这个程序来告诉你:你每天都在面对的Windows,都在使用的Windows,它的一切都是画出来的。普通用户没有“权力”去参与这件工作。但程序员有权参与——仅管我们看上去是在搞破坏。代码解释:我们并不想在"看透"的章节里,就讲太多的技术细节与实现原理。但幸好上面的代码都很自然。如果你不懂什么叫“类(class)”,那么需要继续学习《白话 C++》。上面用到了两个类:TScreen 和 TCanvas。Screen 变量CB 的程序会在程序运行时,就自动产生一个 Screen 变量,它的数据类型为 TScreen 。如其名,它是封装了“屏幕”的一些信息和功能。我们这里仅仅想得知屏幕的分辨率是多少?我的电脑是(1024* 768),别的电脑可能也是,或者是800 *600,最惨就是640 * 480了。(注1) 得到屏幕长宽后(第1、2行),我们也就没有在使用Screen了。Screen由CB自动生成,是一个全局变量,它的释放工作不归我们管。ScreenCanvas 变量TCanvas 类,它封装有关Windows绘画操作的绝大部分数据和功能。所以虽然它的名字为“画布”,但其实包含了“画笔(Pen)、画刷(Brush)”等等。在CB中,窗口会自带一个Canvas变量,但可惜我们现在是要在屏幕上的任何地方画。(第一道线就从从屏幕的左上角一直穿过整个桌面,直到落上右下角的时间区)。这是一种很霸道的行为。CB当然不会为我们这种行为事先准备一个这样的“画布”。所以我们需要生成一个Canvas变量,我命名它为:ScreenCanvas(屏幕画布)。这是第4行做的事:生成一个画布变量。虽然取名为“屏幕画布(ScreenCanvas)”。但事实上新生成的画布都是“空画布”。因为程序这里还不知道你到底想画到哪里。我们想画到整个屏幕上(事实上,如果你有其它程序正在屏幕上显示着,则会画到那个程序的界面)。这就需要向Windows提出申请,请它把整个电脑屏幕的“画布”都交给我们。Windows 是用C和汇编写的,二者都不是面向对象的语言。Windwos大多数数据和功能本身也没做面向对象的封装(后来的COM对象开始有了面向对象的特征,这是后话)。在Windows里,类型画布的东西,称为DC。第三行:HDC hDC =::GetDC(0); 得到了屏幕DC。 参数 0 正是指明要的是整个屏幕的画布。第五行:ScreenCanvas->Handle = hDC; 通过ScreenCanvas 的 Handle属性,得到了整个屏幕的画布。(这些解释,你可能觉得还有不少地方没讲到,但没关系。)其它的,就是画线的过程。代码中已有详细的注释。最后12,13,14三行,用于返还、释放屏幕画布。这就是我们的第一个例子。在屏幕上打一个叉。下一章我们仍然是要画画。不过我们将会规矩一点,只在合适地方,在合适的时间,画上合适的内容。猜一猜,我们会画什么内容?是一个在编程界流传很广,也很古董的内容。当初学习《白话C++》时,我们就做过了!——此言一出,台下一片失望之色。注一:可以这样查看你的电脑屏幕分辨率:在Windows桌面点右键,出现的菜单中选“属性”,然后选择到“设置”页。应该就能找到分辨率,如果没有,可以找一个“高级”按钮,点击后在出现的各页里查找。