一)不规则窗口
Windows提供的只是标准的矩形窗口,要想建立一个不规则的窗口就需要调用API函数来实现。建立一个不规则的窗口,一般是先用创建区域的API函数建立一个不规则的区域,再用API函数SetWindowRgn改变窗口的区域。这些API函数在C++ Builder中包含在头文件wingdi.h和winuser.h里面,因此,要使用这些API函数就要先在程序头部加上包含头文件的语句:
include <wingdi.h>
include <winuser.h>
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_OR 建立两个区域的并集
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);
(一)闪烁程序的标题栏
在某些专业的应用程序中,当程序需要提醒用户或要引起用户的注意时,就不停地闪烁程序的标题栏。要实现这个功能,只需要一个Timer组件和使用一个API函数--FlashWindow。
使用API函数FlashWindow可以闪烁显示指定窗口,让窗口在活动与非活动的状态之间切换,它有两个参数:hwnd和bInvert,头文件为“winuser.h”。其中,参数hwnd为要闪烁的窗口句柄,参数bInvert是一个bool变量,设为true时,程序窗口标题栏从活动切换到非活动状态、或反向切换,当设为false时,窗口标题栏还原为最初的状态。如果配合一个时间组件(Timer组件),以一定的时间间隔执行语句:
FlashWindow(Form1->Handle,true);
程序窗口的标题栏就在活动、非活动的状态之间不停地切换。若把hwnd指定成为应用程序的句柄(Application->Handel),将会闪烁程序在任务栏上的标题栏。
下面就让我们来做一个闪烁窗口标题栏和任务栏上标题栏的程序。 首先,在Form1中添加三个按钮Button1、Button2和Button3,把它们的属性分别为“闪烁窗口标题栏”、“闪烁任务标题栏”和“停止闪烁”,再加入两个时间组件Timer1和Timer2,将两个Timer组件的Enabled属性都设为false,将Interval属性都设为为500(即半秒),改变这个属性的值可以修改闪烁的频率。
然后,双击Timer1,在OnTimer事件中加入:
FlashWindow(Form1->Handle,true);
双击Timer2,在OnTimer事件中加入:
FlashWindow(Application->Handel,true);
双击Button1,在Button1的OnClick事件中加入:
Timer1->Enabled=true;
双击Button2,在Button2的OnClick事件中加入:
Timer2->Enabled=true;
最后,双击Button3,在Button3的OnClick事件中加入:
Timer1->Enabled=false;
Timer2->Enabled=false;
FlashWindow(Form1->Handle,false);
FlashWindow(Application->Handel,false);
这样,一个简单的例子就完成了。按F9编译运行程序,你就可闪烁窗口标题栏或是闪烁任务栏上的标题栏了。
(二)拖动无标题窗体
现在的Windows应用程序,大都使用了图形化的界面、不规则窗口技术,使得程序界面更加漂亮了。但是,使用界面一般要先把窗体的标题栏去掉(在BCB中,将窗体的BorderStyle属性设为bsNone,就可以把窗体的标题栏去掉),这样就不能使用原来的标题栏了,出现了窗口不能移动的问题。没有标题栏怎样用鼠标拖动窗体呢?
我们可以使用Windows的API函数SendMessage来解决这个问题。
首先,新建一个工程,把窗体的BorderStyle属性设为bsNone去掉窗体的标题栏,按F12键切换到代码编辑窗口,在头部加入包含头文件"winuser.h"的代码:
#include <winuser.h>
然后,在窗体的 OnMouseDown 事件中加入下面的代码:
if(Button == mbLeft)//判断是否按了鼠标左键
{
ReleaseCapture();//释放鼠标操作
SendMessage( Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
}
这样,用鼠标左键点住窗口拖动,就可以实现拖动没有标题的窗口了。也可以在窗体上添加组件,然后在该组件的 OnMouseDown 事件中加入上面的代码,这样也可以点住这个组件拖动窗口。你还可以把SendMessage函数的第一个参数修改为这个组件的句柄,如:往窗体添加一个Button组件,在它的 OnMouseDown 事件中加入上面的代码,其中把SendMessage那行语句改为:
SendMessage( Button1->Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
这样就可以在程序运行时,用鼠标在窗口的范围内移动Button1了。
(三)隐藏程序在任务栏的图标
使用API函数ShowWindow可以隐藏一个程序在任务栏的图标,它被包含在头文件“winuser.h”里面。
1、隐藏任务栏图标的代码就是:
ShowWindow(Application->Handle, SW_HIDE);
2、要重新显示的时候就使用:
ShowWindow(Application->Handle, SW_SHOW);
但是,如果将程序最小化后,在任务栏的图标就会重新出现。若要在程序还原最小化后,程序在任务栏的图标重新被隐藏起来,可以在窗体的OnPaint事件中加入隐藏程序在任务栏的图标的代码,这样,程序只有在最小化时任务栏才会出现图标,当程序还原最小化时图标又会重新被隐藏起来。
(四)重启、关闭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);
----------------------------
函数名:
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标记相同
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);
超级链接效果
在很多共享软件的关于对话框里有一些模仿网页的超级链接,如主页URL或E-Mail之类的,当鼠标移到它上面的时候,文字变成红色的,当鼠标离开时,文字又变回原来的蓝色,如果用鼠标点击这个链接则会弹出浏览器窗口打开指定的URL或是运行默认的E-Mail程序撰写新邮件,就和真的超链接一样。你是不是也想在你的程序里做一个呢?其实,我们只要调用API函数ShellExecute和在鼠标移动时改变一下文字的颜色,就可以在自己的程序中出现这种效果。
首先新建一个工程,在窗体Form1上添加两个Label组件,它们的Name属性使用默认的Label1和Label2。
然后在Form1的OnCreate事件中加入代码:
Label1->Cursor=crHandPoint;
Label2->Cursor=crHandPoint;
Label1->Font->Color =clBlue;
Label2->Font->Color =clBlue;
Label1->Caption="主页:初学者之家网站";
Label2->Caption="E-Mail:fdlweb@sina.com";
再在Label1的OnClick(单击)事件中加入:
//蓝色的字请改成自己的主页地址
ShellExecute(Handle,NULL,"http://fdlweb.myrice.com/",NULL,NULL,SW_SHOWNORMAL);
在OnMouseMove事件中加入:
Label1->Font->Color=clRed;
在Label2的OnClick事件中加入:
//蓝色的字请改成自己邮箱地址
ShellExecute(Handle,NULL,"mailto:fdlweb@sina.com",NULL,NULL,SW_SHOWNORMAL);
在OnMouseMove事件中加入:
Label2->Font->Color=clRed;
最后在Form1的OnMouseMove事件中加入:
Label1->Font->Color=clBlue;
Label2->Font->Color=clBlue;
代码输入完了,按F9编译运行程序就看到效果了。
拷贝屏幕 BitBlt函数可以将一幅位图从一个设备场景拷贝到另一个设备场景,这个函数经常用在抓图程序和游戏编程方面,也可以用来做基于桌面的屏幕保护程序。下面让我们用BitBlt函数来做一个虚假桌面的程序:
首先,添加一个Image组件到窗体中,将窗体Form1的BorderStyle属性设为:bsNone。
接着在窗体的OnCreate事件加入程序代码:
Left=0;
Top=0;
Width=Screen->Width;
Height=Screen->Height;
Image1->Left=0;
Image1->Top=0;
Image1->Width=Screen->Width;
Image1->Height=Screen->Height;
//这句代码就是将桌面拷贝到组件Image1中来存放,
// 其中GetDC(0)返回桌面设备的句柄(HDC)
BitBlt(Image1->Canvas->Handle,0,0,Screen->Width,Screen->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函数ExtractIcon可以提取出在程序文件中的图标,它的头文件是shellapi.h,原型为:
HICON ExtractIcon
(
HINSTANCE hInst, //实例句柄
LPCTSTR lpszExeFileName, //要提取图标的那个程序的文件名
UINT nIconIndex //要提取的图标的索引
);
调用该函数时,参数hInst一般设为当前应用程序的实例句柄,如:Form1->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 <vcl.h>
#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->Caption="选择文件";
Button2->Caption="保存图标";
Button3->Caption="保存所有";
Button4->Caption="退出";
Edit1->Text=0;
Edit2->Text=0;
Image1->Width=32;
Image1->Height=32;
OpenDialog1->Filter="可执行文件(*.exe,*.dll)|*.exe;*.dll|图标文件(*.ico)|*.ico|所有文件(*.*)|*.*";
SaveDialog1->Filter="图标文件|*.ico";
ScrollBar1->Enabled=false;
Button2->Enabled=false;
Button3->Enabled=false;
}
//----------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if(OpenDialog1->Execute())
{
TotalIcon = (int)ExtractIcon( Form1->Handle, OpenDialog1->FileName.c_str(), -1 );
if(TotalIcon>0)
{
if(TotalIcon<2)
ScrollBar1->Enabled=false;
else
ScrollBar1->Max=TotalIcon-1;
Button2->Enabled=true;
Button3->Enabled=true;
FileName = OpenDialog1->FileName;
Edit1->Text =TotalIcon;
Icon->Handle = ExtractIcon( Form1->Handle, FileName.c_str(), 0);
Image1->Picture->Icon=Icon;
Edit2->Text=1;
}
else
{
ShowMessage("该文件没有图标");
}
}
}
//----------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
if(SaveDialog1->Execute())
{
//保存图标
Icon->SaveToFile( SaveDialog1->FileName);
}
}
//----------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
if(SaveDialog1->Execute())
//提取所有的图标
for(int i=0;i<TotalIcon-1;i++)
{
Icon->Handle = ExtractIcon( Form1->Handle, FileName.c_str(), i);
Icon->SaveToFile(SaveDialog1->FileName+(AnsiString)i+".ico");
}
}
//----------------------------------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{
Close();
}
//----------------------------------------------------------------
void __fastcall TForm1::ScrollBar1Change(TObject *Sender)
{
Edit2->Text=ScrollBar1->Position+1;
Icon->Handle = ExtractIcon(Form1->Handle, FileName.c_str(),ScrollBar1->Position);
Image1->Picture->Icon=Icon;
}
判断驱动器的类型
使用API函数GetDriveType能判断一个驱动器的类型,该函数返回一个int型的值,当返回值为2时,是软盘;为3时,是硬盘;为4时,是网络映射盘;为5时,是光驱;为6时,是 RAM 磁盘;为其它值时,是非法的盘符。这个API函数包含在winbase.h头文件中,首先在程序头部加上语句:
include <winbase.h>
包含头文件,然后在程序中加入以下代码就可以判断驱动器的类型:
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 来替换。