精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>编程开发>>● 编程世界>>其他>>《编程技术》第15期 (欢迎订阅)

主题:《编程技术》第15期 (欢迎订阅)
发信人: yaoz()
整理人: jinhu(1999-07-13 10:43:48), 站内信件
===========================================================

                         《编 程 技 术》

               一份属于广大编程爱好者的电子杂志

             这是一份完全关于探讨编程的免费电子杂志
             您可以任意传播本刊,但必须保持它的完整
            本刊承诺绝不向订户发送任何形式的垃圾邮件!
===========================================================     
           第15期 99/07/04          本期发送数: 2067 
===========================================================

<本期栏目>
 *刊首语*
 *雕虫小技*
    C++Builder 3技巧 
 *技术文档*
    用DELPHI实现状态栏上的工具条
    如何在Delphi4 BDE中配置Access数据库
    用DirectDraw编写动画程序
    如何在C++Builder中检测硬件 
 *疑难杂症*

***********************************************************

<刊首语>
    
    本人从今天开始正式进入暑假了,呵呵,爽! 今后我将有更多时
间为大家搜索一些实用文章了。也有更多的时间和大家交流了。
    最近不少朋友都建议我多提供一些C++Builder的文章,其实我现
在也在学这个东东,我的感觉就是确实简单易用,很容易上手。但因
为没有VB、VC等那么“历史悠久”,所以相关的文章资料确实非常少
我也是心有余而力不足。当然我会竭尽所能,不惜在电话账单上出现
天文数字的代价,为C++Builder的用户(其实也为自己)多搜寻些相
关文章。本期就有我吐血找到的两篇,希望您能可怜哦是喜欢。

***********************************************************
***********************************************************

<雕虫小技>

                         C++Builder 3技巧 

                         (成都 陈寒秋)


  Insprise公司(原Borland公司)推出的C++ Builder 3.0是新一代的
基于C语言的可视化开发工具。但由于它推出的时间不长,所以有关它的资料
不是很多。在这里,我向大家介绍一些使用C++ Builder 3.0编程的技巧。
  (一):读写注册表。
  现在的Windows程序大都使用了注册表来存储软件的配置信息。C++ 
Builder 3.0中给我们提供了一个TRegistry类帮助我们管理注册表,它有以
下几个常用的方法:
  OpenKey   打开一个键值
  ReadBool 从注册表中读取布尔值
  ReadInteger 从注册表中读取整型值
  ReadString  从注册表中读取字符串值
  WriteBool  向注册表中写入布尔值
  WriteInteger 向注册表中写入整型值
  WriteString  向注册表中写入字符串值
  代码演示:
  TRegistry &regkey=*new TRegistry();
  regkey.OpenKey(“software\myapp”,true);
  regkey.WriteInteger(“Max”,100);
  int x=regkey.ReadInteger(“Max”);
  delete &regkey;
  注意:使用TRegistry类时应包含头文件。
  (二):模仿超级链接。
  用过Winamp的朋友知道Winamp的界面中有一些模仿的超级链接,可以把你
直接带到它的相关网站中去。下面这个例子就实现了这种功能,当鼠标点击其
中的仿超级链接时,你就会直接到达《电脑商情报》的网址:
  新建一个应用,在窗体Form1上添加一个名为Label1的TLabel组件,然后
在代码编辑器中添加以下代码:
  void __fastcall TForm1::FormCreate(TObject *Sender)
  {
   Label1→Cursor=crHandPoint;
   Label1→Font→Color=clBlue;
   Label1→Font→Style=Label1→Font→Style< Label1→Caption=“http:/
/www.cbinews.com”;
  }
  
  void __fastcall TForm1::Label1Click(TObject *Sender)
  {
   ShellExecute(Handle,NULL,Label1→Caption.c_str(),NULL,NULL,SW_SHO
WNORMAL);
  }
  
  void __fastcall TForm1::Label1MouseMove(TObject *Sender, TShiftSt
ate Shift,
   int X, int Y)
  {
   Label1→Font→Color=clRed;
  }
  
  void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftStat
e Shift,
   int X, int Y)
  {
   Label1→Font→Color=clBlue;
  }
  注意:本示例应该包含头文件。
  (三)取得Memo的行和列
  新建一个应用,在窗体Form1上添加两个TLabel组件名为Label1,Label2;

添加两个TButton组件名为Button1,Button2;添加一个TMemo组件名为Memo1。

然后在代码编辑器中添加以下代码。
  void __fastcall TForm1::Button1Click(TObject *Sender)
  {
  Label1→Caption=SendMessage(Memo1→Handle,EM_LINEFROMCHAR,-1,0)+
1;
  }
  
  void __fastcall TForm1::Button2Click(TObject *Sender)
  {
  Label2→Caption=Memo1→SelStart-SendMessage(Memo1→Handle,EM_LINE
INDEX,-1,0)+1;
  }
  这种方法同样适用于RichEdit。
  
***********************************************************
***********************************************************

<技术文档>
             
                    用DELPHI实现状态栏上的工具条 

                           (成都 邓波)

  Inprise(即以前的Borland)公司的新一代编程语言Delphi是一种非常优
秀的编程工具,它丝毫不逊于Microsoft公司的Visual BASIC和Visual C++,

尤其是它在数据库管理方面的特长深受广大程序员的喜爱。
  当今,软件开发商们已经开发出了多种类型的关系数据库系统,如Borland

公司自身设计的dBASE、paradox、Interbase,Microsoft公司的Foxpro、Access

SQL Server以及其它像Sybase、Oracle、DB2等成熟的产品。面对如此众多的选

择,Delphi通过其自带的数据库引擎BDE对它们以及任何ODBC数据源提供了很好

的支持。只需很少的设置步骤,开发者不用修改任何数据库的结构就能直接引
用数据库对象。综合起来说,BDE有以下几点优势:
  1. 使用BDE使开发客户机/服务器的数据库应用更加方便,数据库应用程序

不仅可访问本地数据库,也可访问远程的大型数据库。
  2. 内建的标准数据库paradox使数据库格式更加紧凑,使用更高效。
  3. 通过别名(Aliase)的设置,允许数据库直接、灵活地访问数据源,多

个数据库应用可访问同一个数据库文件。
  4. BDE能够共享不同数据库之间的数据,支持它们之间的相互转化,例如
Access到paradox的数据复制;甚至建立Interbase和Oracle表之间的一对多关系

  5. 通过对查询语言SQL的支持,使用户定义和访问数据库的能力更加强大。

  笔者注意到许多新接触Delphi的人对怎样设置BDE还不甚了解,因此,在此

为大家具体说明BDE在数据库程序中的配置,以Delphi4中引用MSACCESS数据库为

例。
  启动BDE Administrator需选择Windows 98的
“开始\程序\Borland Delphi 4\BDE Administrator”来启动,显示出BDE管理者

窗口。窗体左边有两页,分别是数据库别名页(Databases)和配置页Configura
tion)。
选择配置页,单击Configuration左边的加号展开两层目录树,可以看见安装在缺

省路径下的所有种类的本地(Native)数据库驱动程序。选择相应的数据库种类

就会在右边列出它的配置情况。用同样的方法也可以看到ODBC的配置情况。从中

可以看到MSACCESS有VERSION、TYPE、DLL32、DATABASE NAME、USER NAME、
OPEN MODE、LANGDRIVER等几个主要的属性,其中用黑体表示不可更改,下面是

它们的一些设置方法:
  1. VERSION属性
  Access驱动程序的内部版本号,由系统自动生成,不可更改。
  2. TYPE属性
  当前驱动程序可以连接的服务器种类。它通常有两个值可选:SERVER(SQL服

务器和FILE(标准文件服务器),由系统自动生成,不可更改。
  3. DLL32属性
  驱动程序的32位动态链接库名。缺省的IDDA3532.DLL适用于Access 97,
IDDAO32.DLL适用于Access 95和Jet Engine 3.0。
  4. DRIVER FLAGS属性
  驱动程序内部产品标识。
  5. TRACE MODE属性
  通过数位值设置需纪录的日志量。
  6. DATABASE NAME属性
  Access数据库文件的访问路径。
  7. USER NAM属性
  访问数据库的缺省用户名,通常不予设置。
  8. OPEN MODE属性
  确定数据库打开时的状态,READ/WRITE或READ ONLY。这将影响到数据访问的

权限。
  9. LANGDRIVER属性
  语言驱动程序定义了数据库使用的语言种类,用于选择与数据库纪录文本一

的语言种类。
  10.SYSTEM DATABASE属性
  指定打开系统安全数据库的路径,当驱动程序的上载或下载时这个改变将产

生影响。现在我们为Delphi应用程序配置一个新的Access数据库ALesson。选择

BDE管理器的Object下拉菜单中的New,在弹出的窗口中选择MSACCESS。之后在
Databases页面内会增加新的数据库项目,为它输入新的别名ALesson,在以后
的数据库程序中将直接引用其别名。选择ALesson,在右边的Definition页面中

点击DATABASE NAME项输入ALesson.mdb的路径,其它项目则置为默认值。在左
边ALesson别名上点击鼠标右键,选择弹出菜单中的应用(Apply),存储所有
的设置。这样,ALesson库就与BDE连结起了。
  在程序中只需在Table组件的DatabaseName属性中选择ALesson就行了。除
此之外,Access还可以通过ODBC的数据源来设置,也可达到同样的目的。

***********************************************************

                     如何在Delphi4 BDE中配置Access数据库 

                              (福建 许晓明)

  在使用Netscape的navigator时不知你有没有注意到:在它的状态栏有一组按
钮,
这使我们能很方便地打开另一个窗体,更有效地利用时间(要知道网上冲浪时间就

金钱啊!)。
  接下来我要讲的是如何在我们的程序中加入此功能,我们可以用DELPHI很轻

地实现,在DELPHI中我们可以指定某个Twincontrol类(如Ttoolbar)的父级为另一

Twincontrol(这里是Tstatusbar),然后通过动态创建Ttoolbar就可以在Tstarba
r上
创建TToolbar了,并且为工具条(Toolbar)添加按钮及其事件,通过调用API中的

winexec实现对Window的应用程序的调用。下面给出了详细的代码和注释给大家分
享。
  unit superbar;
  interface
  uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, F
orms, 
Dialogs,ComCtrls;
  type
   TForm1 = class(TForm)
   StatusBar1: TStatusBar;{为窗体添加一个状态栏}
   procedure FormCreate(Sender: TObject);
   private
   {声明以下过程}
   procedure createsuperbar(sender:Tobject;Imagelist:TImagelist);
   {创建工具条}
   procedure createbutton(toolbar:TToolbar;Const ButtonCaptions: arr
ay of String);
   {创建工具条的按钮}
   procedure superbuttonclick(sender:Tobject);
   {为工具条按钮添加事件}
   public
   end;
  var
   Form1: TForm1;
   toolbar:TToolbar;
  implementation
  uses shellapi,commctrl;{注意要加入此句,否则程序运行无法通过}
  {$R *.DFM}
  procedure TForm1.createsuperbar(sender:Tobject;Imagelist:Timagelis
t);
  begin
   toolbar:=TToolbar.Create(self);{动态创建一个工具条}
   with toolbar do
   begin
   parent:=statusbar1;{定义toolbar的父类为状态栏,这样就可以创建出状
态栏的工具条了}
   top:=1;left:=1;height:=18;
   buttonheight:=16;buttonwidth:=16;{定出toolbar和toolbutton的宽度长
度};
   flat:=true;autosize:=true; {设置toolbutton为浮动形式}
   images:=imagelist;{设置toolbutton的图标}
   {使工具条可以脱离状态栏.注意:如果你用的是DELPHI3.0需删除此两句,即
可用}
   dragkind:=dkdock;
   dragmode:=dmautomatic;
   end;
  end;
  procedure TForm1.createbutton(Toolbar:TToolbar;Const ButtonCaption
s: array of String);
  var
   I,m: Integer;
  begin
   m:=0;
   for I := 0 to High(ButtonCaptions) do
   begin
   with TToolButton.Create(ToolBar) do
   begin
   Parent := ToolBar;
   Caption := ButtonCaptions[I];
   onclick:=superbuttonclick;{为toolbutton增加鼠标click事件}
   if (ButtonCaptions[I]=‘|’) then{判断是不是分隔符}
   begin
   Style := tbsSeparator;
   m:=m+1;
   end
   else
   begin
   Style := tbsButton;
   imageindex:=i-m;
   end;
   end;
   end;
  end;
  {这里是响应鼠标事件,实现应用程序的打开}
  procedure TForm1.superbuttonclick(sender:Tobject);
  begin
  winexec(pchar((sender as TToolbutton).caption),SW_ShowNormal);
{打开一个Windows的应用程序}
  end;
  procedure TForm1.FormCreate(Sender: TObject);
  const
   ExeList:array[0..2] of String=(
   ‘winfile.exe’,
   ‘|’,
   ‘notepad.exe’);{可以在这里加上其它应用程序的全称,也可以不要分隔
符}
  var
   imagelist:Timagelist;
   i:integer;
  begin
   imagelist:=Timagelist.Create(self);
   try{加入安全代码,也可不加}
   for i:=0 to high(exelist) do
   if exelist[i] <> ‘|’ then
   begin
  ImageList_AddIcon(ImageList.Handle,ExtractIcon(Handle,PChar(ExeL
ist[i]),0));
{为imagelist添加图标}
   end;
   createsuperbar(self,imagelist);
   createbutton(toolbar,exelist);
   finally
   end;
  end;
  end.
  以上程序已在Pentium-MMX166 32M Windows 98 DELPHI 4.0 通过,小弟反

测试没有问题,才斗胆拿出来与大家分享,如有问题望广大Delphi迷能给小弟多

意见,在下将翘首期待你的来信。本人的E-mail:[email protected].谢谢!

                    
***********************************************************
 
                       用DirectDraw编写动画程序 

                             (成都 张巍)

  一.理论篇
  说起DirectDraw也许大多数人还不知其为何物,但一提到DirectX恐怕每一个

Computer Fan 和Game Fan都再耳熟不过了。(什么!你没听说过DirectX!?Oh,My
 god!
来人哪,拉下去重责五十大板!)DirectX又叫Game SDK,它最大的特点是直接对硬

件的抽象层(HAL)进行操作,利用这个特点可以制作出高性能的Windows游戏。具

体信息请见http://www. microsoft.com/directx/default.asp。
  DirectDraw就是DirectX5的6个组件之一。DirectX5的其它5个组件分别是:

  Direct3D:提供了3D硬件接口。
  DirectSound:立体声和3D声音效果,同时管理声卡的内存。
  DirectPlay:支持开发多人网络游戏,并能处理游戏中网络之间的通信问题

  DirectInput:为大量的设备提供输入支持。
  DirectSetup:自动安装DirectX驱动程序。
  而DirectDraw则是DirectX的基石,DirectX的其它组件都是建立在它的基础

之上的。DirectDraw使用页面切换的方法实现动画,它不仅可以访问系统内存,

还可以访问显示内存,这是以往的Windows程序员所不能的。另外,我们利用
DirectDraw还可以生成、移动、剪切、转换、合成图像数据,从而编写出各种
“炫丽多彩”图形的应用程序。
  介绍了这么一大堆DirectX的理论,你的头是不是已经有些大了呢?心中一

定在嘀咕:“哼,原来是个江湖骗子,光说不练。”各位看官不要着急,不把这

些基础理论搞懂就去看下面的程序,你肯定是“洋鬼子看戏——目瞪口呆”。
  首先,让我们先了解一下DirectDraw的三个重要概念。
  1.表面
  在用DirectDraw编写程序时,我们先要创建若干个图形数据缓冲区,并把这些

图形数据装入其中,再进行转换、拉伸、挎贝等操作,并且还可以显示这些缓冲

区中的图形数据,这些缓冲区就称为表面。
  表面可以分为几类。
  主表面(primary surface)是用户在屏幕上可以看到的,它是显示内存的一部

分。所有DirectDraw程序都有主表面,而且只有一个。它在DirectDraw表面对象

之前就已经存在了,因此不能改变它的尺寸、格式和位置。主表面有一个很重要

的特性——翻转。页面翻转用于程序中,可以产生相当平滑、不闪烁的动画。一

个可以翻转的主表面实际上是两个表面,一个是可见的,一个是不可见的。不可

见的表面称为后备缓冲区。当发生表面翻转时,后备缓冲区就成为可见的,而以

前的可见表面则成为后备缓冲区。
  还有一种表面叫离屏表面(off_screen surface),它是不能直接见到的。离

屏表面作为存储缓冲区,有助于表面之间的互相切换,它的大小是可以改变的。

  主表面和离屏表面都分为有调色板的和无调色板的这两类。像素深度为8位

(256色)的表面称为有调色板的表面;而像素深度为16位(64K色)、24位(16M色)的

像素表面称为无调色板的表面,它们存储实际的色彩值(RGB值)。在本文下面的程

序中,我们使用24位表面即无调色板的表面。
  2. Bltting
  Bltting是用于复制图形的语言,可以将图像从一处拷贝到另一处。例如大家

所熟悉的CDC类(设备描述表类)的BitBlt()就是具有这样功能的函数。在DirectD
raw
中,典型的blt操作是将离屏表面的内容拷贝到一个后备缓冲区,而一般的blt操

作调用一个源表面和一个目标表面,把源表面的内容拷贝到目标表面中,不仅可

以整体拷贝源表面,而且还可以拷贝源表面内的任何矩形区域到目标表面的任何

位置。blt还支持透明拷贝,就是指表面中的某一像素在blt过程中可以不予以拷

贝,而这个像素值是由色彩键码(DDCOLOR KEY )决定的。
  DirectDraw中有三个支持blt的函数,它们是Blt()、BltBatch()、BltFast(
)。
Blt( )用得最多,BltFast()的速度比Blt()要快,但功能却很有限,例如不支持

拉伸、剪切等操作。
  还有一个函数BltSurface(),它是DirectWin类的一个成员函数,Blt()、
BltFast()更具有适应性,并且使用起来更加简单。例如,当我们把源表面拷贝到

目标表面外时需要裁剪,而BltFast()不支持裁剪。这时我们使用BltSurface()函

数,它在内部使用Blt()和 BltSurface()函数,并根据情况自动执行裁剪。
  3.色彩键码
  DirectDraw 可以把某种颜色或某个范围的颜色指定为一个颜色值,这个颜色

值是由DDCOLORKEY结构即色彩键码说明的,DDCORLORKEY结构说明如下:
  typedef struct _DDCOLORKEY{ 
  DWORD dwColorSpaceLowValue; //颜色范围的低端
  DWORD dwColorSpaceHighValue; //颜色范围的高端
  } DDCOLORKEY;
  当我们对表面进行拷贝操作时,表面中哪些像素不被拷贝是由色彩键码决定的

例如当DDCOLORKEY结构的两个分量都为零时,表面内所有置为零的像素都不能被

拷贝。又例如,当表面是24位RGB模式时,若想指定RGB=(120,120,120)像素不

被拷贝,则应该:
  DDCOLORKEY ddck;
  ddck.dwColorSpaceLowValue=RGB(120,120,120);
  ddck.dwColorSpaceHighValue=RGB(120,120,120);
  surf→SetColorKey(DDCKEY_SRCBLT,&ddck);
  其中SetColorKey()函数是把色彩键码赋给表面surf。这样,在对表面surf的

blt操作期间RGB值为(120,120,120)的像素不能被拷贝。
  二.实践篇
  古语云:“君欲善其事,必先利其器”,在编写DirectDraw应用程序之前,

我们先要准备好以下工具:
  Windows95、Windows98或WindowsNT4.0
  DirectX 驱动程序(最好是DirectX3.0以上版本)
  DirectX SDK
  Visual C++ 5.0
  Direct SDK包括开发基于DirectX应用程序所需的全部文件,全部安装需要8
0
几兆的硬盘空间。其实你只需安装必需的头文文件(.h文件)和库文件(.lib文
件)
就行了。
  安装完DirectX SDK,需要通知Visual C++ DirectX SDK的路径。具体做法
是:
在VC的编译环境中,依次把Tools-Options-Directories中的Show Directorie
s for
一栏中的include files和library files中分别填入SDK的inc和lib目录。
  再准备两幅bmp格式的位图,要求24位(16M色),其中background.bmp作背
景,
另一幅bird.bmp作为子画面,如图3。bird.bmp是由4幅小画面组成的,从左到右

从上到下,分别为动画的1至4帧。这样做的目的是避免过多位图文件带来的不必

的麻烦。还要注意一点的是子画面的背景要为黑色(RGB=(0,0,0)),因为在下面的

程序中,色彩键码把黑色设为透明色。
  好,一切准备就绪,让我们开始吧!
  进入VC5的编程环境,File-New-Project,选择DirectDraw AppWizard,输入

项目名Fly,按下Ok,以后每一步都按其缺省值即可,这样AppWizard就会自动创建

一个项目Fly,属性如下:
  APPLICATION STYLE
   Full-Screen
  SETTINGS
   640x480
   16-bit
  CLASS NAMES
   FlyApp
   FlyWin
  CONTENT
   Bitmap
  这时,按“Ctrl+F5”编译运行,你就会看到一个“三角形”在屏幕上撞来
撞去。
  在FlyWin.h中添加代码,如下所示:
  #ifndef FLYWIN_H
  #define FLYWIN_H
  #include “DirectDrawWin.h”
  
  class FlyWin : public DirectDrawWin
  {
  public:
   FlyWin();
  protected:
   //{{AFX_MSG(FlyWin)
   afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
   afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
   afx_msg void OnTimer(UINT nIDEvent);
   afx_msg void OnDestroy();
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
  private:
   int SelectDriver();
   int SelectInitialDisplayMode();
   BOOL CreateCustomSurfaces();
   void DrawScene();
   void RestoreSurfaces();
  private: 
   void CopySurface(LPDIRECTDRAWSURFACE ts,
   LPDIRECTDRAWSURFACE ss,
  int x,int y);
   void SplitSurface(LPDIRECTDRAWSURFACE ts,
   LPDIRECTDRAWSURFACE ss,
   CRect& srcc,
   int x,int y);
  private:
   LPDIRECTDRAWSURFACE bksurf, //定义指向背景画面表面的指针
  flysurf, //指向总子画面表面的指针
  storesurf,//指向存储背景区域表面的指针
  flysurf1, //指向第一帧子画面表面的指针
  flysurf2, //指向第二帧子画面表面的指针
  flysurf3, //指向第三帧子画面表面的指针
  flysurf4, //指向第四帧子画面表面的指针
  cursurf; //指向当前移动的子画面表面的指针
  //背景画面指background.bmp;总子画面指bird.bmp
  
  int x,y,nPreX,nPreY;//定义子画面移动的坐标
  int nIncX,nIncY;// 定义子画面移动的像素数
   int nCount; //定义计数器
   DWORD fw,fh; //定义总子画面的宽和高
  };
  #endif
  其中,CopySurface()函数起保存被子画面覆盖掉的背景区域的作用。参数t
s
是指向目标表面的指针,ss是指向源表面的指针,x、y是源表面上需要拷贝的矩

形区域左上角的坐标。
  SplitSurface()函数用于分离一个大的总子画面表面为4帧小子画面表面。参

数ts是指向目标表面的指针,ss是指向源表面的指针,srcc是源表面上需要选取

的矩形区域,x、y要拷贝到目标表面上的左上角坐标。
  其实当我们看了下面FlyWin.cpp中这两个函数的原型后,细心的朋友也许会

发现,两个函数完全可以写成一个函数。而本文特意写成两个函数,为的是程序

的清楚易读,不易引起混淆。
  下一步在FlyWin.cpp中添加代码,如下所示:
  #include “Headers.h”
  #include “resource.h”
  #include “DriverDialog.h”
  #include “FlyWin.h”
  #pragma comment (lib,“ddraw.lib”)
  #pragma comment (lib,“dxguid.lib”)
  
  const DWORD desiredwidth=640;
  const DWORD desiredheight=480;
  const DWORD desireddepth=16;
  
  const int nMoveSpeed=100; //设置动画速度为100毫秒/帧
  const int nMoveIncX=8; //设置子画面沿X轴移动8个像素
  const int nMoveIncY=8; //设置子画面沿y轴移动8个像素
  
  BEGIN_MESSAGE_MAP(FlyWin, DirectDrawWin)
   //{{AFX_MSG_MAP(FlyWin)
   ON_WM_KEYDOWN()
   ON_WM_CREATE()
   ON_WM_TIMER()
   ON_WM_DESTROY()
   //}}AFX_MSG_MAP
  END_MESSAGE_MAP()
  
  FlyWin::FlyWin()
  {
   x=nPreX=300; //子画面初始位置为(300,150)
   y=nPreY=150;
   nIncX=nMoveIncX;
   nIncY=nMoveIncY;
   nCount=1;
  //设置计数器 
  }
  BOOL FlyWin::CreateCustomSurfaces()
  {
   // create your surfaces here...
   //创建背景表面,并由指针bksurf指向这个表面
   bksurf=CreateSurface(“background.bmp”);
   //创建一个总子画面(由4帧子画面组成)的表面
   //由指针flysurf指向这个表面.
   flysurf=CreateSurface(“bird.bmp”);
  
   //得到总子画面表面的宽(fw),高(fh)
  GetSurfaceDimensions(flysurf,fw,fh);
  
   //创建4帧宽为fw/2,高为fh/2的子画面的表面,分别由
   //指针flysurf1,flysurf2,flysurf3,flysurf4指向它们。
   //现在这4个子表面还是空的。
   flysurf1=CreateSurface(fw/2,fh/2);
   flysurf2=CreateSurface(fw/2,fh/2);
   flysurf3=CreateSurface(fw/2,fh/2);
   flysurf4=CreateSurface(fw/2,fh/2);
  
   //清除4帧子画面表面的内容,使表面内所有像素为0,
   //即表面是透明的。
   ClearSurface(flysurf1,0);
   ClearSurface(flysurf2,0);
   ClearSurface(flysurf3,0);
   ClearSurface(flysurf4,0);
  
  //定义色彩键码,指明黑色(RGB=(0,0,0))为透明色
  //即在blt操作期间不拷贝黑色像素。
  DDCOLORKEY ddck;
  ddck.dwColorSpaceLowValue=0;
  ddck.dwColorSpaceHighValue=0;
  //将色彩键码赋给背景表面,总子画面表面和4个子画面表面
  bksurf→SetColorKey(DDCKEY_SRCBLT,&ddck);
  flysurf→SetColorKey(DDCKEY_SRCBLT,&ddck);
  flysurf1→SetColorKey(DDCKEY_SRCBLT,&ddck);
  flysurf2→SetColorKey(DDCKEY_SRCBLT,&ddck);
  flysurf3→SetColorKey(DDCKEY_SRCBLT,&ddck);
  flysurf4→SetColorKey(DDCKEY_SRCBLT,&ddck);
  
  //定义总子画面表面上的4个矩形区域,即为4个子画面表面区域
  CRect r1(0,0,fw/2-1,fh/2-1);
  
  CRect r2(fw/2,0,fw-1,fh/2-1);
  CRect r3(0,fh/2,fw/2-1,fh-1);
  CRect r4(fw/2,fh/2,fw-1,fh-1);
  //把总子画面表面上的4个矩形区域分别拷贝到4个子画面表面
  //即实现分离总子画面表面
  SplitSurface(flysurf1,flysurf,r1,0,0);
  SplitSurface(flysurf2,flysurf,r2,0,0);
  SplitSurface(flysurf3,flysurf,r3,0,0);
  SplitSurface(flysurf4,flysurf,r4,0,0);
  //创建一个存储表面,由指针storesurf指向它
  //用以存储子画面移动时所覆盖的背景区域
  storesurf=CreateSurface(fw/2,fh/2);
  CopySurface(storesurf,bksurf,x,y);
  
  //初始化当前子画面表面
  cursurf=flysurf1;
  return TRUE;
  }
  void FlyWin::DrawScene()
  {
   //绘制背景表面和第一帧子画面表面
   //把背景表面拷贝到后备缓冲区
   BltSurface(backsurf,bksurf,0,0,TRUE);
   //把当前的子画面表面拷到后备缓冲区
   BltSurface(backsurf,cursurf,x,y,TRUE); 
   //页面翻转,使后备缓冲区的内容可见
   primsurf→Flip(0,DDFLIP_WAIT);
  
   //再次把背景表面拷贝到后备缓冲区
   BltSurface(backsurf,bksurf,0,0,TRUE);
  }
  
  void FlyWin::RestoreSurfaces()
  {
  // reclain lost surfaces with the DirectDrawSurface Restore() func
tion
  // depending on the surface's function, it may be necessary to res
tore
  // surface content as well
   if(bksurf→IsLost()) //如果bksurf
  丢失
   {
   bksurf→Restore(); //恢复内存
   LoadSurface(bksurf,“background.bmp”); //恢复表面内容
   }
   if(flysurf→IsLost())//如果flysurf丢失
   {
   flysurf→Restore(); //恢复内存
   LoadSurface(flysurf,“bird.bmp”); //恢复表面内容
   }
  }
  
  int FlyWin::SelectDriver()
  {
   int numdrivers=GetNumDrivers();
   if (numdrivers==1)
   return 0;
  
   CArray drivers;
   for (int i=0;i    {
   LPSTR desc, name;
   GetDriverInfo( i, 0, &desc, &name );
   drivers.Add(desc);
   }
   DriverDialog dialog;
   dialog.SetContents( &drivers );
   if (dialog.DoModal()!=IDOK)
   return -1;
   return dialog.GetSelection();
  }
  int FlyWin::SelectInitialDisplayMode()
  {
   DWORD curdepth=GetDisplayDepth();
   int i, nummodes=GetNumDisplayModes();
   DWORD w,h,d;
   if (curdepth!=desireddepth)
   ddraw2→SetDisplayMode( 640, 480, curdepth, 0, 0 );
   for (i=0;i    {
   GetDisplayModeDimensions( i, w, h, d );
   if (w==desiredwidth && h==desiredheight && d==desireddepth)
   return i;
   }
   for (i=0;i    {
   GetDisplayModeDimensions( i, w, h, d );
   if (d==desireddepth)
   return i;
   }
   return 0;
  }
  void FlyWin::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
  {
   if (nChar==VK_ESCAPE) //按ESC退出
   PostMessage( WM_CLOSE );
   DirectDrawWin::OnKeyDown(nChar, nRepCnt, nFlags);
  }
  
  int FlyWin::OnCreate(LPCREATESTRUCT lpCreateStruct)
  {
   if (DirectDrawWin::OnCreate(lpCreateStruct) == -1)
   return -1;
   // TODO: Add your specialized creation code here
   SetTimer(1,nMoveSpeed,NULL); //设置定时器
   return 0;
  }
  
  void FlyWin::OnTimer(UINT nIDEvent)
  {
   // TODO: Add your message handler code here and/or call default
   int nWhich=nCount%4+1;
   nCount++;
   //判断当前表面为4个子表面中的哪一个
   switch(nWhich)
   {
   case 1:
   cursurf=flysurf1;
   break;
   case 2:
   cursurf=flysurf2;
   break;
   case 3:
   cursurf=flysurf3;
   break;
   case 4:
   cursurf=flysurf4;
   break;
   default:
   break;
   }
   //处理移动的子画面
   x+=nIncX;
   y+=nIncY;
   //如子画面移动到屏幕下方或上方,应碰撞返回
   if(y>=(int)(480-fh/2)||y<=0)
   {
   nIncY=-nIncY;
   y+=nIncY;
   }
   //如子画面移动到屏幕右方或左方,应碰撞返回
   if(x>=(int)(640-fw/2)||x<=0)
   {
   nIncX=-nIncX;
   x+=nIncX;
   }
   //把当前表面拷贝到后备缓冲区
   BltSurface(backsurf,cursurf,x,y,TRUE);
   //执行页面翻转,使后备缓冲区的内容可见
   primsurf→Flip( 0, DDFLIP_WAIT );
  
   //把备份的背景区域拷贝到后备缓冲区,以便恢复曾被
   //覆盖的背景
   BltSurface(backsurf,storesurf,nPreX,nPreY);
   //存储下一帧的子画面移动时所覆盖的背景区域于storesurf
   CopySurface(storesurf,bksurf,x,y);
   nPrex=x;
   nPrey=y;
   DirectDrawWin::OnTimer(nIDEvent);
  }
  void FlyWin::OnDestroy()
  {
   DirectDrawWin::OnDestroy();
   // TODO: Add your message handler code here
   KillTimer(1); //取消定时器
  }
  void FlyWin::CopySurface(LPDIRECTDRAWSURFACE ts,
   LPDIRECTDRAWSURFACE ss,
   int x,int y)
  {
   if(!ts || !ss ) return;
   DDSURFACEDESC tdesc,sdesc;
   //初始化sdesc,tdesc为零
   ZeroMemory(&sdesc,sizeof(sdesc));
   ZeroMemory(&tdesc,sizeof(tdesc));
   sdesc.dwSize=sizeof(sdesc);
   tdesc.dwSize=sizeof(tdesc);
   //得到源表面ss,目标表面ts的描述信息
   HRESULT r=ss→GetSurfaceDesc(&sdesc);
   if(r!=DD_OK) return;
   r=ts→GetSurfaceDesc(&tdesc);
   if(r!=DD_OK) return;
   //定义源表面上的矩形区域,大小大等于目标表面的面积
   CRect rc(x,y,x+tdesc.dwWidth,y+tdesc.dwHeight);
   //超界处理
   if(x+tdesc.dwWidth>(int)sdesc.dwWidth) return;
   if(y+tdesc.dwHeight>(int)sdesc.dwHeight) return;
   //拷贝源表面ss到目标表面ts
   r=ts→BltFast(0,0,ss,&rc, DDBLTFAST_SRCCOLORKEY|DDBLTFAST_WAIT);

   if(r!=DD_OK) return;
  }
  void FlyWin::SplitSurface(LPDIRECTDRAWSURFACE ts,
   LPDIRECTDRAWSURFACE ss,
   CRect& srcc,
   int x,
   int y)
  {
   if(!ts||!ss) return;
   DDSURFACEDESC tdesc,sdesc;
   ZeroMemory(&sdesc,sizeof(sdesc));
   ZeroMemory(&tdesc,sizeof(tdesc));
   sdesc.dwSize=sizeof(sdesc);
   tdesc.dwSize=sizeof(tdesc);
   HRESULT r=ts→GetSurfaceDesc(&tdesc);
   if(r!=DD_OK) return;
   r=ss→GetSurfaceDesc(&sdesc);
   if(r!=DD_OK) return;
   //超界处理
   if(x+srcc.Width()>(int)tdesc.dwWidth) return;
   if(y+srcc.Height()>(int)tdesc.dwHeight) return;
   //把源表面srcc的矩形区域拷贝到目标表面左上角坐标为(x,y)处
   r=ts→BltFast(x,y,ss,&srcc,
   DDBLTFAST_SRCCOLORKEY|DDBLTFAST_WAIT); 
   if(r!=DD_OK) return;
  }
  DDSURFACEDESC为表面描述结构,用以保存表面的所有信息,它的成员变量

dwSize为结构的大小而且必须要被初始化;dwHeight为表面的高度;dwWidth为

表面的宽度。用函数GetSurfaceDesc()可以获取上述描述信息。 
  DirectDraw首先调用CreateCustomSurface()函数,我们将在这个函数中创

建和准备程序所用到的表面。DrawSence()函数负责更新屏幕,因此我们用它来

显示背景画面。我们主要的工作都在OnTimer()函数里,首先判断当前子画面为

4帧子画面中的哪一帧,然后进行页面翻转、显示子画面和复制背景区域等操作。

  BltFast()函数用于表面之间的拷贝,它的前两个参数指出blt的目标表面位

置,第三个参数是指向源表面的指针,第四个参数是源表面上被拷贝的矩形区域

最后的参数是DDBLTFAST_SRCCOLORKEY和DDBLTFAST_WAIT,第一个标志用来激活

源表面的色彩键码;第二个标志表示只有结束blt操作,BltFast()函数才返回。

  Flip()函数用于页面翻转操作,使backsurf表面的内容可见。它有两个参数

第一个参数是个表面指针,它在用来翻转有多个后备缓冲区的表面时才有用;第

二个参数是DDFLIP_WA
  IT标志,用来指示函数只有在表面完成之后才能返回。
  当程序运行时,一个表面所在的内存可能会被另一个应用程序所占用,这形

成了“表面丢失”。这时DirectDraw会调用RestoreSurface()函数来恢复丢失的

表面。但是,如果你的显卡不是老掉牙了,这种情况一般是不会发生的。所以这

里我只对两个主要表面bksurf、flysurf进行了恢复处理,而不再管其它的表面。

  另外,函数SelectDriver()和SelectInitialDisplayMode()是AppWizard自动

创建的。SelectDriver()选择一个显示驱动程序,SelectInitialDisplayMode()

选择一个初始显示模式。
  三、结束语
  话已至此,你对DirectDraw编程应有一个大致的了解了吧!用DirectDraw编

程其实很简单,说白了其实就是一句话:“几个表面之间拷来拷去”。只不过这

其间可是大有文章可作的哟!            
                 
***********************************************************
     
                     如何在C++Builder中检测硬件 

                           (西安 杨洪辰)

  在我们编写的程序中常常要和硬件打交道,那么如何在程序中确定系统中是否
有该设备,
它的运行状态又是怎样的呢?对于初学者来说,这个问题常常不好解决,其实只
需简单地利
用几个API函数,硬件的问题并不神秘。下面就让我们一起看看在C++ Builder
中是如何
检测硬件的。
  1. 检测CPU的型号
  先让我们从最简单的做起,看一看自己的CPU型号。首先,在C++ Builder
中画出图1
所示的窗体,在下面的几个例子中我们将一直使用这个窗体作示范,它包括一个
用来激活测
试的Button和一个用来显示结果的Memo。我们可以用GetSystemInfo这个API获得
CPU的型
号。将下列代码添加到Button的Click事件里就可以了:
  void __fastcall TForm1::Button1Click(TObject *Sender)
  {
  //获得CPU型号
  SYSTEM_INFO systeminfo;
  GetSystemInfo (&systeminfo);
  Memo1→Lines→Add(“您的CPU类型是:”+String( systeminfo.dwProcess
orType ));
  }
  运行它,点击Test试试,CPU型号出来了吧!
  2.检测内存状态
  获得内存状态的方法和CPU型号差不多,只是他用到的是另外一个API:Glob
alMemoryStatus。
其中,成员dwTotalPhys用来获得物理内存总量,而dwAvailPhys顾名思义是有效
物理内存
的意思。我们只要把下面几行代码加到上面程序的后面就可以了(不用重做,下
同):
  //获得内存状态
  MEMORYSTATUS memory;
  memory.dwLength =sizeof(memory); //初始化
  GlobalMemoryStatus(&memory);
  Memo1→Lines→Add(“您的物理内存是(Mb):”+String(int(memory.dwTot
alPhys /1024/1024)));
  Memo1→Lines→Add(“其中可用内存是(Kb):”+String(int( memory. /10
24)));
  怎么样,看出点门道了么?两段程序的格式几乎一模一样,其实,GetSyste
mInfo和
GlobalMemoryStatus还可以获得许多其他有关CPU和内存的信息,就按照上面的格
式去套
就行了,更详细的资料可以去看C++ Builder4的Help。
  3. 检测可用硬盘空间
  好了,经过前面两个简单问题的热身,我们来处理一个稍微复杂的问题:我
们知道安
装程序大都有一个检测硬盘空间的过程,那么这是怎么实现的呢?他用到的是API
函数
GetDiskFreeSpace,这个函数输入一个参数:目标盘的路径;返回四个参数,依
次是每簇
的扇区数、每扇区的字节数、空闲的簇数、总簇数。假如我们需要检测C盘的总容
量和可用
容量,那么可以把以下代码加到上面的程序中:
  //获得C盘可用空间
  DWORD sector,byte,cluster,free;
  long int freespace,totalspace;
  GetDiskFreeSpace(“C:”,&sector,&byte,&free,&cluster); //获得返
回参数
  totalspace=int(cluster)*int(byte)*int(sector)/1024/1024; //计算总
容量
  freespace=int(free)*int(byte)*int(sector)/1024/1024; //计算可用空

  Memo1→Lines→Add(“C盘总空间(Mb):”+String(totalspace));
  Memo1→Lines→Add(“C盘可用空间(Mb):”+String(freespace));
  怎么样?现在可以自己做安装程序了吧!
  4. 检测CD-ROM
  我们在编写程序时常常需要读取CD-ROM,可是究竟哪一个盘符是光驱呢?有
人是将最
后一个盘符当作光驱的,但是当遇到双光驱或者MO的情况时常常会出错。其实这
个问题用
一个API来解决并不困难,这就是:GetDriveType(),这个函数返回一个0~6之间
的值,
依次代表:0—未知盘、1—不存在、2—可移动磁盘、3—固定磁盘、4—网络磁盘

5—CD-ROM、6—内存虚拟盘。因此我们可以添加下面代码来寻找CD-ROM:
  // 获得CD-ROM信息
  UINT type;
  char name;
  for (name=‘C’;name<=‘Z’;name++) //循环检测A~Z
  { type = GetDriveType((String(name)+String(‘:’)).c_str()); //获
得磁盘类型
  if (type==5)
  Memo1→Lines→Add(“您的光驱盘符为:”+String(name));
   }
  得到光驱盘符之后我们可以进一步利用API函数GetVolumeInformation检测光
驱中
是否有光盘,这个函数如果成功调用,会得到磁盘的卷标序列号等信息;如果调
用失败则
可知光驱中无光盘,程序如下://检测光盘(假设光驱为G:)
  char volname[255],filename[100];//buffer[512];
  DWORD sno,maxl,fileflag ;
  if (!(GetVolumeInformation(“G:”, volname,255,&sno,&maxl,&file
flag,filename,100)))
   //如果返回值为假
  Memo1→Lines→Add (“G驱中没有发现光盘”);
   else
   //如果返回值为真
  {Memo1→Lines→Add (“G驱中光盘卷标为:”+String(volname));
  Memo1→Lines→Add (“G驱中光盘序号为:”+String(sno));
   }
  5. 检测声卡配置
  在编制多媒体程序时,我们常常会用到声音文件,而当这些程序在没有配置
声卡的机
器上运行时,我们应该给出必要的警告。对于声卡的检测,可以分别通过waveOu
tGetNumDevs()
和midiOutGetNumDevs()检测波形设备和MIDI设备,再利用waveOutGetDevCaps()

midiOutGetDevCaps()获得声音设备的细节资料。将下面一段代码加入上面的程序
即可,
但要注意将#include 添至程序首部:
  //检测声卡
  int wavedevice,mididevice;
  WAVEOUTCAPS wavecap;
  MIDIOUTCAPS midicap;
  wavedevice=(int)waveOutGetNumDevs(); //波形设备信息
  mididevice=(int)midiOutGetNumDevs(); // MIDI设备信息
  if (wavedevice==0)
  Memo1→Lines→Add (“没有发现波形设备”);
  else
  {waveOutGetDevCaps(0,&wavecap,sizeof(WAVEOUTCAPS));
  Memo1→Lines→Add (“当前波形设备:”+String(wavecap.szPname));
   }
  if (mididevice==0)
  Memo1→Lines→Add (“没有发现MIDI设备”);
  else
  {midiOutGetDevCaps(0,&midicap,sizeof(MIDIOUTCAPS));
  Memo19→Lines→Add (“当前MIDI设备:”+String(midicap.szPname));
   }
  6. 检测显示器信息
  编写和图形图像有关的程序时常常需要检测显示器的分辨率和色深,最后我
们来看看
这个问题的解决办法。分辨率的求法很简单,直接调用Screen对象的属性就行了
。而要求
色深则要利用API函数GetDeviceCaps获得每像素的比特数和色彩的页面数,然后
计算2的
“每像素的比特数”次幂即得色彩的梯度数,再计算“色彩的梯度数”的“色彩
的页面数”
次幂即得色深。由于该段程序用到了幂运算,所以要记得加上#include ,程序
如下:
  //检测显示器
  int tcs;
  long int bpp,cp,tc;
  Memo1→Lines→Add (“当前分辨率为:”+String(Screen→Width)+“*”

String(Screen→Height));
  bpp=GetDeviceCaps(Form1→Canvas→Handle ,BITSPIXEL);
  tcs=pow(2,bpp); //计算色彩的梯度数
  cp= GetDeviceCaps(Form1→Canvas→Handle,PLANES);
  tc= pow(tcs,cp); //计算色深
  Memo1→Lines→Add(“当前色深为:”+String(tc));
  好了,现在在让我们点击一下Test吧,硬件情况尽收眼底!其实本文所涉及

的API函数的功能不止这些,大家下去可以查一查Win32 API手册,或者直接在C+

Builder 4中察看Help。相信自己开发一个硬件检测软件也不是难事哦!
  以上程序均在Windows98中文版、C++ Builder 4中调试通过,如果大家在
硬件检
测方面有什么问题欢迎和我探讨,mail to [email protected]

***********************************************************
***********************************************************

<疑难杂症>

028. mailto: [email protected]?subject=028
在使用Delphi4时,遇到如下两个小问题:
1。在使用WritePrivateProfileSection向INI文件中写一个整数时,
总是先删除该段的内容后,再写入该条信息,如何将原有信息保留,
只将该修改的信息修改?
2。在调用GetTempPath(20,aa)时,结果正确,将临时目录存到aa中,
但在执行该操作之后执行Form1.Close关闭窗口时,产生一个一般保护
性错误,程序终止,是何道理?如何解决?[email protected]

029. mailto: [email protected]?subject=029
请问:如何用VC++5.0调用VB5.0生成的动态连结库(.dll) ?
[email protected]

030. mailto: [email protected]?subject=030
在下请教两个问题:
1. 在 DELPHI 4.0 中用 reportsmith 报表工具制作报表时存在如下
问题:即不管打印内容的多少,打印结束后,打印机总会走不少空白
纸,这样会造成很多不方便与浪费,请问有没有什么好方法可以控制
打印机在打印内容结束后不进行走纸,也就是说有多少内容就走多少
纸。(对针式打印机而言)
2. 用 reportsmith 报表工具制作出的报表能否转存为一个文本文件
或者是一个 EXCEL 文件,总之只要可以在 EXCEL 中编辑的格式即可! 
谢谢!盼望您的回复![email protected]
   
031. mailto: [email protected]?subject=031
您好!我有几个个问题向请教
1.我在我的视中定义了一个spin控件并映射了WM_VSCROLL消息(我的
视类从CFORMVIEW派生),程序编译通过。但运行时,一拖动框架内滚
动条,windows即刻报“执行非法程序”。我想这可能与我映射的
WM_VSCROLL有关。请问如何解决这个问题。
2.使复选框的选择状态与下拉菜单的选择状态同步;
我已实现了选择复选框时菜单与之同步,但反向(菜单呈选择状态而复
选框呈未选状态)则无法实现.通过什么方法来改变复选框的选则状态?
[email protected]

032. mailto: [email protected]?subject=032
请问在 VC++5.0 下如何得到机器内存地址数据.
E-mail:[email protected]

如果您有什么疑难问题,就请把问题发往[email protected]
主题为“提问”,我会把问题放在杂志上的,希望编程高手们能
不吝赐教,也希望这个栏目能成为大家交流的场所。

***********************************************************
***********************************************************

<编者的话>


本刊已加入【CHINA-EZL】中国电子杂志联盟
联盟主页地址 http://china-ezl.yeah.net

如果您觉得这份刊物还不错的话,欢迎把它介绍给您的朋友.
不知道您看了这期刊物有什么想法或者是意见,欢迎向我提出来.
本人感激不尽,我的联系方法如下:
E-mail:[email protected]
本刊主页: http://yaozheng.soim.com
          http://person.zj.cninfo.net/~yao
         
                                                
<<编程技术>>订阅方法:

请到本刊主页进行订退工作 http://person.zj.cninfo.net/~yao

投稿:向[email protected]发封邮件     主题:投稿
提问:向[email protected]发封邮件     主题:提问

**************************THE END**************************

--
※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 210.74.253.151]

[关闭][返回]