第14章 Windows API 的跨平台开发

14.1 Windows API 跨平台开发概述
  Windows API是Miscrosoft公司在推出其Windows系列操作系统时同时提供给广大用户的一个应用程序接口,在这套应用程序接口中定义了数以千计的可用于面向Windows程序设计的函数。用户使用这些函数可以轻易地完成诸如窗口生成并显示,消息循环和处理等各种工作。由于其良好的可重用性、Windows操作系统的紧密集成以及强大的功能,因此 Windows API构成了面向 Windows应用开发的一个基础。
  虽然Windows API是用C语言开发的,它却能同时用于很多的开发平台。但是由于不同开发平台在实现方式上的差异,使得 Windows API在其他开发工具中的应用和在VisualC++中的应用既有相似之处,又有不同之处。
  现在的主流开发工具除了Microsoft的Visual Studio之外,还有Inprise公司开发的Delphi和C++Builder,这两款开发工具的最大特色就是引入了强大的可视化组件库,这些组件库在很大程度上缓解了程序员在界面设计上的负担,因此自从它们推出之后,得到了广大程序员的热烈欢迎,并迅速流行开来。现在,这两款开发工具的最新版本分别为 Delphi 5和 C++Builder 4。
  本章介绍Windows API在Delphi和C++Builder中的应用,主要是希望通过这部分内容使读者对 Windows API的认识能得到进一步的提高,同时也希望通过它能使读者对Inprise公司的 Delphi和 C++Builder有一个新的认识。
14.2 Windows API 与 Delphi
  Delphi是一个用以开发Windows应用软件和数据库类软件的一个开发工具。Windows API 作为Windows操作系统不可分割的一部分,在Delphi中得到了全面的支持。
14.2.1 Delphi概述
  Delphi融合了当今世界先进的软件技术,采用可视化组件库,支持多种数据库,如Orcale、SQL Server等。作为数据库的前台开发工具,它提供了友好的开发界面,使用户能用较少的工作量就能开发出美观实用的 Windows应用程序。 Delphi的前几个版本均由Inprise公司开发成功,并获得了广大用户的信赖和好评。
  目前由Inprise公司推出的Delphi 5在原有版本的基础上,增加了动态数组、方法重载、默认参数等等。项目管理器允许用户将协同工作的项目放到同一个项目组中,同时允许用户定制自己的桌面。在数据库引擎方面,不仅强化了BDE的性能,而且引进了Microsoft的ADO。在网络应用方面提供了对XML的支持。Delphi支持可扩展的数据库技术,执行最优化的源代码。用户开发的 Delphi应用程序可直接运行在 Windows 95和Windows NT4.0上,并与 C++的 DLL和 VBX兼容。
  尽管Delphi具有上述所介绍的强大功能,但是它也离不开Windows API这个基础。在Delphi中使用Windows API进行程序设计,与Visual C+十的不同之处仅在于C+十与Object Pascal这二者之间的语法差别而已。
14.2.2 Delphi开发 Windows API应用程序的步骤
  下面以 Delphi 5为例,介绍在Delphi中如何实现基于 Window API的程序设计。
  首先,单击 File菜单下的 New命令,这时将弹出如图所示的对话框。

第14章 Windows API 的跨平台开发_第1张图片

在图中,选择 Console Wizard图标,然后单击OK按钮,这时,Delphi的主界面如图所示。

第14章 Windows API 的跨平台开发_第2张图片

  从图 14-2可以看出, Delphi的对象监视器为空,也就是说,当前应用程序没有使用到表单和其他的可视化组件。事实上,这时Delphi为用户只生成了一个项目文件,而没有生成相应的单元文件,这和基于Delphi可视组件库的应用程序是有一定差别的。
  在图中的源文件编辑器中编辑自己的基于Windows API的程序源码,完成后,编译运行即可,这一步和Delphi中基于可视组件库的应用程序是一致的。
14.2.3 Windows AP在 Delphi中的应用实例
下面是一个在 Delphi中采用 Windows API进行程序设计的一个实例。
例 基于 Windows API的 Delphi应用程序该程序是一个用 Windows API编写的简单的应用程序,主要用它来说明如何在 Delphi 中调用 Windows API函数,如何设计基于 Windows API的应用程序。
  由于使用的是Windows API和消息循环来构造应用程序,因此在程序中的uses部分包含了Windows 和Messages两个单元。uses子句和Visual C++中的include很类似,其功能也是为了应用程序能调用在某个文件中定义的函数和常量。在 Delphi中,所有的Windows API函数的原型和数据结构及常量的定义均保存在Windows和Messages这两类文件中。在 Delphi的 Lib目录下可以打开这两个文件,其文件名分别为 Windows.dcu和Messages.dcu,用户可以从这两个文件中获得准确的函数原型、数据结构和常量的定义。
  程序中定义了一个全局变量wClass来代表最终的窗口。这个变量是TWndClass类型的,TWndClass实际上是一个结构体,它是 Visual C++中的 WndClass在 Delphi中的实现。通过对这个结构体变量的各个域进行不同的设置,可以获得不同的窗口风格和界面特征。在程序中可通过如下语句实现对wClass变量的初始化:
with wClass do //初始化窗口类
begin
  Style:=CS_PARENTDC;
  hlcon:=LoadIcon(hInst,'MAINICON');
  lpfnWndProc:=@WindowProc;
  hlnstance:=hlnst;
  hbrBackground:=COLOR_BTNFACE+1;
  lpszClassName:='Sample Class';
  hCursor:=LoadCursor(O,IDC_ARROW);
end;
  上述程序不论是在功能L还是在实现方式上和 Visual C+十中对 wClass进行的初始化都是类似的。例如,指定hCursor域的值时,在上述程序中是通过调用API函数LoadCursor来实现的,其中指定的光标标识符IDC ARROW是在Windows系统中预定义的箭头光标。不过,由于 Delphi用的编程语言是 Object Pascal,因此在指定 IpfnWndProc时,不仅仅需要指定窗口函数的名称,而且必须在其前面添加一个“@”号,这个号表示传递给lpfnWndProc是窗口函数WindowProc的人口地址。
  在完成对变量wClass的初始化之后,接下来应该对窗口类进行注册,这个功能是通过调用函数RegisterClass来实现的。
  在完成对窗口类的注册后,就可以调用函数CreateWindow来创建窗口,程序中对CreateWindow函数的调用如下:
Handle:=CreateWindow(
  'SampleClass', //窗口类名
  '加密器一Windows API在Delphi中的应用', //窗口标题
  WS_OVERLAPPEDWINDOW or WS_VISIBLE, //窗口风格
  10, //左边界坐标
  10, //上边界坐标
  400, //宽度
  300, //高度
  0, //父窗口句柄
  0, //菜单句柄
  hlnst, //应用程序实例
  nil //创建窗口的附加参数
);
  其中,参数 Sample Class用于指定生成窗口的窗口类的名称,也就是通过 RegisterClass函数注册的用户自定义的窗口类。如果CreateWindow函数执行成功,则返回新建的窗口句柄并将这个句柄赋值给全局变量Handle。这里的窗口句桶和Visual C++中的窗口句柄的含义是完全一致的。
  程序中除了创建主窗口外,还通过调用CreateWindow函数创建了两个按钮、一个编辑框和一个静态文本框。窗口类型都是由Windows系统预定义的,所以不用再注册就可以直接使用。这些窗口又被称为子窗口,因为它们是隶属于一个父窗口的,在这里就是隶属于由句柄Handle标识的窗口。正是这些子窗口构成了和用户进行交互的界面。
  在本程序中,还通过调用API函数CreateFont创建了一个字体,程序中对CreateFont函数的调用如下:
hFont:=CreateFont(
  12, //高度
  0, //宽度
  0, //旋转角度
  0, //方向
  0, //线条宽度
  0, //斜体
  0, //下划线
  0, //加粗
  DEFAULT_CHARSET, //字符集
  OUT_DEFAULT_PRECIS, //精度
  CLIP_DEFAULT_PRECIS, //剪裁方式
  DEFAULT_QUALITY, //渲染质量
  DEFAULT_PITCH or FF_DONTCARE, //字体族
  'MS Sans Serif' //字体名
);
  CreateFont函数的参数比较复杂,每个参数描述字体的某一个属性,CreateFont的功能就是用指定的字体属性创建一种字体。创建的字体由CreateFont返回的字体句柄来标识,例如,在向按钮 Encrypt发送 WM-SETFONT消息的同时,把 hFont作为 SendMessnge的wParam参数,实现将按钮Encrypt中的标题字体设置为上述程序中新建的字体。
  SendMessnge用于向各个窗口发送消息,例如,上面的设置字体的消息就是通过如下语句发送的:
SendMessage(hEncrypt,WM_SETFONT,hFont,0); //发送消息
SendMessage(hDecrypt,WM_SETFONT,hFont,O);
SendMessage(hEdit,WM_SETFONT,hFont,O);
SendMessage(hPW,WM_SETFONT,hFont,0);
SendMessage(hLabel,WM_SETFONT,hFont,O);
  上述语句将所有子窗口标题的字体都设置为由CreateFont新建的字体。
  为了实现消息处理,程序中首先调用函数GetWindowLong,取得了Encrypt按钮的默认消息处理函数的入口地址,然后把这个地址转换成指针类型,并把最终的值传递给dEncrypt变量。再调用函数 SCtwilldOWLOflg给 EllCrypt按钮指定由用户定制的消息处理函数的人口地址。
  在调用函数SetFocus把默认的焦点置于Encrypt按钮之上后,主程序就进入了消息循环。在Delphi中的消息循环如下:
while(GetMessage(Msg,Handle,0,0))do //消息循环
begin
  TranslateMessage(Msg); //翻译消息
  DispatchMessage(Msg); //发送消息
end;
  其中,函数GetMessage用于从操作系统的消息队列中获得消息,然后通过函数TranslateMessage对消息进行翻译,最后通过函数DispatchMessage把所有的消息分发出去。只有当消息循环接收到一条 WM-NESTROY消息时才会结束。
  主窗口的消息处理函数如:
function WindowProc(hWnd,Msg,wParam,lParam:Longint);Longint;stdcali;
begin
  Result:=DefWindowProe(hWnd,Msg,wParam,lParam); //消息默认处理
  case Msg of
  WM_SIZE: //改变窗口大小
    Resize;
  WM_COMMAND: //处理命令
    if lParam=hEncrypt then
      Encrypt //加密
    else
      if IParam=hDecrypt then
        Decrypt; //解密
  WM_DESTROY: //结束应用程序
    ShutDown;
  end;
end;
  其中,消息处理函数的参数列表和Visual C+十是一致的,但是在参数的数据类型上有一些细微的差别,在 Delphi中四个参数的类型都是 Longint。
  和Visual C+十中的消息处理类似,在Delphi中也是通过一个大的分支结构来实现对不同的消息进行不同的处理。例如,当窗口接收到 WMSIZE消息时,就调用 Resize函数对窗口进行重画。Resize是自定义的一个函数,其完整的定义如下:
procedure Resize;
var
  RCT:TRect;
begin
  GetWindowRect(Handle,RCT); //获得窗口矩形区域
  MoveWindow(hPW,230,5,RCT.Right-RCT.Left-245,24,True);//移动窗口
  MoveWindow(hEdit,5,34,RCT.Right-RCT.Left-20,RCT.Bottom-RCT.Top-66,True);
end;
  其中,定义了一个用于描述当前窗口所占据的区域的TRect变量RCT,函数GetWindowRect可以获得窗日当前所在的矩形区域,MoveWindow可以使窗口移动,事实上,调用MoveWindow函数的目的是为了当窗日大小发生改变时还能保持窗口中按钮、编辑框等界面元素的相对位置。
  用户单击窗口中的某一个接或将触发一条WM-COMMAND消息,同时在IParam参数中将记录下触发消息的组件句柄。例如,当用户单击Encrypt按钮时,消息处理函数就通过对IParam参数的判断,发现消息来自于Encrypt按钮,于是就调用函数Encrypt加以处理。Encrypt函数是自定义的一个函数,其完整定义如下:
procedure Encrypt;
var
  x,i,
  sText,sPW:Integer;
  Text,PW:PChar;
begin
  sText:=GetWindowTextLength(hEdit+1; //Edit编辑框中的文本长度加1
  sPW:=GetWindowTextLength(hPW)+1; //密码框中的文本长度加1
  GetMem(Text,sText; //分配内存
  GetMem(PW,sPW); //释放内存
  GetWindowText(hEdit,Text,sText); //获取Edit编辑框中的文本
  GetWindowText(hPW,PW,sPW); //获取密码框中的文本
  x:=0;
  for i:=0 to sText-2 do
  begin
    Text[i]:=Chr(Ord(Text[i])+Ord(Pw[x])); //加密
    Inc(x);
    if x=(sPW-1)then x:=0;
  end;
  SetWindowText(hEdit,Text); //重新设置Edit编辑框中的文本
  FreeMem(Text);
  FreeMem(PW);
end;
在Encrypt中,实现对文本框中输入的文本进行加密。其核心语句是:
Text[i]:=Chr(Ord(Text[i])+Ord(PW[x])); //加密
  其中,函数Ord用于取得某一个字符的ASCII码,然后再加上来自密码框中某一个字符的ASCII码,将相加的和赋给原来的字符单元,这样显示出来的文本和最初的文本就不一样了。函数Ord的原型定义如下所示:
function Ord(X):Longint;
关于Ord函数的使用还可以参考下面的例子:
USCS Dialogs;
type
  Colors=(RED,BLUE,GREEN);
Var
  S:string;
begin
  S:='BLUE has all ordinal value of'+IntToStr(Ord(BLUE))+#13#10;
  S:=S+'The ASCII code for "c" is'+IntToStr(Ord('c'))+'decimal';
  MessageDlg(S,mtInformation,[mbOk],0);
end;
  函数GetMem和FreeMem分别用于为字符串分配内存和释放内存;函数GetWindow Text和setWindowText分别用于获取和设置编辑框中的文本。
  与加密类似,如果用户单击了Decrypt按钮,则调用函数Decrypt来执行相应的解密操作。通过减去密码框中相应字符的ASCII码就可以恢复文本框中的文本。实现解密功能的核心语句如下:
Text[i]:=Chr(Ord(Text[i])-ord(PW[x])); //解密
在关闭应用程序时,程序中调用shutDown来完成所有的善后事宜。函数shutDown的完整定义如下:
procedure ShutDown;
begin
  DeleteObject(hFont); //删除字体句柄
  UnRegisterClass('Sample Class',hlnst); //注销窗口类
  ExitProcess(hInst); //结束应用程序
end;
  由于句柄是一种系统资源,因此在关闭应用程序时,应该删除hFont句柄并注销对Sample Class的注册。   ExitProcess是一个 API函数,这个函数用于结束当前进程。
  编译并运行上例,用户将得到如图所示的应用程序主窗口。

第14章 Windows API 的跨平台开发_第3张图片

  单击 Encrypt按钮,则编辑框中的文本将被加密,如图所示。
  当然,用户单击按钮 Decrypt,相应的文本将会被解密,重新得到如图习中所示的文本。

第14章 Windows API 的跨平台开发_第4张图片

14.3 Windows API与C++ Builder
  可以说,基于图形界面的Windows系列操作系统的推出是计算机真正进入各个应用领域的一个具有里程碑意义的事件。
  在Windows取得了巨大的成功的同时,广大程序员也必须面对一个问题,那就是开发基于Windows的应用程序。Windows操作系统的一个核心思想就是图形界面的思想,通过为用户提供一个基于图形的,而不是基于字符串命令的用户界面,同时为了减轻用户在操作上的困难,提供的图形界面中也尽可能地采用一致的图形界面元素,例如对话框、按钮、菜单等均采用统一的风格,提供一致的外观。
  深入了解Windows API的好处在于可以有助于理解C++Builder和操作系统是如何工作的。如果理解了创建Windows程序的核心技术,那么就可以从这些信息中获得C++Builder的工作原理,以及它能做什么,它不能做什么。
  许多人使用C++Builder,是因为C++Builder可以在相对轻松的情况下较快捷地建立复杂的应用程序。这是C++Builder的一大优点。但是如果要真正创建一些强有力的程序或者组件,那么仅仅了解高级工具显然是不够的,还应该了解C++Builder是如何实现和Windows进行交互的,在Windows底层是如何实现的?这里介绍的关于C++Builder中如何进行 Windows API程序设计的内容希望能为读者朋友理解上述问题提供一点帮助。
  但必须指出的是, C++Builder能通过 Windows API进行程序设计,但是这并不是C++Builder的强处。基于VCL并结合Windows API等多种手段才能充分发挥C++Builder的编程能力,并编写出高质量的应用程序来。
12.3.1 C++Builder开发 Windows API应用程序的步骤
  C++Builder是Inprise开发的Delphi的姊妹产品,因此C++Builder无论是在界面特征还是在功能实现上都和 Delphi非常类似。在开发基于 Windows API的应用程序的步骤上,两者也是非常相似的。
  当启动C++Builder后,单击File菜单下的New菜单项,这时将弹出如图的对话框。

第14章 Windows API 的跨平台开发_第5张图片

在这个对话框中,选择 Console Wizard,然后单击 OK按钮,这时将得到如图所示的C++Builder运行界面。

第14章 Windows API 的跨平台开发_第6张图片

  在图中, C++Builder自动为用户生成了一个Windows API应用程序的框架,如自动将头文件windows.h包含进项目中,自动生成的WinMain函数等。用户可以直接在此基础上开发自己的 Windows API应用程序。
12.3.2 Windows API在 C++Builder中的应用实例
  由于 C++Builder的编程语言是 C++,因此,通过C++Builder开发Windows API应用程序和通过Visual C+十开发Windows API应用程序在语法上、结构上都是非常类似的。事实上,在C++Builder中开发的基于Windows API应用程序在Visual C+十中也能编译运行,在 Visual C+十中开发的 Windows API应用程序在 C++Builder中也能编译运行。
  下面是使用C++Builder开发的一个简单的Windows API应用程序,该程序将显示一个窗口,并在该窗口的客户区中用蓝色、绿色和红色三种画笔画线。
例 Windows API在 C++Builder中的应用
  程序中首先对窗口消息处理函数WndProc、窗口初始化函数InitWindows的原型进行声明。然后定义一个全局变量hWndMain记录主窗口的句柄。
例中的WinMain函数、InitWindows函数和前面介绍过的几乎相同,这里就不赘述了。
  下面重点分析一下程序是如何实现绘制直线功能的。
  在消息处理函数WndProc中,首先定义了红、绿、蓝三支逻辑画笔,然后在对窗口进行重给时,通过 CreatePenIndirect函数创建逻辑画笔实例,通过 SelectObject函数选择不同的画笔到设备描述表中就可以实现绘制不同颜色的直线。
  绘制直线之前,程序先调用函数MoveToEx将画笔移动到直线的起点,然后通过调用函数LineTo绘制一条直线。
  因为画笔句柄耗费系统资源,所以在结束绘画后,必须调用 DeleteObject函数销毁画笔实例。

你可能感兴趣的:(第14章 Windows API 的跨平台开发)