《VB 与Windows API 2》很好的vb攻略初学进阶必看_kikyou無念的空间_ ...

VB 与 Windows API 讲座(二) -
屏幕抓取与屏幕保护程序
----                                                                                                                   
「VB 没有提供这样的功能, 必须呼叫 Windows API」,当笔者这样回答读者之后,有些读者会要求笔者介绍 Windows API 方面的书籍, 尤其是中文书, 但实际上,目前市面上只看到一本原文书(ZD Press 出版的 Visual Basic 5.0 ProgrammingGuide to Win32 API),中文书则未见过, 为什么没有 Windows API 方面的中文书呢?道理很简单, Windows API 对 VB 的学习者而言,犹如山峰之颠, 能够一路爬上来的人原本就十分有限,而越往山顶,人烟越稀少, 因此这样的书写出来, 销售成绩必然惨不忍睹,也许有些读者会觉得本土的作者实在很混,只愿意写简单的书,但撰写深入的书籍, 费时费力费神而销售成绩又不好,最终结果是这种不混的作者混不下去,当过读者而现在又是作者的笔者,对于此一现象有时也颇感无奈。不管怎样, 笔者现在已经开始介绍 Windows API 了,也许真的不想混了,但混与不混不是笔者开启本系列讲座最担心的事情,笔者所担心的是这样的主题对读者来说会不会太枯燥?会不会太艰涩?会不会像笔者当初研读 Windows API 时, 越研读心中的疑问越多,就好像进入无底黑洞一样?为了避免以上的结果, 在内容的介绍上,笔者会倾向于即学即用的 API, 短期内尽量避免深入 Windows 无底黑洞地带, 而会先到那些有花有草地方。

本期笔者想xx您去的地方是:屏幕抓取程序与屏幕保护程序。
------------------------------------------------------------------------
屏幕抓取程序简介
------------------------------------------------------------------------

如果不写程序, 抓取屏幕图像的方法是:按下 Print Screen 键,然后开启Windows 的小画家,接着选取小画家菜单的「编辑/贴上」,即可将屏幕图像复制到小画家的绘图区域中。 如果写程序呢?稍后笔者就会介绍,介绍以前,建议您先进入笔者的网站下载笔者所撰写的屏幕抓取程序(CopyScr.vbp),一来可了解程序的基本运作模式,二来不必自己输入程序,此一程序执行后,选取菜单的「我抓我抓我抓抓」,即可将屏幕的图像复制进来。
图-1「屏幕抓取程序」
------------------------------------------------------------------------
----
----
屏幕保护程序简介
------------------------------------------------------------------------
----
----
保护屏幕最简单的方法就是不用时关掉它,但有这种好习惯的人实在不多,所以有人写程序侦测鼠标与键盘的输入,如果有一段时间没有鼠标或键盘的输入,便将计算机视为没有人在使用,而把屏幕显示成黑色, 用以保护屏幕,后来又有人觉得黑色太难看了,就加了一点动画,慢慢地动画越来越多也越有趣,结果反倒成为屏幕保护程序不可或缺的功能,而不再以保护屏幕为主了。如何撰写屏幕保护程序当然也是本期的重点,同样的, 介绍以前,请进入笔者的网站下载笔者所撰写的屏幕保护程序(saver.vbp),此一程序预设的等待时间是 0.5 分钟, 如果您等待 30 秒不操作键盘与鼠标,程序便会进入屏幕保护状态,您也可以设定等待的时间, 如图-2,或者直接按下「立刻测试」钮以检视屏幕保护程序的动画。(特别注意:请务必以程序右上角的关闭钮结束程序,不要以 VB 的结束钮来结束程序, 否则…, 玩看看,就知道会有什么后果) 图-2 「屏幕保护程序」的设定画面了解屏幕抓取程序及屏幕保护程序的基本运作模式,接下来让笔者来说明相关的工作概念。
------------------------------------------------------------------------

Windows 的绘图观念
------------------------------------------------------------------------

VB 用来绘图的方法有 Line(画线及矩形)、Circle(画圆)、PSet(画点)、PaintPicture(描绘图片)…等,这些方法有一共通特点 ─ 只能描绘在窗体(Form)窗口的「显示区域」(Windows 称之为 client rectangle),想跨越显示区域而把文字或图形描绘在窗口标题区、菜单区、屏幕任意区域,想都别想,而以屏幕保护程序为例,则必须在屏幕任意位置绘图。想在屏幕任意位置绘图,我们必须先了解 Windows 的绘图观念屏幕任意位置绘图, 先取 hDC
------------------------------------------------------------------------
----
----
所有 Windows 的绘图, 都必须先取得 hDC, 然后以 hDC 呼叫绘图的 API。hDC 是 handle of DC(Device Context) 的缩写, 有关 handle 的观念笔者上一期已经有所说明,本期不再重述, 至于 DC 呢?Windows 规定任何程序绘图之前都必须先配置一块绘图专用的系统资源(通常是内存),这绘图专用的系统资源就是 DC 啦。DC 与其它系统资源一样, 会有一个{wy}的 handle 值, 此一 handle 值便是 hDC。VB 程序呼叫 Line、Circle、Pset、PaintPicture…等方法时, 表面上好像与 DC无关,但实际上它们都会预先配置好 DC, 然后透过 DC 来绘图,您可以查阅窗体及 PictureBox 对象的说明文件,结果可以发现它们都含有 hDC 属性,此一属性即为窗体及 PictureBox 对象用来呼叫 Windows 绘图函数的 DC handle。


BitBlt:图像描绘函数
------------------------------------------------------------------------
----
----
二话不说, 让我们直接观察一个利用 DC 来绘图的 API 函数 ─ BitBlt,BitBlt 的作用与 PaintPicture 有点类似, 假设窗体上含有一 PictureBox, 而此一PictureBox 的 Picture 属性亦设定有图片, 若使用 VB 的绘图功能, 则将PictureBox 的图片绘制在 Form1 的方法是:Form1.PaintPicture Picture1.Picture, 0, 0 如果使用 BitBlt 函数则是:

ret = BitBlt(Form1.hDC, 0, 0, Picture1.Width, Picture1.Height,
Picture1.hDC,
ret = BitBlt(Form1.hDC, 0, 0, Picture1.Width, Picture1.Height,
Picture1.hDC,
0, 0, SRCCOPY)
作用是把 Picture1 的图像复制到 Form1, 使用此一函数时,请特别注意参数四(如以上叙述之 Picture1.Width)及参数五(如以上叙述之 Picture1.Height),这两个参数分别表示长与宽,但单位是「像素」(pixel),所以执行以上叙述之前, 必须先把窗体的 ScaleMode 属性都设定成 "3 - 像素" 才可以, 此一 BitBlt 函数之使用实例请参阅笔者所完成的 BitBlt.vbp(包含在下载之档案中)。 

屏幕 hDC 的取得与释回
------------------------------------------------------------------------
----
----
窗体及 PictureBox 对象的 DC 是 VB 预先帮我们准备好的,在屏幕抓取及保护程序中,我们则必须呼叫 Windows API 取得屏幕的 DC,如此方可读取屏幕的图像, 以及复制图像到屏幕上。取得屏幕 DC 的函数是 GetDC, 呼叫之叙述如下:
Dim hDC As Long
hDC = GetDC(0)
传回值 hDC 即等于屏幕的 hDC。在此笔者顺便要说明 Windows 另一个重要的观念:系统资源不用时应释回,所以在我们取得屏幕 DC,并且完成绘图之后, 别忘了将 DC 释回, 此时呼叫的 API 是:
' hDC 是先前 GetDC(0) 的传回值
ret = ReleaseDC(0, hDC)


将屏幕图像复制到 VB 对象中
------------------------------------------------------------------------
----
介绍 BitBlt 及 GetDC 两个 API 函数之后,屏幕抓取程序几乎是呼之欲出了,例如使用以下的叙述可以将屏幕图像复制到窗体对象上: 
Dim hDC As Long
hDC = GetDC(0)
ret = BitBlt (Form1.hDC, 0, 0, 屏幕宽, 屏幕高, hDC, 0, 0, SRCCOPY)
ret = ReleaseDC(0, hDC)
不过请注意「屏幕宽」及「屏幕高」两个参数,这两个参数的长度单位是「像素」,以 640×480 的屏幕为例, 应该指定成 640 及 480,但问题是别人使用的屏幕可能不是640×480, 所以我们应该利用 Screen 对象的 Width 及 Height 属性读取屏幕的宽与高(注:此时读取之宽与高是以 Twips 为单位),然后再利用以下公式求取屏幕以 pixel 为单位的宽与高: 
pixel单位之屏幕宽 = Screen.Width \ Screen.TwipsPerPixelX
pixel单位之屏幕高 = Screen.Height \ Screen.TwipsPerPixelY
将屏幕图像复制到窗体上面的完整程序, 请参阅笔者所完成的 ScrForm.vbp(包含在下载之档案中)。
------------------------------------------------------------------------
----
----
屏幕抓取程序之制作
------------------------------------------------------------------------
----
----
接着请以笔者所完成的 CopyScr.vbp 来例,让我们来了解屏幕抓取程序还有哪些地方该注意。

 
AutoRedraw(自动重绘)属性的使用
------------------------------------------------------------------------
----
----
首先笔者想请您做个实验:执行 ScrForm.vbp 程序,并且复制屏幕图像到窗体上,接着以其它窗口盖住此一窗体, 然后再移开,结果窗体上曾经被盖住的区域其图像都会消失。解决以上问题常用的方法是将窗体的 AutoRedraw(自动重绘)属性设定为 True,当我们将窗体的 AutoRedraw 属性设定为 True 之后,将来窗体若有区域被覆盖住而又回到萤幕前端,则窗体都会自动重绘被覆盖的区域,使得窗体能够展现原有的图像。利用 AutoRedraw 属性来解决以上的问题看起来十分简单, 但它有个严重的缺点:

当我们将窗体的 AutoRedraw 属性设定为 True 之后,窗体将会记录每一个绘图的动作,而当绘图动作越来越多时,所使用的内存将会逐渐增加,自动重绘的时间也会越来越长。为了改善以上的缺点,笔者的作法如下:
Form名.AutoRedraw = True ' 设定自动重绘
...
... 连续性的绘图动作
...
Set Form名.Picture = Form名.Image ' 将窗体的图像设定成常驻性的图片
Form名.AutoRedraw = False ' 取消自动重绘
由于 Picture 属性中的图片是常驻的, 因此当我们将窗体的 Image 属性(等于表单上的图像)设定给它,便可以将 AutoRedraw 属性设定为 False, 以避免不必要的重绘动作。  
以上程序使用了 AutoRedraw、Image、及 Picture 属性,这些属性除了适用于表单物件之外,也适用于 PictureBox 对象,而实际上, 笔者所撰写的 CopyScr.vbp并不是把屏幕复制到窗体上,而是复制到 PictureBox 对象之中。浏览图片的操作接口
------------------------------------------------------------------------
----
----
除了将屏幕图像复制到 PictureBox 之外,另一个问题则是屏幕总是比窗体的显示区域来得大,以致被复制到 PictureBox 的图像会超出窗体的显示区域,为了让使用者可以看到屏幕的全貌,我们必须在窗体上布置卷动轴,并提供浏览的操作接口。 有关大图片浏览程序,常见的作法是把显示图片的 PictureBox 布置在另一个PictureBox 里面,然后再利用卷动轴调整内部 PictureBox 相对于外部 PictureBox 的位置,而达到浏览图片的目的, 如图-3。
 
图-3 图片浏览程序所需之控制组件及其布置
至于相关程序则请参考 CopyScr.frm 的 SetPicture 子程序、VScroll1_Change、及 HScroll1_Change 事件程序。
图片的存盘
------------------------------------------------------------------------
----
----
 
{zh1}就是图片的存盘了, VB 提供的存盘叙述是 SavePicture,呼叫格式如下:
 
SavePicture 图片, 文件名称

其中「图片」参数可以是对象的 Picture 属性或利用 LoadPicture 所加载的Picture
对象, 以本程序为例, 则指定成 PictureBox 的 Picture 属性。
------------------------------------------------------------------------
----
----
屏幕保护程序的制作
------------------------------------------------------------------------
----
----
笔者所撰写的屏幕保护程序, 是将屏幕切割成 M×N 个方块,然后在屏幕保护程式启动时,随机变换任意两个方块,此时所需执行的动作是:
 
(1) 将 (M1, N1) 坐标的方块复制到一暂存区。
(2) 将 (M2, N2) 坐标的方块复制到 (M1, N1) 坐标。
(3) 将暂存区的方块复制到 (M2, N2) 坐标。
 
假设每一方块的边长等于 80, 则所需执行的 BitBlt 叙述如下: 
' hDCMem 为暂存区之 hDC
' hDCScreen 为屏幕之 hDC
ret = BitBlt(hDCMem, 0, 0, 80, 80, hDCScreen, M1 * 80, N1 * 80,SRCCOPY)
ret = BitBlt(hDCScreen, M1 * 80, N1 * 80, 80, 80, hDCScreen, M2 * 80, M2 * 80, SRCCOPY)
ret = BitBlt(hDCScreen, M2 * 80, M2 * 80, 80, 80, hDCMem, 0, 0,SRCCOPY) 
暂存区 DC 的取得与释回
暂存区 DC 的取得与释回
------------------------------------------------------------------------
----
----
在以上程序中, hDCScreen 是利用 GetDC(0) 取得, 而 hDCMem(暂存区的 DC)则必须利用 CreateCompatibleDC 函数来取得, 如下: 
Dim hDCMem As Long
hDCMem = CreateCompatibleDC(hDCScreen)
此一函数的作用是建立兼容性 DC, 由于传入的是 hDCScreen(屏幕的 hDC),所以建立出来的 hDCMem 将与屏幕兼容(主要是色盘之颜色数目相同)。暂存区 DC 不再使用时应将其释回, 此时呼叫之 API 是 DeleteDC,如下:(释回屏幕 DC 的函数是 ReleaseDC,两者不可以混合使用)
ret = DeleteDC(hDCMem)
CreateCompatibleBitmap:为 DC 建立位图
------------------------------------------------------------------------
----
----
 
取得 hDCMem 之后, 利用 BitBlt 应该就可以将 hDCScreen 的图像复制到暂存区 DC中, 但实际上不然, BitBlt 只对含有「位图」(Bitmap)的 DC 有作用,而经由 CreateCompatibleDC 所建立的暂存区 DC 一开始是不含位图的, 所以呼叫BitBlt 函数并没有作用, 为了让 BitBlt 能够复制图像到暂存区 DC,以及从暂存区 DC读取图像, 我们必须为暂存区 DC 建立位图, 此时呼叫之 API 如下: 
Dim hBitmap As Long
hBitmap = CreateCompatibleBitmap(hDCScreen, 宽, 高) ' 建立位图
hBitmap = CreateCompatibleBitmap(hDCScreen, 宽, 高) ' 建立位图
ret = SelectObject(hDCMem, hBitmap) ' 将位图设定给 hDCMem
请特别注意传入 CreateCompatibleBitmap 的 hDC 是屏幕的 hDC,不是暂存区的hDC,其作用是建立与屏幕兼容的位图(颜色数目与屏幕相同的位图),而接下来必须呼叫 SelectObject 让此一位图的 handle(hBitmap) 附属于 hDCMem, 也就是让暂存区 DC 含有位图, 如此一来 hDCMem 方可用于 BitBlt 函数。

位图的释回
------------------------------------------------------------------------
----
----
 
不使用位图时, 可呼叫 DeleteObject(hBitmap) 将其释回,但请注意附属于DC 的位图是不可以释回的, 除非 DC 也已经被释回系统,例如:
 
' hBitmap 附属于 hDCMem
ret = DeleteDC(hDCMem) ' 先释回 DC
ret = DeleteObject(hBitmap) ' 再释回位图
屏幕保护程序之动画
------------------------------------------------------------------------
----
----
 
使用以上所介绍的 API 函数,总算可以达到随机变换屏幕任意两个方块的目的了,请看笔者所完成的子程序:(此一子程序放置 saver.vbp 专案的 saver.bas 档案中,也包含在下载的档案中)

 
Sub Saver()
Dim M1 As Long, M2 As Long, N1 As Long, N2 As Long, ret As Long
Dim hDCMem As Long, hDCScreen As Long, hBitmap As Long
Dim sx As Integer, sy As Integer
sx = Screen.Width \ Screen.TwipsPerPixelX ' 以 pixel 为单位之屏幕宽
sy = Screen.Height \ Screen.TwipsPerPixelY ' 以 pixel 为单位之屏幕高
hDCScreen = GetDC(0) ' 取得屏幕 DC
hDCMem = CreateCompatibleDC(hDCScreen) ' 建立暂存区 DC
hBitmap = CreateCompatibleBitmap(hDCScreen, BSize, BSize) ' 建立位图
ret = SelectObject(hDCMem, hBitmap) ' 将位图设定给暂存区 DC
M1 = CInt(Rnd * sx \ BSize)
N1 = CInt(Rnd * sy \ BSize) ' (M1, N1) 为方块一
M2 = CInt(Rnd * sx \ BSize)
N2 = CInt(Rnd * sy \ BSize) ' (M2, N2) 为方块二
' 方块一与方块二互换
ret = BitBlt(hDCMem, 0, 0, BSize, BSize, hDCScreen, M1 * BSize, N1 *BSize,SRCCOPY)
ret = BitBlt(hDCScreen, M1 * BSize, N1 * BSize, BSize, BSize, hDCScreen, M2* BSize, N2 *BSize, SRCCOPY)
ret = BitBlt(hDCScreen, M2 * BSize, N2 * BSize, BSize, BSize, hDCMem, 0,0,SRCCOPY)

ret = ReleaseDC(0, hDCScreen) ' 释回屏幕 DC
ret = DeleteDC(hDCMem) ' 释回暂存区 DC
ret = DeleteObject(hBitmap) ' 释回位图, 一定要放在 DeleteDC 之后
End Sub
有了 Saver 子程序, 再配合连续的变换动作,这就是笔者所制作的屏幕保护程式动画。 

键盘及鼠标的侦测
------------------------------------------------------------------------
----
----
 
当屏幕保护程序进入动画时,程序必须侦测使用者是否有操作键盘或鼠标,如果有,则终止动画,并且将屏幕还原,而当程序离开动画或尚未进入动画时,必须侦测使用者没有操作键盘或鼠标的时间是否超过等待时间,如果是, 则进入动画。但是在 Windows 的多任务作业环境底下,所有键盘及鼠标的输入是由 Windows 统筹管里的,当使用者操作键盘或鼠标时, 键盘与鼠标的接收者是 Windows,而 Windows 会根据当时的情况,决定键盘及鼠标的输入该传给哪一个程序或窗口,因此每一个程式或视窗只会收到属于自己的键盘及鼠标输入。以上的运作模式就好像电话一样,正常情况之下, 每个人都只会收到属于自己的电话,除非, 嘿嘿,窃听。在 Windows 的工作模式底下,想要窃听键盘及鼠标的输入是可能的,一旦窃听成功,属于其它窗口的键盘及鼠标输入都会先传到我们的程序中。有关窃听方面的程序设计,笔者想留待下一回再介绍,因为必须说明的观念实在不少,本期请直接使用笔者所完成的键盘及鼠标侦测程序 ─ Saver.vbp 中的 hook.bas 模,本期请直接使用笔者所完成的键盘及鼠标侦测程序 ─ Saver.vbp 中的 hook.bas 模组, 此一模块虽然没有提供窃听的功能,但可以侦测到是否有键盘及鼠标被输入(不管输入的对象是哪一个程序或窗口都可以侦测到)。使用 hook.bas 的方法如下:
 
1. 侦测键盘及鼠标的输入之前, 呼叫 StartHook 子程序。
2. 呼叫 StartHook 之后, 只要发生键盘或鼠标的输入, hook 模块便会将键盘或鼠标被输入的时间录在 tmKbMouse 全域变量中,其它程序可以利用目前时间(呼叫 Now函数)减去此一变量,得知使用者隔多久没有操作键盘及鼠标了,如果超过等待时间,便可以进入屏幕保护程序的动画。
3. 进入屏幕保护程序的动画之前, 记得把 hook.bas 的 KbMouseDetect 全域变数设定为 False, 接着进入屏幕保护状态之后,只要使用者再度操作键盘或鼠标,此一变数便会被 hook.bas 模块设定 True, 而其它程序可以再判断此一变量,以停止动画并还原屏幕。
4. 屏幕保护程序结束以前一定要呼叫 FreeHook 子程序, 不然会造成 Windows运作不正常, 举例来说, 笔者所撰写的 saver.frm 中便是在 Form_Load 事件程序中呼叫 StartHook, 在 Form_Unload 事件程序中呼叫 FreeHook。
屏幕的还原
------------------------------------------------------------------------
----
----
当屏幕保护程序停止动画时, 我们必须把屏幕还原,还原的方法有很多种,例如启动动画之前先储存屏幕的图像,结束时再回存图像,不过这个方法比较浪费内存,笔者使用的方法是把窗体隐藏起来,然后再显示出来, 接着放到{zd0},然后恢复成原大小,作用是利用窗体覆盖整个屏幕,则{zh1}当窗体又恢复成原大小时,屏幕图像即会自动还原,程序如下:
Sub RestoreScreen()
Me.Visible = False
Me.Visible = True
Me.WindowState = vbMaximized
Me.WindowState = vbNormal
End Sub
 
时间的控制
------------------------------------------------------------------------
 
除了 saver 及 hook 两个模块之外, 最重要的事情就是时间的控制了,在笔者的 saver 窗体中, 布置了两个 Timer 控制组件(Timer1 及 Timer2), 其中 Timer2用来侦测使用者隔多久没有操作键盘及鼠标,若超过等待时间, 则驱动 Timer1 进入动画描绘状态,其程序如下:
 
Private Sub Timer2_Timer()
If DateDiff("s", tmKbMouse, Now) > WaitTime * 60 Then
Timer1.Interval = 10 ' 驱动动画
End If
End Sub
 
至于 Timer1 则是用来显示动画,并判断使用者是否操作了键盘及鼠标以决定是否中断动画,程序如下:
 
Private Sub Timer1_Timer()
If Not StartSaver Then ' 进入动画的初始动作
StartSaver = True
KbMouseDetect = False
Exit Sub
End If
If KbMouseDetect Then ' 用者操作了键盘或鼠标
Timer1.Interval = 0
StartSaver = False
RestoreScreen ' 还原屏幕
Else
Saver ' 显示动画
End If
End Sub
 
------------------------------------------------------------------------
----
----
制作您自己的屏幕保护程序
End Sub
------------------------------------------------------------------------
----
----
以上介绍的是笔者所制作的屏幕保护程序, 虽然动画的部分稍嫌简单,但Windows 技术的难处均已克服, 如果您想制作自己的屏幕保护程序,需要修改的是动画的部分,也就是 saver.bas 中的 saver 子程序, 此外,屏幕保护程序平常是隐藏起来的,所以 saver 窗体加载后要呼叫 Me.Hide 自我隐藏, {zh1}则是把编译成执行档的萤幕保护程序复制到 Windows 的「启动」数据夹中, 让 Windows 启动时,把屏幕保护程式载入系统中。



郑重声明:资讯 【《VB 与Windows API 2》很好的vb攻略初学进阶必看_kikyou無念的空间_ ...】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——