HTML5远程桌面 - 第一部分






4.55/5 (8投票s)
基于 AJAX、JSON 和 HTML5 构建的远程桌面软件
引言
VNC 和远程桌面软件已经存在很长时间了。毫无疑问,它们在大多数情况下都能很好地工作,但是,我想将旧的 VNC / 远程桌面概念带到 Web 上,以便实现完全的 Web 应用程序集成和开箱即用的跨浏览器跨平台支持。
尽管名为 ThinVNC,但它不是传统的 VNC,因为它没有实现 AT&T RFB 协议。相反,它是基于当今的网络标准构建的:AJAX、JSON 和 HTML5。
代码 (第一部分)
在本第一部分中,我们将看到用于进行屏幕捕获的代码。标准方法是捕获整个桌面。但是,在这种情况下,我们将单独捕获每个窗口,应用剪切区域并保存单个位图,以便稍后进行比较和提取差异。
首先,我们需要枚举所有可见的顶级窗口
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<TWin>): 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<TWin>);
begin
WinList.Clear;
EnumWindows(@EnumWindowsProc, Longint(WinList));
end;
我们希望保留一个窗口列表,其中包含它们的的基本属性和位图,以便可以与新的窗口进行比较,并将差异发送到客户端。在这里,我们将窗口列表合并到一个 TWindowMirror
列表中
TWindowMirror = class
private
FIndex : Integer;
FRgn : HRGN;
FHandle : THandle;
FBoundsRect : TRect;
FProcessId : Integer;
FImage : TBitmap;
FDiffStreamList : TList<TImagePart>;
...
...
end;
procedure TMirrorManager.RefreshMirrorList(out OneMoved:Boolean);
procedure GetProcessWindowList(WinList:TList<TWin>);
begin
WinList.Clear;
EnumWindows(@EnumWindowsProc, Longint(WinList));
end;
var
wl : TList<TWin>;
n : Integer;
wm : TWindowMirror;
begin
OneMoved:=False;
wl := TList<TWin>.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<MOVE_TIME);
end else begin
// Do not create a TWindowMirror for invisible windows
if IsIconic(wl[n].Wnd) then Continue;
wm:=TWindowMirror.Create(Self,wl[n].Wnd,wl[n].Rect, wl[n].pid);
FMirrorList.Add(wm);
end;
// Saves the zIndex
wm.FIndex:=wl.Count-n;
// Generates clipping regions
wm.GenRegions(wl,n);
end;
finally
ClearList(wl);
end;
// Sorts the mirror list by zIndex
FMirrorList.Sort;
finally
wl.free;
end;
end;
现在,我们进行捕获
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;
在下一篇文章中,我们将重点介绍与客户端的协议和客户端代码。包含更新源代码的文章可以在 这里 找到。
历史
ThinVNC 目前处于 alpha 阶段,此版本应被视为技术预览。远程鼠标输入已部分实现,远程键盘输入仍缺失。