星期一, 九月 03, 2007

C Builder中如何应用消息

标准的BCB程序使用Application->Run()进入消息循环,在Application的ProcessMessage方法中,使用PeekMessage方法从消息队列中提取消息,并将此消息从消息队列中移除。然后ProcessMessage方法检查是否存在Application->OnMessage方法。存在则转入此方法处理消息。之后再将处理过的消息分发给程序中的各个对象。至此,WndProc方法收到消息,并进行处理。如果有无法处理的交给重载的Dispatch方法来处理。要是还不能处理的话,再交给父类的Dispatch方法处理。最后Dispatch方法实际上将消息转入DefaultHandler方法来处理。(嘿嘿,实际上,你一样可以重载DefaultHandler方法来处理消息,但是太晚了一点,我想没有人愿意最后一个处理消息吧)。

1.TApplication的OnMessage事件的应用
在C++Builder开发的应用程序中,任何窗体接收到一个Windows消息都会触发一次OnMessage事件,所以,可以通过相应TApplication对象的OnMessage事件来捕获任何发送给本程序的Windows消息。


OnMessage的事件的处理函数原型如下:
typedef void __fastcall (__closure *TMessageEvent ) (tagMsg &Msg,bool &Handled );
这个处理函数有两个参数,其中参数Msg表示的是被截获的消息,而参数Handled则用来指示本消息是否已经处理完成。在程序中可以通过设置参数Handled为true,以避免后续的过程处理这个消息,反之把Handled设为false则允许后继过程继续处理这个消息。
需要注意的是,OnMessage事件仅仅接受发送到消息队列的消息,而直接通过API函数SendMessage()发送给窗口函数的消息将不会被截获。另外,当程序运行的时候,OnMessage事件被触发的频率有可能非常高,所以这个事件的处理函数代码执行时间将直接影响到整个程序的运行效率。

2.利用消息映射截获消息
C++Builder的VCL提供了对大多数Windows消息的处理机制,对于一般的应用程序是足够了。但是,VCL也不是无所不包的。有的情况下,程序需要处理那些VCL处理没有处理的Windows消息,或者程序需要屏蔽某些特定的消息时,则就需要程序员自己捕获Windows消息。
为此C++Builder提供了一种消息映射机制,通过消息映射程序能将特定的Windows消息与对应的处理函数联系起来,当窗口捕获到这个消息时就会自动调用对应的处理函数。
使用消息映射有一下几个步骤:
(1) 消息映射表,把某些消息的处理权交给自定义的消息处理函数。
这样的消息映射列表应该位于一个组件类的定义中,它以一个没有参数的BEGIN_MESSAGE_MAP 宏开始,以END_MESSAGE_MAP宏结束。END_MESSAGE_MAP宏的唯一参数应该是组件的父类的名字。通常情况下,这个所谓的父类指的就是TForm。在宏BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间插入一个或者是多个MESSAGE_HANDLER 宏。
MESSAGE_HANDLER宏将一个消息句柄和一个消息处理函数联系在一起。
MESSAGE_HANDLER宏有三个参数:Windows消息名、消息结构体名和对应的消息处理函数名。其中,消息结构体名既可以是通用的消息结构体TMessage,也可以是特定的消息结构体,比如TWMMouse。
在使用消息映射的时候要注意以下两点:
a.一个窗口类定义中只能有一个消息映射表。
b.消息映射必须位于它所引用的所有消息处理函数声明的后面。
(2) 在窗口类中声明消息处理函数
这里的消息处理函数名和参数都必须和对应的MESSAGE_HANDLER宏一致。
一个典型的消息处理函数的声明如下:
void __fastcall 消息处理函数名(消息结构体名 &Message);
例如:
void __fastcall WMNchitTest(TMessage &message);
(3) 实现消息处理函数
消息处理函数的编制和普通的函数没什么太大的差异,唯一不同的是,通常在此函数的最后要加上一条语句 TForm::Dispatch(&Message),以完成VCL对于消息的默认处理。如果没有这一句,消息将会被完全拦截;在某些情况下,VCL可能会因为得不到消息而无法工作。

3.重载WndProc()函数
在某些情况下,程序需要捕获或者屏蔽某些特定的消息,这时可以用前面介绍的消息映射的方法。当然,这种方法也不是唯一的,也可以通过重载窗口函数WndProc()来实现。因为系统将在调用函数Dispatch()派发消息之前调用窗口函数WndProc(),所以,可以通过重载函数WndProc()得到一个在分派消息之前过滤消息的机会。
这个消息处理的窗口函数的原型如下:
virtual void __fastcall WndProc(TMessage &Message);
例如:(详细请看NowCan的例子)
void __fastcall TForm1::WndProc(TMessage &Message)
{
PCOPYDATASTRUCT pMyCDS;
if(Message.Msg==g_MyMsg)
{
ShowMessage("收到注册消息,wParam="+IntToStr(Message.WParam)+" lParam="+IntToStr(Message.LParam));
Message.Result=0;//消息处理的结果,当然在本例中没有意义。
}
else if(Message.Msg==g_MyMsg1)
{
Application->MessageBoxA((char *)Message.LParam,"收到发送方的字符串",MB_OK);
}
else if(Message.Msg==WM_COPYDATA)
{
pMyCDS = (PCOPYDATASTRUCT)Message.LParam;
Application->MessageBoxA((char *)pMyCDS->lpData,"收到发送方的字符串",MB_OK);
}

TForm::WndProc(Message);//其他的消息继续传递下去
}
乍看起来,这和重载Dispatch方法好象差不多。但实际上还是有差别的。差别就在先后次序上,消息是先交给WndProc来处理,最后才调用Dispatch方法的。这样,重载WndProc方法可以比重载Dispatch方法更早一点点得到消息并处理消息。

4.Application->HookMainWindow方法
如果您打算使用Application->OnMessage来捕获所有发送至您的应用程序的消息的话,您大概要失望了。它无法捕获使用SendMessage直接发送给窗口的消息,因为这不通过消息队列。您也许会说我可以直接重载TApplication的WndProc方法。呵呵,不可以。因为TApplication的WndProc方法被Borland申明为静态的,从而无法重载。显而易见,这么做的原因很可能是Borland担心其所带来的副作用。那该如何是好呢?查看TApplication的WndProc的pascal源码可以看到:
procedure TApplication.WndProc(var Message: TMessage);
... // 节约篇幅,此处与主题无关代码略去
begin
try
Message.Result := 0;
for I := 0 to FWindowHooks.Count - 1 do
if TWindowHook(FWindowHooks[I]^)(Message) then Exit;
... // 节约篇幅,此处与主题无关代码略去
WndProc方法一开始先调用HookMainWindow挂钩的自定义消息处理方法,然后再调用缺省过程处理消息。这样使用HookMainWindow就可以在WndProc中间接加入自己的消息处理方法。使用这个方法响应SendMessage发送来的消息很管用。最后提醒一下,使用HookMainWindow挂钩之后一定要对应的调用UnhookMainWindow卸载钩子程序。给个例子:

void __fastcall TForm1::FormCreate(TObject *Sender)
{
Application->HookMainWindow(AppHookFunc);
}
//---------------------------------------------------------------------------
bool __fastcall TForm1::AppHookFunc(TMessage &Message)
{
bool Handled ;
switch (Message.Msg)
{
case WM_CLOSE:
mrYes==MessageDlg("Really Close??", mtWarning, TMsgDlgButtons() << mbYes break;
}
return Handled;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
Application->UnhookMainWindow(AppHookFunc);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
SendMessage(Application->Handle,WM_CLOSE,0,0);
}
//---------------------------------------------------------------------------

5.自己发送消息
应用程序也可以像Windows系统一样在窗口或者是组件之间发送消息。C++Builder为此提供了几种途径:使用函数TControl::Perform()或者API函数SendMessage()和PostMessage()向特定的窗体发送消息,或者是使用函数TWinControl::Broadcast()和API函数BroadcastSystemMessage()广播消息。


Perform()函数的作用就是将指定的消息传递给TControl的WndProc过程,适用于所有由TControl类派生的对象,Perform()原型如下:
int __fastcall Perform(unsigned Msg, int WParam, int LParam);
要等到消息处理之后才返回。


在同一个应用程序的不同窗体和控件之间使用函数Perform()是非常便捷的。但是这个函数是TControl类的成员函数。也就是说,使用它时,程序必须知道这个接受消息的控件的实例。而在许多情况下程序并不知道这个接受消息的窗体的实例而只是知道这个窗体的句柄,比如,在不同应用程序的窗体之间发送消息就属于这种情况。这时,函数Perform()显然无法使用,取而代之的应该是函数SendMessage()和PostMessage()。


函数SendMessage()和PostMessage()的功能基本上一样,它们都可以用来向一个特定的窗口句柄发送消息。主要的区别是,函数SendMessage()直接把一个消息发送给窗口函数,等消息被处理之后才返回;而函数PostMessage()则只是把消息发送到消息队列,然后就立刻返回。
这两个函数的原型声明分别如下:
LRESULT SendMessage(HWND hWnd,UINRT Msg,WPARAM,wParam,LPARAM,lParam);
BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM,wParam,LPARAM,lParam);
可以看到,这两个函数参数同函数Perform()十分类似,只是增加了一个hWnd参数用以表示目标窗口的句柄。


Broadcast()和BroadcastSystemMessage()
函数Broadcast()适用于所有由TWinControl类派生的对象,它可以向窗体上的所有子控件广播消息。其函数原型如下:
void __fastcall Broadcast(void *Message);
可以看到,这个函数只有一个Message参数,它指向被广播的TMessage类型的消息结构体。
函数Broadcast()只能向C++Builder应用程序中的指定窗体上的所有子控件广播消息,如果要向系统中其他应用程序或者窗体广播消息,函数Broadcast()就无能为力了。这时可以使用API函数BroadcastSystemMessage(),这个函数可以向任意的应用程序或者组件广播消息。其函数原型如下:
long BroadcastSystemMessage(
DWORD dwFlags,
LPWORD lpdwRecipients,
UINT uiMessage,
WPAREM wParam,
LPARAM lParam
);

API技巧集

API技巧集(一)- -




一、拖动无标题窗体:

包含头文件:

#include $#@60;winuser.h$#@62;

在窗体或组件的 OnMouseDown 事件中加入以下代码:

if(Button == mbLeft)
{
ReleaseCapture();
SendMessage( Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
}

二、弹出和关闭光驱:

包含头文件:

#include $#@60;mmsystem.h$#@62;

在窗体的OnCreate事件中加入:

mciSendString("open cdaudio alias cd wait shareable",0,0,0);

1、要弹出光驱时使用:

mciSendString("set cd door open",0,0,0);

2、要关闭光驱时使用:

mciSendString("set cd door closed",0,0,0);

三、提取图标:

包含头文件:

#include $#@60;shellapi.h$#@62;

例子:

TIcon *Icon = new TIcon();
AnsiString FileName = "C:\\WINDOWS\\SYSTEM\\SHELL32.DLL";
int TotalIcon;

//得到文件SHELL32.DLL的总图标数
TotalIcon = (int)ExtractIcon(Form1->Handle,FileName.c_str(), -1);

//提取第一个图标,0为第一个,1为第二个,类推...
Icon->Handle = ExtractIcon( Form1->Handle, FileName.c_str(), 0);

//保存图标
Icon->SaveToFile("C:\\1.ICO");

四、设置顶端窗口(永在上面):

包含头文件:

#include $#@60;winuser.h$#@62;

1、设置顶层窗口

SetWindowPos( Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE||SWP_NOSIZE);

2、取消顶层窗口

SetWindowPos( Handle, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE||SWP_NOSIZE);


API技巧集 (二)

  (一)不规则窗口

  Windows提供的只是标准的矩形窗口,要想建立一个不规则的窗口就需要调用API函数来实现。建立一个不规则的窗口,一般是先用创建区域的API函数建立一个不规则的区域,再用API函数SetWindowRgn改变窗口的区域。这些API函数在C++ Builder中包含在头文件wingdi.h和winuser.h里面,因此,要使用这些API函数就要先在程序头部加上包含头文件的语句:

include $#@60;wingdi.h$#@62;

include $#@60;winuser.h$#@62;

  SetWindowRgn函数能改变一个窗口的区域,该函数有三个参数,第一个参数hWnd是欲设置区域的窗口句柄,第二个参数hRgn是欲设置的区域,第三个参数bRedraw一般设为true,即立即重画窗口。

  用来创建区域的API函数有多个,最常用的有三个:

  1、CreateRectRgn函数,用来创建一个由X1、Y1和X2、Y2坐标点确定的矩形区域。当坐标点X1和Y1相等、X2和Y2也相等的时候,创建的是一个正方形。

  例子:

//创建长方形

HRGN hRect=CreateRectRgn(0,0,400,200);

SetWindowRgn(Handle,hRect,true);

//创建正方形

HRGN hRect=CreateRectRgn(0,0,300,300);

SetWindowRgn(Handle,hRect,true);

  2、CreateEllipticRgn函数,用来创建一个由X1、Y1和X2、Y2坐标点确定的矩形所内切的椭圆。同样,X1、Y1和X2、Y2坐标点所确定的矩形为正方形时,创建的就是一个圆形。

  例子:

//创建椭圆

HRGN hElliptic=CreateEllipticRgn(0,0,400,250);

SetWindowRgn(Handle,hElliptic,true);

//创建圆形

HRGN hElliptic=CreateEllipticRgn(0,0,400,400);

SetWindowRgn(Handle,hElliptic,true);

  3、CombineRgn函数,能将两个区域组合为一个新区域,它有四个参数,第一个参数hrgnDest保存合并后的新区域,第二个参数hrgnSrc1、三个参数hrgnSrc2为欲合并的两个区域,第四个参数fnCombineMode是区域组合的方式,它的值是为下面组合方式之一:

  组合方式 说明

RGN_AND 建立两个区域的交集

RGN_COPY 建立hrgnSrc1的拷贝

RGN_DIFF 建立两个区域不相交的部分

RGN 建立两个区域的并集

RGN_XOR 建立除两个区域并集之外的部分

  例子:

//创建一个圆形和长方形交集的组合形状

HRGN hRect=CreateRectRgn(0,0,300,300);

HRGN hElliptic=CreateEllipticRgn(0,0,400,250);

CombineRgn(hRect,hRect,hElliptic,RGN_OR);

SetWindowRgn(Handle,hRect,true);

  当需要将窗口还原为标准Windows矩形窗口时,只要将SetWindowRgn函数的hRgn参数设为0就行了,如:

SetWindowRgn(Handle,0,true);


API技巧集 (二)



  (二)得到系统声卡的个数

  当我们编写一个多媒体程序(如播放器)的时候,有时需要检测一下计算机中是否安装了声卡,如果没有装声卡程序则会终止运行。

  在这里,我们就要使用API函数waveOutGetNumDevs,调用这个函数可返回系统中安装了的声卡的个数。在C++ Builder 5.0中,它被包含在头文件“mmsystem.h”里面。

  例子:

  1、首先在程序头部加入包含头文件的代码:

#include $#@60;mmsystem.h$#@62;

  2、在窗体的OnCreate事件中加入下面的代码:

int Num;

//得到声卡的个数

Num=waveOutGetNumDevs();

if(Num)

ShowMessage("你有安装了"+IntToStr(Num)+"块声卡");

else

{

ShowMessage("你没有安装声卡!\n程序终止运行!");

Close();

}

  3、编译运行程序。

API技巧集 (二)


  (三)获得、设置鼠标双击的间隔时间

  在指定间隔的时间内,连续两次鼠标单击操作称为双击,双击间隔的时间可以在控制面板中的鼠标属性里面改变。若要在自编的应用程序中能获得或设置鼠标双击的间隔时间,我们只需使用Windows的两个API函数GetDoubleClickTime和SetDoubleClickTime。调用GetDoubleClickTime可以返回鼠标双击的间隔时间,而使用SetDoubleClickTime则可以设置鼠标双击间隔的时间。

  下面让我们来做一个获得和设置鼠标双击间隔时间的简单的程序:

  首先,在Borland C++ Builder 5.0 中新建一个工程,往窗体Form1中添加两个Button组件,把它们的Caption属性分别改为“获取双击间隔时间”和“设置双击间隔时间”,再添加一个Edit组件,将Edit1的Text属性改为“200”,添加一个Label组件,把Caption属性改为“毫秒”。

  然后,双击按钮Button1,在它的OnClick(单击)事件中加入下面的代码:

//返回鼠标双击间隔时间

ShowMessage("鼠标双击间隔时间为"+IntToStr(GetDoubleClickTime())+"毫秒");

  再双击按钮Button2,也在它的OnClick事件中加入代码:

//设置鼠标双击间隔时间

SetDoubleClickTime(StrToInt(Edit1-$#@62;Text));

  最后,按F9编译运行一下程序。点击窗口中的“获取双击间隔时间”按钮就会弹出一个显示当前系统鼠标双击间隔的时间,若要设置鼠标双击间隔的时间,只要改变文本框中的数值,比如300吧,再点击“设置双击间隔时间”按钮就可以了。需要注意的是,鼠标双击间隔时间的单位是毫秒,设置的值越小,间隔的时间就越小,双击的速度就越快,系统默认的是400毫秒,可不要设得太小了,否则“我的电脑”会打不开的(你双击的速度不够快,^_^)。





 API技巧集 (二)



  (四)启动控制面板控制台应用程序

  在控制面板里有许多的控制面板项目,这些项目就是控制台应用程序,它们都是标准的DLL(动态链接库)文件,我们经常需要通过它们来对Windows进行配置。rundll32.exe就是专门用来调用DLL文件的程序,在C++ Builder编程中,我们可以通过使用API函数WinExec运行外部程序rundll32.exe调用DLL来实现启动控制面板的控制台应用程序。下面是收集的一些调用DLL启动控制台应用程序的例子:

  1、打开控制面板  

WinExec("rundll32.exe shell32.dll,Control_RunDLL",SW_SHOWNORMAL);

  2、打开方式对话框

WinExec("rundll32.exe shell32.dll,OpenAs_RunDLL "c:\\autoexec.bat"",SW_SHOWNORMAL);

  3、添加Modem

WinExec("rundll32.exe shell32.dll,Control_RunDLL modem.cpl,,add",SW_SHOWNORMAL);

  4、添加打印机

WinExec("rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL AddPrinter",SW_SHOWNORMAL);

  5、复制磁盘

WinExec("rundll32.exe diskcopy.dll,DiskCopyRunDll",SW_SHOWNORMAL);

  6、鼠标

WinExec("rundll32.exe shell32.dll,Control_RunDLL main.cpl",SW_SHOWNORMAL);

  7、网络

WinExec("rundll32.exe shell32.dll,Control_RunDLL netcpl.cpl",SW_SHOWNORMAL);

  8、密码

WinExec("rundll32.exe shell32.dll,Control_RunDLL password.cpl",SW_SHOWNORMAL);

  9、游戏控制器

WinExec("rundll32.exe shell32.dll,Control_RunDLL joy.cpl",SW_SHOWNORMAL);

  10、日期/时间

WinExec("rundll32.exe shell32.dll,Control_RunDLL timedate.cpl",SW_SHOWNORMAL);

  11、Internet 属性
WinExec("rundll32.exe shell32.dll,Control_RunDLL inetcpl.cpl",SW_SHOWNORMAL);

  12、添加/删除程序

//安装/卸载

WinExec("rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,1",SW_SHOWNORMAL);

//Windows 安装

WinExec("rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,2",SW_SHOWNORMAL);

//启动盘

WinExec("rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,3",SW_SHOWNORMAL);

  13、区域设置

//区域设置

WinExec("rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,0",SW_SHOWNORMAL);

//数字

WinExec("rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,1",SW_SHOWNORMAL);

//货币

WinExec("rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,2",SW_SHOWNORMAL);

//时间

WinExec("rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,3",SW_SHOWNORMAL);

//日期

WinExec("rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,4",SW_SHOWNORMAL);

  14、辅助选项

//键盘

WinExec("rundll32.exe shell32.dll,Control_RunDLL access.cpl,,1",SW_SHOWNORMAL);

//声音

WinExec("rundll32.exe shell32.dll,Control_RunDLL access.cpl,,2",SW_SHOWNORMAL);

//显示

WinExec("rundll32.exe shell32.dll,Control_RunDLL access.cpl,,3",SW_SHOWNORMAL);

//鼠标

WinExec("rundll32.exe shell32.dll,Control_RunDLL access.cpl,,4",SW_SHOWNORMAL);

//常规

WinExec("rundll32.exe shell32.dll,Control_RunDLL access.cpl,,5",SW_SHOWNORMAL);

  15、多媒体

//音频

WinExec("rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,0",SW_SHOWNORMAL);

//视频

WinExec("rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,1",SW_SHOWNORMAL);

//MIDI

WinExec("rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,2",SW_SHOWNORMAL);

//CD 音乐

WinExec("rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,3",SW_SHOWNORMAL);

//设备

WinExec("rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,4",SW_SHOWNORMAL);

  16、系统

//常规

WinExec("rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,0",SW_SHOWNORMAL);

//设备管理器

WinExec("rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,1",SW_SHOWNORMAL);

//硬件配置文件

WinExec("rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,2",SW_SHOWNORMAL);

//性能

WinExec("rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,3",SW_SHOWNORMAL);

  17、显示器

//背景

WinExec("rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,0",SW_SHOWNORMAL);

//屏幕保护

WinExec("rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,1",SW_SHOWNORMAL);

//外观

WinExec("rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,2",SW_SHOWNORMAL);

//设置

WinExec("rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,3",SW_SHOWNORMAL);  

  这些例子在Windows 98和Windows 2000中都可以使用通过,前提是在控制面板中安装了该项目。


API技巧集(三)

  (一)闪烁程序的标题栏

  在某些专业的应用程序中,当程序需要提醒用户或要引起用户的注意时,就不停地闪烁程序的标题栏。要实现这个功能,只需要一个Timer组件和使用一个API函数--FlashWindow。

  使用API函数FlashWindow可以闪烁显示指定窗口,让窗口在活动与非活动的状态之间切换,它有两个参数:hwnd和bInvert,头文件为“winuser.h”。其中,参数hwnd为要闪烁的窗口句柄,参数bInvert是一个bool变量,设为true时,程序窗口标题栏从活动切换到非活动状态、或反向切换,当设为false时,窗口标题栏还原为最初的状态。如果配合一个时间组件(Timer组件),以一定的时间间隔执行语句:

FlashWindow(Form1-$#@62;Handle,true);

程序窗口的标题栏就在活动、非活动的状态之间不停地切换。若把hwnd指定成为应用程序的句柄(Application-$#@62;Handel),将会闪烁程序在任务栏上的标题栏。

  下面就让我们来做一个闪烁窗口标题栏和任务栏上标题栏的程序。

  首先,在Form1中添加三个按钮Button1、Button2和Button3,把它们的属性分别为“闪烁窗口标题栏”、“闪烁任务标题栏”和“停止闪烁”,再加入两个时间组件Timer1和Timer2,将两个Timer组件的Enabled属性都设为false,将Interval属性都设为为500(即半秒),改变这个属性的值可以修改闪烁的频率。

  然后,双击Timer1,在OnTimer事件中加入:

FlashWindow(Form1-$#@62;Handle,true);

  双击Timer2,在OnTimer事件中加入:

FlashWindow(Application-$#@62;Handel,true);

  双击Button1,在Button1的OnClick事件中加入:

Timer1-$#@62;Enabled=true;

  双击Button2,在Button2的OnClick事件中加入:

Timer2-$#@62;Enabled=true;

  最后,双击Button3,在Button3的OnClick事件中加入:

Timer1-$#@62;Enabled=false;

Timer2-$#@62;Enabled=false;

FlashWindow(Form1-$#@62;Handle,false);

FlashWindow(Application-$#@62;Handel,false);

  这样,一个简单的例子就完成了。按F9编译运行程序,你就可闪烁窗口标题栏或是闪烁任务栏上? 题栏了。


  (二)拖动无标题窗体

  现在的Windows应用程序,大都使用了图形化的界面、不规则窗口技术,使得程序界面更加漂亮了。但是,使用界面一般要先把窗体的标题栏去掉(在BCB中,将窗体的BorderStyle属性设为bsNone,就可以把窗体的标题栏去掉),这样就不能使用原来的标题栏了,出现了窗口不能移动的问题。没有标题栏怎样用鼠标拖动窗体呢?我们可以使用Windows的API函数SendMessage来解决这个问题。

  首先,新建一个工程,把窗体的BorderStyle属性设为bsNone去掉窗体的标题栏,按F12键切换到代码编辑窗口,在头部加入包含头文件"winuser.h"的代码:

#include $#@60;winuser.h$#@62;

  然后,在窗体的 OnMouseDown 事件中加入下面的代码:

if(Button == mbLeft)//判断是否按了鼠标左键

{

ReleaseCapture();//释放鼠标操作

SendMessage( Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);

}

  这样,用鼠标左键点住窗口拖动,就可以实现拖动没有标题的窗口了。也可以在窗体上添加组件,然后在该组件的 OnMouseDown 事件中加入上面的代码,这样也可以点住这个组件拖动窗口。你还可以把SendMessage函数的第一个参数修改为这个组件的句柄,如:往窗体添加一个Button组件,在它的 OnMouseDown 事件中加入上面的代码,其中把SendMessage那行语句改为:

SendMessage( Button1-$#@62;Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);

这样就可以在程序运行时,用鼠标在窗口的范围内移动Button1了。

  (三)隐藏程序在任务栏的图标

  使用API函数ShowWindow可以隐藏一个程序在任务栏的图标,它被包含在头文件“winuser.h”里面。

  1、隐藏任务栏图标的代码就是:

ShowWindow(Application-$#@62;Handle, SW_HIDE);

  2、要重新显示的时候就使用:

ShowWindow(Application-$#@62;Handle, SW_SHOW);

  但是,如果将程序最小化后,在任务栏的图标就会重新出现。若要在程序还原最小化后,程序在任务栏的图标重新被隐藏起来,可以在窗体的OnPaint事件中加入隐藏程序在任务栏的图标的代码,这样,程序只有在最小化时任务栏才会出现图标,当程序还原最小化时图标又会重新被隐藏起来。


API技巧集(三)




  (四)重启、关闭Windows

  当用户修改了Windows里面的一些设置,Windows经常会提问是否要重新启动计算机,当用户点Yes的时候,计算机将会自动重启。这个就是API函数ExitWindowsEx的一个典型的应用。

  ExitWindowsEx,顾名思义就是退出Windows的函数,它有两个参数,第一个是退出Windows的选项,常用的有:EWX_REBOOT(重新启动计算机),EWX_SHUTDOWN(关闭计算机),EWX_LOGOFF(注销当前用户),第二个参数系统保留没有使用,可设为0。

  在自编的程序中(如:注册表修改程序),当用户修改了某项设置需要重新启动计算机的时候,就要使用EWX_REBOOT选项重启计算机。如:

ExitWindowsEx(EWX_REBOOT,0);

  使用WX_SHUTDOWN选项,可以实现关机。如:

ExitWindowsEx(EWX_SHUTDOWN,0);

  当需要注销的时候,就使用EWX_LOGOFF选项。如:

ExitWindowsEx(EWX_LOGOFF,0);


api技巧集(四)



函数名:

  SetWindowPos

头文件:

  winuser.h

函数原型:

  BOOL SetWindowPos
  (
  HWND hWnd, //窗口句柄
  HWND hWndInsertAfter, //排列顺序的句柄
  int X, //水平坐标
  int Y, //垂直坐标
  int cx, //宽
  int cy, //高
  UINT uFlags //窗口定位标识
  );

说明:

  这个函数能改变窗口的大小、位置和设置子窗口、弹出窗口或顶层窗口的排列顺序。
  返回值:

  BOOL,如果返回值非零表示成功,返回零表示失败。错误信息请参看GetLastError函数。

参数表:

  参数 类型及说明
  hwnd HWND,欲定位的窗口句柄
  hWndInsertAfter HWND,置于hwnd前面的窗口句柄。这个参数必须是窗口的句柄或是下面的值之一:   HWND_BOTTOM 将窗口置于其它所有窗口的底部
  HWND_NOTOPMOST 将窗口置于其它所有窗口的顶部,并位于任何最顶部窗口的后面。如果这个窗口非顶部窗口,这个标记对该窗口并不产生影响
  HWND_TOP 将窗口置于它所有窗口的顶部
  HWND_TOPMOST 将窗口置于其它所有窗口的顶部,并位于任何最顶部窗口的前面。即使这个窗口不是活动窗口,也维持最顶部状态

x: 
 
  int,指定窗口新的X坐标

Y:  

  int,指定窗口新的Y坐标

cx:  

  int,指定窗口新的宽度

cy:  

  int,指定窗口新的高度

wFlags:

  UINT,指定窗口状态和位置的标记。这个参数使用下面值的组合: SWP_DRAWFRAME 围绕窗口画一个框
  SWP_FRAMECHANGED 发送一条WM_NCCALCSIZE消息进入窗口,即使窗口的大小没有发生改变。如果不指定这个参数,消息WM_NCCALCSIZE只有在窗口大小发生改变时才发送
  SWP_HIDEWINDOW 隐藏窗口
  SWP_NOACTIVATE 不激活窗口
  SWP_NOCOPYBITS 屏蔽客户区域
  SWP_NOMOVE 保持当前位置(X和Y参数将被忽略)
  SWP_NOOWNERZORDER 不改变所有窗口的位置和排列顺序
  SWP_NOREDRAW 窗口不自动重画
  SWP_NOREPOSITION 与SWP_NOOWNERZORDER标记相同 r>   SWP_NOSENDCHANGING 防止这个窗口接受WM_WINDOWPOSCHANGING消息
  SWP_NOSIZE 保持当前大小(cx和cy会被忽略)
  SWP_NOZORDER 保持窗口在列表的当前位置(hWndInsertAfter将被忽略)
  SWP_SHOWWINDOW 显示窗口


备注:

  如果设置了SWP_SHOWWINDOW或SWP_HIDEWINDOW标记,这个窗口不发生移动或改变大小。窗口成为最顶级窗口后,它的所有子窗口也会进入最顶级。一旦将其设为非最顶级,则它的所有子窗口也会转为非最顶级。

相关函数:

  MoveWindow,SetActiveWindow,SetForegroundWindow

例子:

  //设置顶层窗口
  SetWindowPos( Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE||SWP_NOSIZE);

  //取消顶层窗口
  SetWindowPos( Handle, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE||SWP_NOSIZE);



api技巧集(四)




超级链接效果

  在很多共享软件的关于对话框里有一些模仿网页的超级链接,如主页URL或E-Mail之类的,当鼠标移到它上面的时候,文字变成红色的,当鼠标离开时,文字又变回原来的蓝色,如果用鼠标点击这个链接则会弹出浏览器窗口打开指定的URL或是运行默认的E-Mail程序撰写新邮件,就和真的超链接一样。你是不是也想在你的程序里做一个呢?其实,我们只要调用API函数ShellExecute和在鼠标移动时改变一下文字的颜色,就可以在自己的程序中出现这种效果。

  首先新建一个工程,在窗体Form1上添加两个Label组件,它们的Name属性使用默认的Label1和Label2。

  然后在Form1的OnCreate事件中加入代码:

Label1-$#@62;Cursor=crHandPoint;

Label2-$#@62;Cursor=crHandPoint;

Label1-$#@62;Font-$#@62;Color =clBlue;

Label2-$#@62;Font-$#@62;Color =clBlue;

Label1-$#@62;Caption="主页:初学者之家网站";

Label2-$#@62;Caption="E-Mail:fdlweb@sina.com";

  再在Label1的OnClick(单击)事件中加入:

//蓝色的字请改成自己的主页地址

ShellExecute(Handle,NULL,"http://fdlweb.myrice.com/",NULL,NULL,SW_SHOWNORMAL);

  在OnMouseMove事件中加入:

Label1-$#@62;Font-$#@62;Color=clRed;

  在Label2的OnClick事件中加入:

//蓝色的字请改成自己邮箱地址

ShellExecute(Handle,NULL,"mailto:fdlweb@sina.com",NULL,NULL,SW_SHOWNORMAL);

  在OnMouseMove事件中加入:

Label2-$#@62;Font-$#@62;Color=clRed;

  最后在Form1的OnMouseMove事件中加入:

Label1-$#@62;Font-$#@62;Color=clBlue;

Label2-$#@62;Font-$#@62;Color=clBlue;

  代码输入完了,按F9编译运行程序就看到效果了。



拷贝屏幕

  BitBlt函数可以将一幅位图从一个设备场景拷贝到另一个设备场景,这个函数经常用在抓图程序和游戏编程方面,也可以用来做基于桌面的屏幕保护程序。下面让我们用BitBlt函数来做一个虚假桌面的程序:

  首先,添加一个Image组件到窗体中,将窗体Form1的BorderStyle属性设为:bsNone。

  接着在窗体的OnCreate事件加入程序代码:

Left=0;

Top=0;

Width=Screen-$#@62;Width;

Height=Screen-$#@62;Height;

Image1-$#@62;Left=0;

Image1-$#@62;Top=0;

Image1-$#@62;Width=Screen-$#@62;Width;

Image1-$#@62;Height=Screen-$#@62;Height;

//这句代码就是将桌面拷贝到组件Image1中来存放,

// 其中GetDC(0)返回桌面设备的句柄(HDC)

BitBlt(Image1-$#@62;Canvas-$#@62;Handle,0,0,Screen-$#@62;Width,Screen-$#@62;Height,GetDC(0),0,0,SRCCOPY);

  按F9运行,一个假的桌面就出来了,在这个“桌面”上怎么按鼠标都没有反应,可以用来捉弄人喔!。有些桌面的小游戏也是这么干的,你可以在这个程序的基础上加上更多的功能,如在窗体上加上Label组件和Timer组件,用Timer组件来控制Label组件在窗体上移动,再在窗体Form1的OnKeyDown事件和Image1的OnMouseDown事件中加入关闭窗口的代码“Close();”,最后将编译了的程序的扩展名改为scr,这就成了一个文字在桌面上乱动的屏幕保护程序了。






取得磁盘总空间和剩余空间

  要取得磁盘总空间和剩余空间,最简单直接的方法是调用API函数 GetDiskFreeSpace。

  GetDiskFreeSpace函数有5个参数,第一个参数是要判断可用空间的驱动器名,第二个参数是一个存放每簇扇区数的变量,第三个参数是一个存放每扇区字节数的变量,第四个参数是存放剩余簇数的变量,第五个参数是存放总簇数的变量。套用相应计算磁盘空间的公式即可得出指定驱动器的总空间或剩余空间。

磁盘总空间和剩余空间的计算公式分别为:

  磁盘上剩余空间(字节) = 簇的扇区数 * 扇区的字节数 * 剩余簇数

  磁盘上总空间(字节) = 簇的扇区数 * 扇区的字节数 * 总簇数

下面就是取得C盘的总空间和剩余空间的例子:

unsigned long Sectors,Bytes,Free,Total;

GetDiskFreeSpace("C:\\",&Sectors,&Bytes,&Free,&Total);

//可用空间(单位:MB)

int FreeKB = Bytes * Sectors * Free / 1024;

//总空间(单位:MB)

int TotalKB = Bytes * Sectors * Total / 1024;

ShowMessage("C盘的可用空间有:" + IntToStr(FreeKB) + "MB,总空间有:" + IntToStr(TotalKB) +"MB");



api技巧集(五)

提取图标


  调用API函数ExtractIcon可以提取出在程序文件中的图标,它的头文件是shellapi.h,原型为:

HICON ExtractIcon
(
HINSTANCE hInst, //实例句柄
LPCTSTR lpszExeFileName, //要提取图标的那个程序的文件名
UINT nIconIndex //要提取的图标的索引
);

  调用该函数时,参数hInst一般设为当前应用程序的实例句柄,如:Form1-$#@62;Handle。

  参数lpszExeFileName为需要提取图标的程序文件的完整路径,这个程序文件可以是EXE文件、DLL文件、ICO文件等,只要是包含有图标资源的文件一般都可以提取图标。

  当参数nIconIndex指定一个图标的索引可以返回指向图标的句柄,如指定的文件中不存在图标,则返回零,当参数nIconIndex设为-1,函数返回文件的图标总数。

  函数返回的句柄可以赋给一个用TIcon类声明的变量,再使用该变量的SaveToFile方法就可以把图标保存出来。

例子:

TIcon *Icon = new TIcon();
AnsiString FileName = "C:\\WINDOWS\\SYSTEM\\SHELL32.DLL";
int TotalIcon;
//得到文件SHELL32.DLL的总图标数
TotalIcon = (int)ExtractIcon(Form1->Handle,FileName.c_str(), -1);
//提取第一个图标,0为第一个,1为第二个,类推...
Icon->Handle = ExtractIcon( Form1->Handle, FileName.c_str(), 0);
//保存图标
Icon->SaveToFile("C:\\1.ICO");
  下面给出一个完整的图标提取程序源码。

  这个程序需要四个按钮控件(Button)、四个文本标签控件(Label)、两个文本框控件(Edit)、一个水平滚动条控件(ScrollBar)、一个打开文件对话框控件(OpenDialog)、一个保存文件对话框控件(SaveDialog)和一个图片控件(Image),还有一个Panel控件是装饰用的。界面如图所示:

             

  把各个控件排列好,再把四个Label控件的Caption属性修改一个,最后输入程序代码,运行程序,一个提取图标的程序就出来了,你以后也就不会为没有图标资源可用而发愁了。

程序清单(Unit1.cpp):
//--------------------------------------- ----------------------
#include $#@60;vcl.h$#@62;
#pragma hdrstop
#include "Unit1.h"
//----------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
AnsiString FileName;
TIcon *Icon = new TIcon();
int TotalIcon;
//----------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}

//----------------------------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)
{

Caption="图标小偷 1.0";
Button1-$#@62;Caption="选择文件";
Button2-$#@62;Caption="保存图标";
Button3-$#@62;Caption="保存所有";
Button4-$#@62;Caption="退出";
Edit1-$#@62;Text=0;
Edit2-$#@62;Text=0;
Image1-$#@62;Width=32;
Image1-$#@62;Height=32;
OpenDialog1-$#@62;Filter="可执行文件(*.exe,*.dll)|*.exe;*.dll|图标文件(*.ico)|*.ico|所有文件(*.*)|*.*";
SaveDialog1-$#@62;Filter="图标文件|*.ico";
ScrollBar1-$#@62;Enabled=false;
Button2-$#@62;Enabled=false;
Button3-$#@62;Enabled=false;

}
//----------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{

if(OpenDialog1-$#@62;Execute())
{

TotalIcon = (int)ExtractIcon( Form1-$#@62;Handle, OpenDialog1-$#@62;FileName.c_str(), -1 );
if(TotalIcon$#@62;0)
{

if(TotalIcon$#@60;2)

ScrollBar1-$#@62;Enabled=false;

else

ScrollBar1-$#@62;Max=TotalIcon-1;

Button2-$#@62;Enabled=true;
Button3-$#@62;Enabled=true;
FileName = OpenDialog1-$#@62;FileName;
Edit1-$#@62;Text =TotalIcon;
Icon-$#@62;Handle = ExtractIcon( Form1-$#@62;Handle, FileName.c_str(), 0);
Image1-$#@62;Picture-$#@62;Icon=Icon;
Edit2-$#@62;Text=1;

}
else
{

ShowMessage("该文件没有图标");

}

}

}
//----------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{

if(SaveDialog1-$#@62;Execute())
{

//保存图标
Icon-$#@62;SaveToFile( SaveDialog1-$#@62;FileName);

}

}
//----------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{

if(SaveDialog1-$#@62;Execute())
//提取所有的图标
for(int i=0;i$#@60;TotalIcon-1;i++)
{

Icon-$#@62;Handle = ExtractIcon( Form1-$#@62;Handle, FileName.c_str(), i);
Icon-$#@62;SaveToFile(SaveDialog1-$#@62;FileName+(AnsiString)i+".ico");

}

}
//----------------------------------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{

Close();

}
//----------------------------------------------------------------
void __fastcall TForm1::ScrollBar1Change(TObject *Sender)
{

Edit2-$#@62;Text=ScrollBar1-$#@62;Position+1;
Icon-$#@62;Handle = ExtractIcon(Form1-$#@62;Handle, FileName.c_str(),ScrollBar1-$#@62;Position);
Image1-$#@62;Picture-$#@62;Icon=Icon;

}
//----------------------------------------------------------------

判断驱动器的类型


  使用API函数GetDriveType能判断一个驱动器的类型,该函数返回一个int型的值,当返回值为2时,是软盘;为3时,是硬盘;为4时,是网络映射盘;为5时,是光驱;为6时,是 RAM 磁盘;为其它值时,是非法的盘符。这个API函数包含在winbase.h头文件中,首先在程序头部加上语句:

include $#@60;winbase.h$#@62;

包含头文件,然后在程序中加入以下代码就可以判断驱动器的类型:

int drv;
//这里的"C:\\"为要判断的盘符
drv=GetDriveType("C:\\");
switch (drv) //判断drv的值
{
case 2 : //DRIVE_REMOVABLE

ShowMessage("软盘");
break;

case 3 : //DRIVE_FIXED

ShowMessage("硬盘");
break;

case 4 : //DRIVE_REMOTE

ShowMessage("网络映射盘");
break;

case 5 : //DRIVE_CDROM

ShowMessage("光驱");
break;

case 6 : //DRIVE_RAMDISK

ShowMessage("RAM 磁盘");
break;

default :

ShowMessage("这个磁盘不存在!");
break;

}

注:case语句后的数值也可以用注释后的常数替换。如2可用常数 DRIVE_REMOVABLE 来替换。


api技巧集(七)


窗口最小化、最大化和恢复

  通过调用API函数ShowWindow可以控制指定窗口的状态,如将窗口最小化、最大化或者是恢复原来的状态,等等。

  虽然通过窗口标题栏上的控制按钮也可以将窗口最小化、最大化或者是恢复,但ShowWindow函数能实现更多的功能,又如隐藏窗口、将窗口最小化到桌面等,这些是标准的控制按钮所做不到的。

  下面介绍一个ShowWindow函数的例子。这个例子演示了如何将一个窗口最小化到桌面或最小化到任务栏和最大化、恢复窗口原始状态。

  首先,在C++ Builder中新建一个工程,为了方便演示,工程需要两个窗口。添加第二个窗口Form2的方法是:选择“File”菜单下的“New Form”。添加了新窗口后,选择“File”菜单下的“Include Unit Hdr...”包含窗口Form2的头文件“Unit2.h”,或是直接在Form1的代码编辑窗口的头部加上“#include "Unit2.h"”语句。然后在Form1上放上四个按钮,它们的Caption属性分别为“最大化”、“最小化到桌面”、“恢复”和“最小化到任务栏并恢复”。

  接着,双击窗件Form1,在它的OnCreate事件中加入:

void __fastcall TForm1::FormCreate(TObject *Sender)

{

  //设置Form1为顶层窗口

Form1-$#@62;FormStyle=fsStayOnTop;

}

  双击按钮Button1,在它的OnClick事件中加入:

void __fastcall TForm1::Button1Click(TObject *Sender)

{

  //最大化

ShowWindow(Form2-$#@62;Handle, SW_MAXIMIZE);

}

双击按钮Button2,在它的OnClick事件中加入:


void __fastcall TForm1::Button2Click(TObject *Sender)

{ //最小化到桌面

ShowWindow(Form2-$#@62;Handle, SW_MINIMIZE);

}

双击按钮Button3,在它的OnClick事件中加入:

void __fastcall TForm1::Button3Click(TObject *Sender)

{

  //恢复最小化

ShowWindow(Form2-$#@62;Handle, SW_RESTORE);

}

{{??最??苤赵??蛹? 酸???蔑棠奴 ??锸◆ ?硌斯譬 ?拨霾蝌?棠 utton4腔OnClick岈璃?〔????别载陇?ㄛ?洁楼??跺??腔测钨ㄩ

void __fastcall TForm1::Button4Click(TObject *Sender)

{

  //最小化到任务栏

ShowWindow(Application-$#@62;Handle, SW_MINIMIZE);

  //延时1秒

Sleep(1000);

  //恢复最小化

ShowWindow(Application-$#@62;Handle, SW_RESTORE);

}

  最后,编译运行程序。

  获取磁盘序列号、卷标和文件系统类型

  使用API函数GetVolumeInformation,可以获取一个磁盘的有关信息,如磁盘的序列号、卷标、文件系统类型。有些软件就是利用磁盘的序列号来加密的。

  需要获取磁盘信息的时候,加入下面的代码就可以了:

  //定义长度为255的卷标字符串变量缓冲区:

AnsiString VolumeName=AnsiString::StringOfChar(" ", 255);

  //序列号

unsigned long SerialNumber;

  //定义长度为20的文件系统类型字符串缓冲区

AnsiString SystemName = AnsiString::StringOfChar(" ",20);

  //获取磁盘信息

GetVolumeInformation("C:\\", VolumeName.c_str(), 255, &SerialNumber, 0, 0, SystemName.c_str(), 20);

ShowMessage("C盘的卷标:" + Trim(VolumeName));

ShowMessage("C盘的序列号:" + IntToStr(SerialNumber));

ShowMessage("C盘的文件系统类型:" + Trim(SystemName));
  屏幕放大镜

  你一定用过Windows98自带的那个屏幕放大镜吧,你想不想自已做一个呢?其它,这个程序的关键是使用了API函数StretchBlt。

  调用API函数StretchBlt可以把一个设备中指定大小的位图从拷贝到另一个设备,在拷贝的过程中,还可以根据需要来缩放位图。

  下面是它的原型和参数说明:

BOOL StretchBlt

(

HDC hdcDest, //目标设备句柄

int nXOriginDest, //目标矩形左上角的X坐标

int nYOriginDest, //目标矩形左上角的Y坐标

int nWidthDest, //目标矩形的宽度

int nHeightDest, //目标矩形的高度

HDC hdcSrc, //源设备句柄

int nXOriginSrc, //源矩形左上角的X坐标

int nYOriginSrc, //源矩形左上角的Y坐标

int nWidthSrc, //源矩形的宽度

int nHeightSrc, //源矩形的高度

DWORD dwRop //光栅运算操作

);

  StretchBlt函数的头文件为“wingdi.h”。其中,它的dwRop参数有15种操作,最常用的就是拷贝运算SRCCOPY了。当源设备和目标设备指定的矩形大小不相等时,函数会根据源矩形和目标矩形的大小比例对位图进行放大或缩小操作后,拷贝到目标设备中。

  下面就是一个把屏幕上左上角坐标为0x0、宽和高都为100的矩形位图缩小2倍后拷贝到图片控件Image1中的例子:

StretchBlt(Image1-$#@62;Canvas-$#@62;Handle, 0, 0, 50, 50, GetDC(0), 0, 0, 100, 100, SRCCOPY);

  这句代码的GetDC(0)语句为取得桌面设备的句柄。

  如果不断地使用上面那句代码,把屏幕缩小放到Image1中,这就成一个“屏幕缩小镜”了。当然,缩小屏幕并没有什么实际的用处,我们只要把它改一下就可以做成“屏幕放大镜”了。

  要做这个“屏幕放大镜”,首先要运行Borland C Builder,在窗体Form1上放上一个图片控件Image1和一个时间控件Timer1。

双击窗体Form1,在它的OnCreate事件中加入代码:

void __fastcall TForm1::FormCreate(TObject *Sender)

{

Image1-$#@62;Width=200;

Image1-$#@62;Height=200;

Timer1-$#@62;Interval=10;
  //设置顶层窗口

SetWindowPos( Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE||SWP_NOSIZE);

}

  双击时间控件Timer1,在它的OnTimer事件中加入以下代码:

void __fastcall TForm1::Timer1Timer(TObject *Sender)

{

int x,y;

POINT CurPos;

  //取得鼠标当前坐标

GetCursorPos(&CurPos);

  //保证拷贝的图象不在屏幕外

if(CurPos.x $#@62;Screen-$#@62;Width - 100)

x=Screen-$#@62;Width - 100;

else if(CurPos.x $#@60;50)

x=0;

else

x=CurPos.x-50;

if(CurPos.y $#@62;Screen-$#@62;Height-100)

y=Screen-$#@62;Height-100;

else if(CurPos.y $#@60;50)

y=0;

else

y=CurPos.y-50;

Caption="坐标:" + IntToStr(CurPos.x) + "," + IntToStr(CurPos.y);

Image1-$#@62;Canvas-$#@62;FillRect(Rect(0,0,200,200));
  //拷贝放大图象

StretchBlt(Image1-$#@62;Canvas-$#@62;Handle, 0, 0, 200, 200, GetDC(0), x, y, 100, 100, SRCCOPY);

}

  代码输入完了,按F9或点击运行按钮运行程序。怎么样?和Windows自带的那个差不多吧!

  需要注意的是:C++ Builder并象VB那样图片控件可以是非持久性的,在C++ Builder中,并不能直接不断地调用StretchBlt函数来得到连贯的图象,而是要先使用图片控件的FillRect方法填充图片框(相当于清空图片框),再调用StretchBlt函数才行。

  时间的延迟

  延迟在程序设计中非常有意义!比如,程序启动时的等待画面,又或者你需要等待一个过程的完成才能运行程序下面的代码,这时就要使用到时间的延迟了。

  但是在很多编程语言中一般都没有现成的延迟函数。在Dos的C时代,当程序需要延迟时,有不少人使用的是for循环:

for(int i=0;i$#@60;10000;i++);

  到现在也可能还有人在用吧。到了Win32时代,在系统的API函数库里已经提供有时间延迟的函数了,它就是Sleep函数。当然,在Windows编程中,你也可以使用Timer控件,但使用Sleep函数更方便、快捷。我们只需简单的调用一下Sleep函数,就可以实现时间的延迟。

  Sleep函数只有一个参数cMilliseconds,用来指定需要延迟的时间,它的单位是毫秒。让我们看下面的例子,请在一个按钮的单击(OnClick)事件中加入下面的代码:

Sleep(3000);//延迟三秒

ShowMessage("本对话框已经延迟了三秒!");

  这个例子演示了一个延时的对话框,单击了该按钮,本应立即弹出的对话框延迟了三秒钟的时间才弹出。

BCB6常见问题

1.先安装一些控件后,以后卸载,但编译后来的项目总提示找不到。。。,需要给一些lib,bpi指定路径?

解决方法:close掉工程,使用ultraedit打开.bpr文件,把你卸掉的一些*.bpi,*.lib从 ,中手工删除掉,保存。然后再打开编译就好了。

2.从旧版本cb项目中copy过来的文件,dfm格式以文本编辑器打开后是乱码?

cbuidler中打开,切换到设计视图,右键选中“Text DFM”,OK!

3.旧版本cb项目中的copy过来的Form,先执行FormCreate才执行构造函数?

使用ultraedit,执行批量替换: 选定你的代码目录录,对所有..dfm文件,执行

“OldCreateOrder = True” 替换为 “OldCreateOrder = False”

4. 加入到cc中的项目,打开时报错,提示“File Access Denied”或直接close你的cb6?

第一个问题是不应该把.res文件加入到cc,应该把该文件保存为私有; 第二种情况需要你把项目 .bpr,.bpf,.cpp三个文件先checkout,然后再打开,就没问题了!

5.使用TList时,使用delete 清除?

使用TList,一般我们都会 使用new ,添加指针类型到 TList中作为元素,因此在删除时,必须先delete每一个元素,再执行clear,然后在delte TList对象本身,如:

if (pSubExeList)
{
TSubExe* pSubExe=NULL;
for (int i = 0; i < pSubExeList->Count; i++)
{
pSubExe = (TSubExe*)pSubExeList->Items[i];
delete pSubExe;
}
pSubExeList->Clear();
delete pSubExeList;
pSubExeList = NULL;
}

虽然这是一个很简单的问题,但还是可以看到很多写的不正确的代码,:(

6.如果先生成了一个Form,后来需要把这个Form改为从另外一个基类Form继承?

方法是:1.修改类定义的方法,添加从基类继承;
2.修改构造函数,改为执行基类的构造函数;
3.点击Form,右键-》View As Text ,然后修改 第一个object为inherited

7.在cbuilder查找一个函数很麻烦?

建议使用gexperts工具,其中有一个功能 “Procedure List”,把这个菜单项拖到编辑区的右边,非常容易找函数,在代码量很多的情况下很方便。

另外这个工具还有个最常用的功能是:注释块和取消注释。写代码时没有这个东东真是累。

8.在实现自定义控件的时候或者动态构建界面的时候,需要写很多动态创建控件的代码。不知道怎么写或写起来太累?

有一个好方法,也是gexperts工具提供的,“Component to code”,先创建一个临时FOrm,把你想要动态创建的布局设计好,然后选中控件,点击“Component to code”,自动帮你生成了代码,你只需要copy过来,稍加整理就可以了。

9.特别多的头文件,到处都要添加include代码?

可以定义一个专门的.h 文件,把通常需要包含的所有include代码写在里面;以后用的时候就只用添加这一个.h 了。

10.几个常用的API函数:

找窗口:注意第一个是类名;第二个是窗口的Caption,返回窗口句柄

FindWindow(className,FormCaption)

动态启动另外一个可执行程序Exe,或者是打开文档:注意第四个参数,可用来传递程序的启动参数,参数之间以空格隔开,取参数。

ShellExecute (ParentFormHandle,"open",filename,startup arguments,default dir ,Show Mode)

取应用程序启动参数:

ParamCount() :取参数个数;

ParamStr(argument index):取参数,因为ParamStr(0)标识的是执行程序文件的名称,因此实际上第一个参数是ParamStr(1);

发送消息:

PostMessage(Target WIndow Handle,Message Type ID,Message content Ptr,LParam):使用PostMessage时,当第二个参数,即自定义的消息标识
SendMessage(Target WIndow Handle,Message Type ID,Message content Ptr,LParam):用于发送消息,但等待结果返回。



PostMessage在进程内部使用的时候,消息内容含有指针类型,可以被成功接收;如跨进程,则只能使用copydata类型, SendMessage方法进行消发送:

COPYDATASTRUCT data;
data.dwData = MsgFlag; //ת·¢ÏûÏ¢±êʶ
data.cbData = Msg.Length();
//memcpy(data.lpData,cMsg,strlen(cMsg));
data.lpData = Msg.c_str();

SendMessage(pExeWnd,WM_COPYDATA,(WPARAM)Application->Handle,(LPARAM)&data);

11.TImage控件可以使用.gif格式的图片?

刚开始用的时候,发现TImage控件可以支持.gif格式的图片;后来发现又不行了,觉得很奇怪。原来是因为把TeeChart7控件卸载了! 装了TeeChart后,TeeChart自己包含了GifImage.pas,经过编译安装后就可以让Timage在设计时加载Gif格式的图片了(不过还没试能不能显示动画)

12.TLabel控件需要内容纵向居中?

开始一直以为TLabel控件不能纵向居中,后来才发现lauout!很多控件都有layout属性,如果是内容布局的问题,就尝试设置一下layout!

13. 图标资源都是gif或者jpg格式的,不能在TImageList中使用?

这要借助于Acdsee工具,打开Acdsee,选中所有你的gif或是jpg格式的图片,右键“工具”……“转换文件格式”……选中你bmp或者ico,然后就可以加在imagelist中使用了!

或者使用Snagit工具,在snagit中有个“BatchConvertImages”更方便、好用。

14. delete Component的时候出现异常?

有的时候会犯一个简单的错误,如:

TFrame* pFrame;

void __fastcall TMainForm::CreateFrame()
{
pFrame = new TFrame(Application);
}

void __fastcall TMainForm::~TMainForm()
{
if(pFrame)
delete pFrame;
}

在执行析构的时候可能会导致异常。主要是因为构造的时候不应使用Application作为Owner,应该使用MainForm作为其Owner。所以在构造Component的时候一定要注意把哪个对象作为Owner,因为Owner负责对象流的保存及资源释放等。



第二部分

1、BCB 编辑快捷键

左/右移 块代码

选中 块代码
1 CTRL+ SHIFT+ I 是整片往右移,
2 CTRL+ SHIFT+ U 是整片文字往左移

2、得到执行程序的当前路径

ExtractFileDir(Application->ExeName);


3、循环中响应其它操作

在循环内加入Application->ProcessMessage()这一句。

4、向外部提供dll函数标准windows格式

extern "C" __declspec(dllexport) __stdcall __int32 Fun(__int32 n32_i);

5、从外部dll输入函数标准windows格式

extern "C" __declspec(dllimport) __stdcall __int32 Fun(__int32 n32_i);

6、对DLL的调试

RUN/PARAMETERS 中填上调用该DLL的 *.exe。

7、使用*.chm帮助文件

ShellExecute(NULL,NULL,帮助文件的路径,NULL,NULL,SW_SHOWNORMAL);


8、PB_C数据类型转换表

PB_C数据类型转换表 MICROSOFT PB(16Bit) PB(32Bit)
Bool Boolean Boolean
Byte, Char Char Char
Char* Ref string Ref String
Colorref Uint Ulong
Double Double Double
Dword Uint Ulong
Float N/A N/A
Handle Uint Ulong
Hdc Uint Ulong
Hfile Uint Ulong
Hinstance Uint Ulong
Hwnd Uint Ulong
Int Int Int
Long Long Long
Lparam Uint Ulong
Lpbyte Ref Int Ref Long
Lpcwstr Ref Blob Ref Blob (Unicode use ToUnicode())
Lpcvoid Ref String Ref String
Lpdword Ref Uint Ref Ulong
Lpfiletime Ref Time Ref Time
Lpint Ref Int Ref Long
Lpstr,Lpcstr Ref String Ref String
Lpvoid Ref Structstruct_inst Ref Struct struct_inst
Lpword Ref Int Ref Ulong
Mcierror Long Long
Pbyte Ref Int[#] Ref Long[#]
Short Int Int
Structure Ref Struct struct_inst Ref Struct Struct_inst
Uint Uint Uint
Void** SUBROUTINE SUBROUTINE
Word Int Long
Wparam Uint Ulong


9、使用CB内存漏洞工具

选中Progect/Option->CodeGuard

支持环境CG32.LIB/CG32.DLL

10、MFC基本运行库目录
mfc42.dll
MFC42D.DLL
MFCD42D.DLL
MFCN42D.DLL
MFCO42D.DLL
MSVCP60.DLL
MSVCP60D.DLL
MSVCRTD.DLL
NTDLL.DLL


11、数据库连接测试(ADO)

建一文件,*.udl,内容空。
双击,按照提示操作。


12、编译器设置

Project|Options

Compiler(编译)
"Full debug"(完全调试模式)
"Code optimization"(代码优化)

"debugging"(调试)
"Debug information"(调试信息)
"Line number information"(行数信息)
"Disable inline expansions"(禁用内联扩展)

"Pascal"标签

"Optimization"优化
"debugging"(调试)

"Linker"(链接)
"Create debug information"(生成调试信息)
"Don’t generate state files"(不要生成状态文件)
"Use dynamic RTL"(使用动态RTL)

"Directories/Conditionals"(路径/条件)

"Packages"(程序包)
"Build with runtime packages"(带运行时程序包编译)


Tools|Debugger Options
Integrated debugging"(集成调试器)

Project|Build All(彻底的编译)



13、设置RTL

C builder 有几种运行时库,多线程静态链接库,单线程静态链接库 以及动态的,含有

VCL的,下面介绍多线程静态链接库,单线程静态链接库 是没有VCL的,VCL中自动 包含多线程。

Use RTL multi-threaded static library 使用多线程静态链接库

到*.bpr 中,按下面的修改即可。



Use RTL single-threaded static library 使用单线程静态链接库

到*.bpr 中,按下面的修改即可。



14、CODEGUARD调试器

库文件CG32.LIB/CG32.DLL
一、编译 (Project/Option-> CodeGuard)
二、运行(Tools/CodeGuard Configuration)
文件为*.CGI
日志文件中,文件名为 *.CGI。用 View/Debug Window/CodeGuard Log察看或者记事本。