当前位置:文档之家› 《WINAPI技巧集》

《WINAPI技巧集》

一、拖动无标题窗体:

包含头文件:

#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技巧集 (二)

12/3/2000 0:0:0··符东亮··yesky

(一)不规则窗口

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毫秒,可不要设得太小了,否则“我的电脑”会打不开的(你双击的速度不够快,^_^)。


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

在控制面板里有许多的控制面板项目,这些项目就是控制台应用程序,它们都是标准的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);

//Windo

ws 安装

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事件中加入隐藏程序在任务栏的图标的代码,这样,程序只有在最小化时任务栏才会出现图标,当程序还原最小化时图标又会重新被隐藏起来。

(四)重启、关闭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);

超级链接效果

在很多共享软件的关于对话框里有一些模仿网页的超级链接,如主页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@https://www.doczj.com/doc/cf17524053.html,";

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

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

ShellExecute(Handle,NULL,"https://www.doczj.com/doc/cf17524053.html,/",NULL,NULL,SW_SHOWNORMAL);

在OnMouseMove事件中加入:

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

在Label2的OnClick事件中加入:

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

ShellExecute(Handle,NULL,"mailto:fdlweb@https://www.doczj.com/doc/cf17524053.html,",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函数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 T

Form1::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”。其中,它的dwRo

p参数有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;1000

0;i++);

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

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

Sleep(3000);//延迟三秒

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

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



WINDOW的消息处理机制为了能在应用程序中监控系统的各种事件消息,提供了挂接各种反调函数(HOOK)的功能。这种挂钩函数(HOOK)类似扩充中断驱动程序,挂钩上可以挂接多个反调函数构成一个挂接函数链。系统产生的各种消息首先被送到各种挂接函数,挂接函数根据各自的功能对消息进行监视、修改和控制等,然后交还控制权或将消息传递给下一个挂接函数以致最终达到窗口函数。WINDOW系统的这种反调函数挂接方法虽然会略加影响到系统的运行效率,但在很多场合下是非常有用的,通过合理有效地利用键盘事件的挂钩函数监控机制可以达到预想不到的良好效果。

一、在WINDOWS键盘事件上挂接监控函数的方法

WINDOW下可进行挂接的过滤函数包括11种:

WH_CALLWNDPROC 窗口函数的过滤函数

WH_CBT 计算机培训过滤函数

WH_DEBUG 调试过滤函数

WH_GETMESSAGE 获取消息过滤函数

WH_HARDWARE 硬件消息过滤函数

WH_JOURNALPLAYBACK 消息重放过滤函数

WH_JOURNALRECORD 消息记录过滤函数

WH_MOUSE 鼠标过滤函数

WH_MSGFILTER 消息过滤函数

WH_SYSMSGFILTER 系统消息过滤函数

WH_KEYBOARD 键盘过滤函数

其中键盘过滤函数是最常用最有用的过滤函数类型,不管是哪一种类型的过滤函数,其挂接的基本方法都是相同的。

WINDOW调用挂接的反调函数时总是先调用挂接链首的那个函数,因此必须将键盘挂钩函数利用函数SetWindowsHookEx()将其挂接在函数链首。至于消息是否传递给函数链的下一个函数是由每个具体函数功能确定的,如果消息需要传统给下一个函数,可调用API函数的CallNextHookEx()来实现,如果不传递直接返回即可。

挂接函数可以是用来监控所有线程消息的全局性函数,也可以是单独监控某一线程的局部性函数。如果挂接函数是局部函数,可以将它放到一个.DLL动态链接库中,

也可以放在一个局部模块中;如果挂接函数是全局的,那么必须将其放在一个.DLL动态链接库中。挂接函数必须严格按照下述格式进行声明,以键盘挂钩函数为例:

int FAR PASCAL KeyboardProc(

int nCode,WORD wParam,DWORD lParam)

其中KeyboardProc为定义挂接函数名,该函数必须在模块定义文件中利用EXPORTS命令进行说明;nCode决定挂接函数是否对当前消息进行处理;wParam和lParam为具体的消息内容。

二、键盘事件挂接函数的安装与下载

在程序中可以利用函数SetWindowsHookEx()来挂接过滤函数,在挂接函数时必须指出该挂接函数的类型、函数的入口地址以及函数是全局性的还是局部性的,挂接函数的具体调用格式如下:

SetWindowsHookEx(iType,iProc,hInst,iCode)

其中iType为挂接函数类型,键盘类型为WH_KEYBOARD,iProc为挂接函数地址,hInst为挂接函数链接库实例句柄,iCode为监控代码-0表示全局性函数。

如果挂接函数需要将消息传递给下一个过滤函数,则在该挂接函数返回前还需要调用一次CallNextHookEx()函数,当需要下载挂接函数时,只要调用一次UnhookWindowsHookEx(iProc)函数即可实现。

如果函数是全局性的,那么它必须放在一个.DLL动态链接库中,这时该函数调用方法可以和其它普通.DLL函数一样有三种:

1.在DEF定义文件中直接用函数名或序号说明:

EXPORTS

WEP @1 RESIDENTNAME

InitHooksDll @2

InstallFilter @3

KeyboardProc @4

用序号说明格式为:链接库名.函数名(如本例中说明方法为KEYDLL.KeyboardProc)。

2.在应用程序中利用函数直接调用:

首先在应用程序中利用LoadLibrary(LPSTR "链接库名")将动态链接库装入,并取得装载库模块句柄hInst,然后直接利用GetProcAddress(HINSTANCE hInst,LPSTR "函数过程名")获取函数地址,然后直接调用该地址即可,程序结束前利用函数FreeLibrary( )释放装入的动态链接库即可。

3.利用输入库.LIB方法

利用IMPLIB.EXE程序在建立动态链接库的同时建立相应的输入库.LIB,然后直接在项目文件中增加该输入库。

三、WINDOWS挂钩监控函数的实现步骤

WINDOWS挂钩函数只有放在动态链接库DLL中才能实现所有事件的监控功能。在.DLL中形成挂钩监控函数基本方法及其基本结构如下:

1、首先声明DLL中的变量和过程;

2、然后编制DLL主模块LibMain(),建立模块实例;

3、建立系统退出DLL机制WEP()函数;

4、完成DLL初始化函数InitHooksDll(),传递主窗口程序句柄;

5、编制挂钩安装和下载函数InstallFilter();


、编制挂钩函数KeyboardProc(),在其中设置监控功能,并确定继续调下一个钩子函数还是直接返回WINDOWS应用程序。

7、在WINDOWS主程序中需要初始化DLL并安装相应挂钩函数,由挂接的钩子函数负责与主程序通信;

8、在不需要监控时由下载功能卸掉挂接函数。

四、WINDOWS下键盘挂钩监控函数的应用技术

目前标准的104 键盘上都有两个特殊的按键,其上分别用WINDOW程序徽标和鼠标下拉列表标识,本文暂且分别称为Micro左键和Micro右键,前者用来模拟鼠标左键激活开始菜单,后者用来模拟鼠标右键激活属性菜单。这两个特殊按键只有在按下后立即抬起即完成 CLICK过程才能实现其功能,并且没有和其它按键进行组合使用。

由于WINDOWS 系统中将按键划分得更加详细,使应用程序中很难灵活定义自己的专用快捷键,比如在开发.IME等应用程序时很难找到不与WORD8.0等其它应用程序冲突的功能按键。如果将标准104键盘中的这两个特殊按键作为模拟CTRL和ALT 等专用按键,使其和其它按键组合,就可以在自己的应用程序中自由地设置专用功能键,为应用程序实现各种功能快捷键提供灵活性。正常情况下WINDOWS 键盘事件驱动程序并不将这两个按键的消息进行正常解释,这就必须利用键盘事件的挂钩监控函数来实现其特定的功能。其方法如下:

1、首先编制如下一个简单动态链接库程序,并编译成DLL文件。

#include "windows.h"

int FAR PASCAL LibMain(HANDLE hModule,UINT wDataSeg,

UINT cbHeapSize,LPSTR lpszCmdLine);

int WINAPI WEP(int bSystemExit);

int WINAPI InitHooksDll(HWND hwndMainWindow);

int WINAPI InstallFilter(BOOL nCode);

LRESULT CALLBACK KeyHook(int nCode,WORD wParam,DWORD lParam);

static HANDLE hInstance; // 全局句柄

static HWND hWndMain; // 主窗口句柄

static int InitCalled=0; // 初始化标志

static HHOOK hKeyHook;

FARPROC lpfnKeyHook=(FARPROC)KeyHook;

BOOL HookStates=FALSE;

int FAR PASCAL LibMain(

HANDLE hModule,

UINT wDataSeg,

UINT cbHeapSize,

LPSTR lpszCmdLine)

{

if (cbHeapSize!=0) UnlockData(0);

hInstance = hModule;

return 1;

}

int WINAPI WEP (int bSystemExit)

{ return 1;}

int WINAPI InitHooksDll(HWND hwndMainWindow)

{ hWndMain = hwndMainWindow;

InitCalled = 1;

return (0);

}

int WINAPI InstallFilter(BOOL nCode)

{ if (InitCalled==0) return (-1);

if (nCode==TRUE) {

hKeyHook=SetWindowsHookEx(WH_KEYBOARD,

(HOOKPROC)lpfnKeyHook,hInstance,0);

HookStates=TRUE;


} else {

UnhookWindowsHookEx(hKeyHook);

HookStates=FALSE;

}

return(0);

}

LRESULT CALLBACK KeyHook(int nCode,WORD wParam,DWORD lParam)

{

static BOOL msflag=FALSE;

if(nCode>=0) {

if(HookStates==TRUE){

if((wParam==0xff)|| //WIN3.X下按键值

(wParam==0x5b)||(wParam==0x5c)){//WIN95下按键值

if((i==0x15b)||(i==0x15c)){ //按键按下处理

msflag=TRUE;

PostMessage(hWndMain,0x7fff,0x1,0x3L);

} else if((i==0xc15b)||(i==0xc15c)){//按键抬起处理

msflag=FALSE;

PostMessage(hWndMain,0x7fff,0x2,0x3L);

}

}

}

}

return((int)CallNextHookEx(hKeyHook,nCode,wParam,lParam));

}

该程序的主要功能是监控键盘按键消息,将两个特殊按键Micro按下和抬起消息转换成自定义类型的消息,并将自定义消息发送给应用程序主窗口函数。

2、在应用程序主函数中建立窗口后,调用InitHooksDll()函数来初始化动态链接库,并将应用程序主窗口句柄传递给链接库,然后调用InstallFilter()函数挂接键盘事件监控回调函数。

InitHooksDll(hIMEWnd); //初始化DLL

InstallFilter(TRUE); //安装键盘回调函数

3、在应用程序主窗口函数处理自定义消息时,保存Micro按键的状态,供组合按键处理时判断使用。

switch (iMessage) {

case 0x7fff: //自定义消息类型

if(lParam==0x3L){//设置Micro键的状态

if(wParam==0x1) MicroFlag=TRUE;

else if(wParam==0x2) MicroFlag=FALSE;

}

break;

4、在进行按键组合处理时,首先判断Micro键是否按下,然后再进行其它按键的判断处理。

case WM_KEYDOWN: // 按键按下处理

if(MicroFlag==TRUE){//Micro键按下

if((BYTE)HIBYTE(wParam)==0x5b){

//Micro+"["组合键

......//按键功能处理

} else if((BYTE)HIBYTE(wParam)==0x5d){

//Micro+"]"组合键

......//按键功能处理

}

}

break;

5、当应用程序退出时应注意下载键盘监控函数,即调用InstallFilter(FALSE)函数一次。

6、利用本文提供的方法设置自己的应用程序功能按键,在保证程序功能按键不会与其它系统发生冲突的同时,有效地利用了系统中现有资源,而且在实现应用程序功能的同时灵活应用了系统中提供的各种功能调用。
 

关于Hook

一、基本概念:

钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理

函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。

钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。

二、运行机制:

1、钩子链表和钩子子程:

每一个Hook都有一个与之相关联的指针列表,称之为钩子链表,由系统来维护。这个列表的指针指向指定的,应用程序定义的,被Hook子程调用的回调函数,也就是该钩子的各个处理子程。当与指定的Hook类型关联的消息发生时,系统就把这个消息传递到Hook子程。一些Hook子程可以只监视消息,或者修改消息,或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。

Windows 并不要求钩子子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载,Windows 便释放其占用的内存,并更新整个Hook链表。如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系统会自动为它做卸载钩子的操作。

钩子子程是一个应用程序定义的回调函数(CALLBACK Function),不能定义成某个类的成员函数,只能定义为普通的C函数。用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可以是系统中所有线程的事件。

钩子子程必须按照以下的语法:
LRESULT CALLBACK HookProc
(
int nCode,
WPARAM wParam,
LPARAM lParam
);
HookProc是应用程序定义的名字。

nCode参数是Hook代码,Hook子程使用这个参数来确定任务。这个参数的值依赖于Hook类型,每一种Hook都有自己的Hook代码特征字符集。
wParam和lParam参数的值依赖于Hook代码,但是它们的典型值是包含了关于发送或者接收消息的信息。

2、钩子的安装与释放:

使用API函数SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子链表中。SetWindowsHookEx函数总是在Hook链的开头安装Hook子程。当指定类型的Hook监视的事件发生时,系统就调用与这个Hook关联的Hook链的开头的Hook子程。每一个Hook链中的Hook子程都决定是否把这个事件传递到下一个Hook子程。Hook子程传递事件到下一个Hook子程需要调用CallNextHookEx函数。

HHOOK SetWindowsHookEx(
int idHook, // 钩子的类型,即它处理的消息类型
HOOKPROC lpfn, // 钩子子程的地址指针。如果dwThreadId

参数为0
// 或是一个由别的进程创建的线程的标识,
// lpfn必须指向DLL中的钩子子程。
// 除此以外,lpfn可以指向当前进程的一段钩子子程代码。
// 钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。
HINSTANCE hMod, // 应用程序实例的句柄。标识包含lpfn所指的子程的DLL。
// 如果dwThreadId 标识当前进程创建的一个线程,
// 而且子程代码位于当前进程,hMod必须为NULL。
// 可以很简单的设定其为本应用程序的实例句柄。
DWORD dwThreadId // 与安装的钩子子程相关联的线程的标识符。
// 如果为0,钩子子程与所有的线程关联,即为全局钩子。
);
函数成功则返回钩子子程的句柄,失败返回NULL。

以上所说的钩子子程与线程相关联是指在一钩子链表中发给该线程的消息同时发送给钩子子程,且被钩子子程先处理。

在钩子子程中调用得到控制权的钩子函数在完成对消息的处理后,如果想要该消息继续传递,那么它必须调用另外一个SDK中的API函数CallNextHookEx来传递它,以执行钩子链表所指的下一个钩子子程。这个函数成功时返回钩子链中下一个钩子过程的返回值,返回值的类型依赖于钩子的类型。这个函数的原型如下:

LRESULT CallNextHookEx
(
HHOOK hhk;
int nCode;
WPARAM wParam;
LPARAM lParam;
);

hhk为当前钩子的句柄,由SetWindowsHookEx()函数返回。
NCode为传给钩子过程的事件代码。
wParam和lParam 分别是传给钩子子程的wParam值,其具体含义与钩子类型有关。

钩子函数也可以通过直接返回TRUE来丢弃该消息,并阻止该消息的传递。否则的话,其他安装了钩子的应用程序将不会接收到钩子的通知而且还有可能产生不正确的结果。

钩子在使用完之后需要用UnHookWindowsHookEx()卸载,否则会造成麻烦。释放钩子比较简单,UnHookWindowsHookEx()只有一个参数。函数原型如下:

UnHookWindowsHookEx
(
HHOOK hhk;
);
函数成功返回TRUE,否则返回FALSE。

3、一些运行机制:

在Win16环境中,DLL的全局数据对每个载入它的进程来说都是相同的;而在Win32环境中,情况却发生了变化,DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。当进程在载入DLL时,操作系统自动把DLL地址映射到该进程的私有空间,也就是进程的虚拟地址空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间。也就是说每个进程所拥有的相同的DLL的全局数据,它们的名称相同,但其值却并不一定是相同的,而且是互不干涉的。

因此,在Win32环境

下要想在多个进程中共享数据,就必须进行必要的设置。在访问同一个Dll的各进程之间共享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。

#pragma data_seg预处理指令用于设置共享数据段。例如:
#pragma data_seg("SharedDataName")
HHOOK hHook=NULL;
#pragma data_seg()
在#pragma data_seg("SharedDataName")和#pragma data_seg()之间的所有变量 将被访问该Dll的所有进程看到和共享。

当进程隐式或显式调用一个动态库里的函数时,系统都要把这个动态库映射到这个进程的虚拟地址空间里(以下简称"地址空间")。这使得DLL成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈。

4、系统钩子与线程钩子:

SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。

线程勾子用于监视指定线程的事件消息。线程勾子一般在当前线程或者当前线程派生的线程内。

系统勾子监视系统中的所有线程的事件消息。因为系统勾子会影响系统中所有的应用程序,所以勾子函数必须放在独立的动态链接库(DLL) 中。系统自动将包含"钩子回调函数"的DLL映射到受钩子函数影响的所有进程的地址空间中,即将这个DLL注入了那些进程。

几点说明:
(1)如果对于同一事件(如鼠标消息)既安装了线程勾子又安装了系统勾子,那么系统会自动先调用线程勾子,然后调用系统勾子。

(2)对同一事件消息可安装多个勾子处理过程,这些勾子处理过程形成了勾子链。当前勾子处理结束后应把勾子信息传递给下一个勾子函数。

(3)勾子特别是系统勾子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装勾子,在使用完毕后要及时卸载。

三、钩子类型

每一种类型的Hook可以使应用程序能够监视不同类型的系统消息处理机制。下面描述所有可以利用的Hook类型。

1、WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks

WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks使你可以监视发送到窗口过程的消息。系统在消息发送到接收窗口过程之前调用WH_CALLWNDPROC Hook子程,并且在窗口过程处理完消息之后调用WH_CALLWNDPROCRET Hook子程。

WH_CALLWNDPROCRET Hook传递指针到CWPRETSTRUCT结构,再传递到Hook子程。CWPRETSTRUCT结构包含了来自处理消息的窗口过程的返回值,同样也包括了与这个消息关联的消息参数。

2、WH_CBT Hook

在以下事件之前,系统都会调用WH_CBT Hook子程,这些事件包括

相关主题
文本预览
相关文档 最新文档