随着互联网技术的不断发展,远程桌面技术已经逐渐成为日常工作和生活中不可或缺的一部分。传统的VNC技术虽然稳定可靠,但在现代的Web环境中,希望能够有更加灵活和高效的解决方案。ThinVNC就是在这样的背景下应运而生的。它不仅继承了VNC技术的优点,还通过现代Web技术实现了全平台、全浏览器的支持。
ThinVNC并非传统意义上的VNC,因为它并没有实现AT&T的RFB协议。相反,它是构建在当今Web标准之上的,包括AJAX、JSON和HTML5。这使得ThinVNC能够无缝地集成到各种Web应用程序中,并且提供了开箱即用的跨浏览器和跨平台支持。
在第一部分中,将探讨如何实现屏幕捕获。通常的做法是捕获整个桌面,但在这里,将单独捕获每个窗口,应用裁剪区域,并保存单独的位图以便于后续的比较和差异提取。
首先,需要枚举所有可见的顶层窗口:
type
TWin = class(TObject)
private
Wnd : Hwnd;
Rect : TRect;
Pid : Cardinal;
public
constructor Create(AWnd:HWND;ARect:TRect;APid:Cardinal);
end;
function EnumWindowsProc(Wnd: HWnd; const obj:TList): Bool; export; stdcall;
var
ProcessId : Cardinal;
R,R1 : TRect;
Win : TWin;
begin
Result:=True;
GetWindowThreadProcessId(Wnd,ProcessId);
if IsWindowVisible(Wnd) and not IsIconic(wnd)then begin
GetWindowRect(Wnd,R);
IntersectRect(R1,R,Screen.DesktopRect);
if not IsRectEmpty(R1) then begin
win := TWin.Create(Wnd,R,ProcessId);
obj.Add(win);
end;
end;
end;
procedure GetProcessWindowList(WinList:TList);
begin
WinList.Clear;
EnumWindows(@EnumWindowsProc, Longint(WinList));
end;
希望保持一个窗口列表,包括它们的基本属性和位图,以便可以与新的窗口进行比较,并将差异发送给客户端。在这里将窗口列表合并到一个窗口镜像列表中:
type
TWindowMirror = class
private
FIndex : Integer;
FRgn : HRGN;
FHandle : THandle;
FBoundsRect : TRect;
FProcessId : Integer;
FImage : TBitmap;
FDiffStreamList : TList;
...
...
end;
接下来,将更新镜像列表:
procedure TMirrorManager.RefreshMirrorList(out OneMoved:Boolean);
procedure GetProcessWindowList(WinList:TList);
begin
WinList.Clear;
EnumWindows(@EnumWindowsProc, Longint(WinList));
end;
var
wl : TList;
n : Integer;
wm : TWindowMirror;
begin
OneMoved:=False;
wl := TList.Create;
try
// Enumerates top windows
GetProcessWindowList(wl);
try
for n := wl.Count - 1 downto 0 do begin
// Looks for a cached window
wm:=GetWindowMirror(FMirrorList,wl[n].Wnd);
if assigned(wm) then begin
if IsIconic(wl[n].Wnd) then
wm.SetBoundsRect(Rect(0,0,0,0))
else wm.SetBoundsRect(wl[n].Rect);
// Returns true when at least one window moved
OneMoved:=OneMoved or (DateTimeToTimeStamp(Now-wm.FMoved).time
现在,进行捕获:
function TWindowMirror.Capture(ANewImage:TBitmap): Boolean;
function BitBlt(DestDC: HDC; X, Y, Width, Height: Integer; SrcDC: HDC;
XSrc, YSrc: Integer; Rop: DWORD): BOOL;
begin
// Capture only visible regions
SelectClipRgn(DestDC,FRgn);
result:=Windows.BitBlt(DestDC, X, Y, Width, Height, SrcDC,
XSrc, YSrc, Rop);
SelectClipRgn(DestDC,0);
end;
var
DC : HDC;
RasterOp,ExStyle: DWORD;
begin
RasterOp := SRCCOPY;
ExStyle:=GetWindowLong(FHandle, GWL_EXSTYLE);
if (ExStyle and WS_EX_LAYERED) = WS_EX_LAYERED then
RasterOp := SRCCOPY or CAPTUREBLT;
DC := GetDCEx(FHandle,0,DCX_WINDOW or DCX_NORESETATTRS or DCX_CACHE);
try
Result:=BitBlt(ANewImage.Canvas.Handle,0,0,
Width(FBoundsRect),Height(FBoundsRect),DC,0,0, RasterOp)
finally
ReleaseDC(FHandle,DC);
end;
end;
现在已经捕获了所有可见区域,需要获取与之前捕获的位图的差异。通过循环遍历窗口,然后是它们的可见区域,最后计算发现位图差异的区域:
function TWindowMirror.CaptureDifferences(reset:boolean=false): Boolean;
....
begin
...
result:=Capture(TmpImage);
if result then begin
...
ra:=ExtractClippingRegions(Rect(0,0,TmpImage.Width,TmpImage.Height));
for n := 0 to Length(ra) - 1 do begin
ra2:=GetDiffRects(FImage,TmpImage,ra[n]);
for m := 0 to Length(ra2) - 1 do begin
Jpg := TJpegImage.Create;
...
CopyBmpToJpg(Jpg,TmpImage,ra2[m]);
FDiffStreamList.Add(TImagePart.Create(rbmp,'jpeg'));
Jpg.SaveToStream(FDiffStreamList[FDiffStreamList.Count-1].FStream);
...
Bitblt(FImage.Canvas.Handle,
ra2[m].Left,ra2[m].Top,Width(ra2[m]),Height(ra2[m]),
TmpImage.Canvas.handle, rbmp.Left,ra2[m].Top,SRCCOPY);
end;
end;
...
end;