用delphi实现远程屏幕抓取
| 在网络管理中,有时需要通过监视远程计算机屏幕来了解网上微机的使用情况。虽然,市面上有很多软件可以实现该功能,有些甚至可以进行远程控制,但在使用上缺乏灵活性,如无法指定远程计算机屏幕区域的大小和位置,进而无法在一屏上同时监视多个屏幕。其实,可以用delphi自行编制一个灵活的远程屏幕抓取工具,简述如下。 一、软硬件要求。 windows95/98对等网,用来监视的计算机(以下简称主控机)和被监视的计算机(以下简称受控机)都必须装有tcp/ip 协议,并正确配置。如没有网络,也可以在一台计算机上进行调试。 二、实现方法。 编制两个应用程序,一个为vclient.exe,装在受控机上,另一个为vserver.exe,装在主控机上。vserver.exe指定要监视的受控机的ip地址和将要在受控机屏幕上抓取区域的大小和位置,并发出屏幕抓取指令给vclient.exe,vclient.exe得到指令后,在受控机屏幕上选取指定区域,生成数据流,将其发回主控机,并在主控机上显示出抓取区域的bmp图象。由以上过程可以看出,该方法的关键有二:一是如何在受控机上进行屏幕抓取,二是如何通过tcp/ip协议在两台计算机中传输数据。 udp(user datagram protocol,意为用户报文协议)是internet上广泛采用的通信协议之一。与tcp协议不同,它是一种非连接的传输协议,没有确认机制,可靠性不如tcp,但它的效率却比tcp高,用于远程屏幕监视还是比较适合的。同时,udp控件不区分服务器端和客户端,只区分发送端和接收端,编程上较为简单,故选用udp协议,使用delphi 4.0提供的tnmudp控件。 三、创建演示程序。 第一步,编制vclient.exe文件。新建delphi工程,将默认窗体的name属性设为“client”。加入tnmudp控件,name属性设为“cudp”;localport属性设为“1111”,让控件cudp监视受控机的1111端口,当有数据发送到该口时,触发控件cudp的ondatareceived事件;remoteport属性设为“2222”,当控件cudp发送数据时,将数据发到主控机的2222口。 在implementation后面加入变量定义 const bufsize=2048;{ 发送每一笔数据的缓冲区大小 } var bmpstream:tmemorystream; leftsize:longint;{ 发送每一笔数据后剩余的字节数 } 为client的oncreate事件添加代码: procedure tclient.formcreate(sender: tobject); begin bmpstream:=tmemorystream.create; end; 为client的ondestroy事件添加代码: procedure tclient.formdestroy(sender: tobject); begin bmpstream.free; end; 为控件cudp的ondatareceived事件添加代码: procedure tclient.cudpdatareceived(sender: tcomponent; numberbytes: integer; fromip: string); var ctrlcode:array[0..29] of char; buf:array[0..bufsize-1] of char; tmpstr:string; sendsize,leftpos,toppos,rightpos,bottompos:integer; begin cudp.readbuffer(ctrlcode,numberbytes);{ 读取控制码 } if ctrlcode[0]+ctrlcode[1]+ctrlcode[2]+ctrlcode[3]='show' then begin { 控制码前4位为“show”表示主控机发出了抓屏指令 } if bmpstream.size=0 then { 没有数据可发,必须截屏生成数据 } begin tmpstr:=strpas(ctrlcode); tmpstr:=copy(tmpstr,5,length(tmpstr)-4); leftpos:=strtoint(copy(tmpstr,1,pos(':',tmpstr)-1)); tmpstr:=copy(tmpstr,pos(':',tmpstr)+1,length(tmpstr) -pos(':',tmpstr)); toppos:=strtoint(copy(tmpstr,1,pos(':',tmpstr)-1)); tmpstr:=copy(tmpstr,pos(':',tmpstr)+1,length(tmpstr)- pos(':',tmpstr)); rightpos:=strtoint(copy(tmpstr,1,pos(':',tmpstr)-1)); bottompos:=strtoint(copy(tmpstr,pos(':',tmpstr )+1,length(tmpstr)-pos(':',tmpstr))); screencap(leftpos,toppos,rightpos,bottompos); { 截取屏幕 } end; if leftsize>bufsize then sendsize:=bufsize else sendsize:=leftsize; bmpstream.readbuffer(buf,sendsize); leftsize:=leftsize-sendsize; if leftsize=0 then bmpstream.clear;{ 清空流 } cudp.remotehost:=fromip; { fromip为主控机ip地址 } cudp.sendbuffer(buf,sendsize); { 将数据发到主控机的2222口 } end; end; 其中screencap是自定义函数,截取屏幕指定区域, 代码如下: procedure tclient.screencap(leftpos,toppos, rightpos,bottompos:integer); var rectwidth,rectheight:integer; sourcedc,destdc,bhandle:integer; bitmap:tbitmap; begin rectwidth:=rightpos-leftpos; rectheight:=bottompos-toppos; sourcedc:=createdc('display','','',nil); destdc:=createcompatibledc(sourcedc); bhandle:=createcompatiblebitmap(sourcedc, rectwidth,rectheight); selectobject(destdc,bhandle); bitblt(destdc,0,0,rectwidth,rectheight,sourcedc, leftpos,toppos,srccopy); bitmap:=tbitmap.create; bitmap.handle:=bhandle; bitmap.savetostream(bmpstream); bmpstream.position:=0; leftsize:=bmpstream.size; bitmap.free; deletedc(destdc); releasedc(bhandle,sourcedc); end; 存为“c:\vclient\clnunit.pas”和“c:\vclient\vclient.dpr”, 并编译。 第二步,编制vserver.exe文件。新建delphi工程,将窗体的name属性设为“server”。加入tnmudp控件,name属性设为“sudp”;localport属性设为“2222”,让控件sudp监视主控机的2222端口,当有数据发送到该口时,触发控件sudp的ondatareceived事件;remoteport属性设为“1111”,当控件sudp发送数据时,将数据发到受控机的1111口。加入控件image1,align属性设为“alclient”;加入控件button1,caption属性设为“截屏”;加入控件label1,caption属性设为“左:上:右:下”;加入控件edit1,text属性设为“0:0:100:100”;加入控件label2,caption属性设为“受控机ip地址”;加入控件edit2,text属性设为“127.0.0.1”; 在implementation后面加入变量定义 const bufsize=2048; var rsltstream,tmpstream:tmemorystream; 为server的oncreate事件添加代码: procedure tserver.formcreate(sender: tobject); begin rsltstream:=tmemorystream.create; tmpstream:=tmemorystream.create; end; 为client的ondestroy事件添加代码: procedure tserver.formdestroy(sender: tobject); begin rsltstream.free; tmpstream.free; end; 为控件button1的onclick事件添加代码: procedure tserver.button1click(sender: tobject); var reqcode:array[0..29] of char;reqcodestr:string; begin reqcodestr:='show'+edit1.text; strpcopy(reqcode,reqcodestr); tmpstream.clear; rsltstream.clear; sudp.remotehost:=edit2.text; sudp.sendbuffer(reqcode,30); end; 为控件sudp的ondatareceived事件添加代码: procedure tserver.sudpdatareceived(sender: tcomponent; numberbytes: integer; fromip: string); var reqcode:array[0..29] of char;reqcodestr:string; begin reqcodestr:='show'+edit1.text; strpcopy(reqcode,reqcodestr); sudp.readstream(tmpstream); rsltstream.copyfrom(tmpstream,numberbytes); if numberbytes< bufsize then { 数据已读完 } begin rsltstream.position:=0; image1.picture.bitmap.loadfromstream(rsltstream); tmpstream.clear; rsltstream.clear; end else begin tmpstream.clear; reqcode:='show'; sudp.remotehost:=edit2.text; sudp.sendbuffer(reqcode,30); end; end; 存为“c:\vserver\svrunit.pas”和 “c:\vserver\vserver.dpr”,并编译。 四、测试。 1、本地机测试:在本地机同时运行vserver.exe和vclient.exe,利用程序的默认设置,即可实现截屏。查看“控制面板”-“网络”-“tcp/ip”-“ip地址”,将程序的“客户ip地址”设为该地址 ,同样正常运行。 2、远程测试:选一台受控机,运行vclient.exe;另选一台主控机,运行vserver.exe,将“受控机ip地址”即edit2的内容设为受控机的ip地址,“截屏”即可。以上简要介绍了远程屏幕抓取的实现方法,至于在主控机上一屏同时监视多个受控机,读者可自行完善。以上程序,在windows98对等网、delphi 4.0下调试通过。 |