按理来说,本人不该发表此类专业文章,鄙人零星碎片化的开发经历,让本人斗胆向诸位网友,在远控方面做一点演示说明,谈论一点自己的认识。
程序工程代码地址:点击此处下载。
程序分为两个部分,控制端和被控端,他们之间通过网络来连接和交互,其工作过程大体如下:
被控端每隔20毫秒截屏,图像经过压缩,通过tcp网络传输给控制端,控制端对接收到的视频帧实时刷新显示;被控端实时接收控制端的对屏幕的操作消息(主要是键盘的按键、鼠标的位置和动作等),并在本端模拟这些键盘鼠标操作。
控制端代码主要在RemoteControlRecver.cpp和RemoteControlListener.cpp中,被控端代码主要在RemoteControlProc.cpp和RemoteControl.cpp中。
程序是自己对远控的一点探索和demo演示,实现过程吻合本文思路,虽然离商用有很远距离,但从实际的使用效果看,已经具备了远控的基础功能和效果。
经典的远控,比如国外的TeamViewer、国内近几年出现的ToDesk,功能强大且精密复杂,但这并不说明它的高不可攀(远控软件,除非算法上的突破,无论理论和工程技术,恐怕都无法为设计开发者赢得博士学位),它的原理无非就是:将被控端的屏幕实时复制到主控端并保持刷新,主控端就像使用本地的屏幕一样,使用菜单、键盘输入、鼠标点击等视觉交互,主控端在该屏幕上的键盘和鼠标操作,通过网络传输给被控端,转化为被控端相应的键盘和鼠标操作。
当然这是极为简略的描述,实践中要考虑很多其他因素,比如对网络流量的考虑:如果每一帧画面都是截屏数据,要保证动画的连贯逼真,每秒至少要传输24帧以上的画面,每一帧画面如果采用24位真彩色、屏幕分辨率假定是最常用的1920x1080,此时,未压缩前的大小是6220800字节,压缩后一般最少也有150-200kb左右(压缩率跟画面的像素有关,一般来讲,常见的图像压缩算法,越是像素排列无规则、相对速度运动越快、变化率越大的像素值,压缩比越低),这时每秒的带宽压力要达到80M/b(10MB)以上,有些网络环境下,这是一个恐怖的数字,实际环境可能达不到,因此,如何压缩减少视频传输流量,画面的高清显示、提高反应速度和控制的丝滑程度,是此类软件的核心技术之一。
一般来说,要实现此类软件的敏捷开发,最快捷的方式是使用第三方开发包,比如大名鼎鼎的ffmpeg,此开发包中有多种方案可以实现高效的视频传输,如h264、h265协议接口,此类接口可以将传输数据减少1到2个数量级,实际测试数据流量在每秒几百kb甚至100kb/s以下,已经可以满足实际需要,但是,这样的网络流量或者实际显示画面的综合效果并不够优秀,从测试中发现,微软自带的远程控制软件mstsc.exe,在100kb以内的网速下,画面显示依然清晰、控制依然保持流畅,这就不是第三方开发包可以轻易达到的。另外此类第三方接口中没有键盘鼠标消息的处理,很多定制化需求不能被满足,还需要对开发包进一步定制和开发。
如果对第三方开发包不太满意,那就只有自己动手手撸代码了。
魔鬼隐藏在细节中。从经验上来说,就算很简单的理论描述,工程实践中也会有很多细节需要填坑夯实,理学在前挖坑,工程学在后填坑,这也许就是工程学(比如软件工程)存在的意义吧。
下面就是对思路的细节描述。
主控端的具体代码逻辑如下:
每一个被控客户端的远程连接,控制端需要创建两个线程,一个负责与被控端网络通信,一个负责窗口显示刷新和窗口消息。
网络通信线程有两个执行节点,一个节点是执行recv函数,接收被控端的截屏数据;另一个执行节点是执行send函数,发送显示窗口的键盘鼠标消息。窗口显示和消息处理线程主要是实时刷新和显示被控端的截屏,切入点是依靠窗口的WM_PAINT消息,每当网络通信线程接收到一帧截屏后会调用InvalidateRect(参数是显示窗口的HWND句柄),此时窗口程序会执行刷新过程。此线程另外一个功能是,捕获主控端的在显示窗口中的键盘鼠标消息,并存放在全局变量中,这样网络通信线程就可以读取和发送给被控端,被控端模拟点击和键盘输入,将收到的键盘鼠标消息转换为本地的键盘鼠标操作。
另外需要注意的是,两个线程中,资源申请和释放、连接控制等主要是在显示刷新窗口线程中完成的,在主控和被控之间因各种原因断开连接时,要保证所有的资源都有效释放。
两个线程共用的客户端结构体如下:
被控端程序比较简单,主要是在一个循环中,获取截屏数据,发送给控制端,然后接收控制端的键盘鼠标消息,并将这些键盘鼠标消息转换为本地的键盘鼠标消息。
代码中的api采用了函数指针的调用方式,去掉前面的lp前缀就可以理解了。主要的功能模块如下:
1. 被控端截屏发送给控制端。从网上的资料来看,截屏功能的实现方法如下:
上面有几点需要啰嗦几句:
(1) windows上gdi二维图像api都是用DC句柄来实现的。测试发现,GetDC(0)等同于CreateDC("display",0, 0, 0),也等同于GetDC(GetDesktopWindow()),这几种用法都是用来获取桌面的DC。
(2) CreateCompatibleBitmap函数中的HDC要使用桌面的HDC,而不能是新创建的内存hdcmem,这是一个隐蔽的知识盲点,microsoft的解释如下:
大意是,CreateCompatibleBitmap产生的hBitmap位图中的位数和颜色跟使用的hdc参数中的保持一致,而使用CreateCompatibleDC函数创建的HDC默认都是2位的位图。
(3) GetDIBits函数有文档中未指明的知识盲点。比如lpbi参数指向的BITMAPINFO,在8位256色颜色模式下,要给调色板留下空间,调色板一般需要另外的1024字节大小的空间,否则调用此api会发生内存越界异常。另外,此函数如果不知道如何填写BITMAPINFO位图参数,可以在第一次调用时,lpData参数为空,调用后,函数会自动填充BITMAPINFO结构的参数,然后第二次调用此函数,即可得到相应参数的位图数据。
BITMAPINFO结构体定义如下:
该函数的官方文档如下:
注意这里的描述,如果lpvBits参数有效,那么前6个参数必须初始化,并且扫描线的数值必须是Dword对齐。前6个参数指的是biSizeImage之前的6个参数,biSizeImage的计算比较复杂,不论位图的颜色深度是多少位,扫描线长度必须要4字节对齐。测试中还发现扫描线的行数并不需要dword对齐。
文档中说,函数调用时hbitmap参数不能被SelectObject选中,测试中发现,hbitmap即使已经被调用了SelectObject函数被选中,调用时也可以成功。
截图支持8位、16位、24位、32位颜色值,测试程序使用的16位色。从视觉效果上,16位色跟24位,观看起来区别很小,特别是24位色(32位色相对于24位色只是增加了alpha值透明度),已经超过人的眼睛对颜色的识别程度的上限,再高的颜色值已经没有意义。由于传输的是像素值,而不是跟jpeg或者其他视频流算法中使用的近似压缩值(或者近似压缩块),所以画面的清晰度是很好的,这也是相比较于ffmpeg等第三方开发包使用h264、h265压缩视频流的优势。
另外,建议查看文档的英文版,中文版好多翻译不准确或者非常不严谨,长期依赖中文翻译,会导致开发水平得不到提高。
2. 数据压缩传输。采用zip压缩,压缩参数设置为最大化压缩,压缩比估值大概是6-20倍。如果是8位的位图帧,分辨率1920x1080,一帧压缩后大约是60-120KB;如果是16位,压缩后大约为100-300KB;32位的话,大约是150-600KB。当然这样的压缩比仍然难以满足实际需求,此文在第三个话题中会详细介绍如何减少视频流量,最终可以将网络流量降低到平均100KB/S。
3. 截屏帧的显示刷新。主要代码如下:
代码中,lpClientBitmap指向接收到的内存中的bmp文件,调用CreateDIBSection函数是为了创建一个类似于此bmp文件格式和参数的hbitmap句柄后,然后将bmp文件的像素值拷贝到句柄指向的像素内存地址中,并使用StretchBlt将像素值显示在当前窗口中的客户区中,因为客户端和服务器的窗口大小可能不一样,所以使用StretchBlt实现缩放而不是BitBlt函数。
同时要注意,CreateDIBSection函数第一个参数为0,0即相当于GetDC(0),代表桌面窗口的HDC,这点在官方文档中并未说明,但是可以直接使用。
4. 键盘鼠标消息。控制消息的收发和视频帧是顺序关系,而不是异步关系,被控端每发送一帧截屏后接收控制端的键盘鼠标消息,并将此消息模拟为本机的键盘鼠标操作;与此同时,控制端每接收一帧截屏后发送键盘鼠标消息。由于网络通信使用阻塞模式,此时一定要保证,被控端的先发送和后接收、控制端先接受再发送,主控和被控任何的执行分支都要分别执行这两对代码节点,否则会造成网络收发的死锁。另外,控制端如果发现,键盘鼠标的位置和动作跟上次的值相同,就会向被控端发送一个REMOTE_DUMMY_PACKET数据包,告诉被控端,控制端没有控制消息给你,你可以适当的增加截屏延时(一次增加10毫秒),以便减少网络消耗。主要的键盘鼠标结构体如下:
该结构由几个全局变量存放:键盘按键值,鼠标左键、中键、右键是否有点击动作,鼠标滚轮的滚动距离,鼠标的坐标位置等。控制端的窗口程序监听鼠标键盘消息,将这些消息填充为上述结构体,并由通信线程发送给被控端。
被控端通过keybd_event和mouse_event将收到的控制信息转换为本机的键盘鼠标消息。例子代码如下:
测试中发现,实际的网速有可能比想象中偏低,比如很多服务器网络带宽只有几百kb/s,上述依靠传输截屏帧的显示方式,按照40ms一帧的延时,8位位图数据帧,经过zip压缩后,网络流量可以平均减少10倍左右也就是大约1~2MB/s左右,依然无法满足实际需求,经过考虑,采用了如下几种改善措施:
此处第一个StretchBlt函数的作用是,将主控端显示窗口大小转化为适合被控端宽度高度的大小,原因是,被控端无法得知主控端显示窗口的大小,被控发送的像素值位置是对本窗口的偏移值,如果两边窗口大小不一致,那么被控端的像素位置值就失去了意义。此时通过StretchBlt函数转换后,就可以将像素值直接写入转换后的hbitmap,并再次调用StretchBlt函数,将客户端的窗口大小调整为主控端显示窗口的大小。此处也用到了SetDIBits和GetDIBits函数,该函数上边已经讲过了,功能比较强大,但是使用起来比较复杂。此处有个内存越界的bug,也就是像素的偏移值会大于整个截屏的像素个数总数,会导致WriteLog那一行的执行,原因应该是,两边的窗口大小不一致,若被控的窗口比较大,而主控端窗口比较小,主控端的缓冲区是按照主控窗口大小分配的,转换坐标后,有可能发生内存溢出。
像素值比对函数如下:
ScreenFrameChecker函数分8位、16位、24位、32位4种颜色深度值,跟上一帧比对截屏像素值,返回发生变化的像素值个数。为了加快速度,也可以按照每种规则,比如每次8个字节比对,或者使用simd指令优化,每次比对16字节。
typedef
struct
{
SOCKET hSockClient;
sockaddr_in stAddrClient;
HWND
hwndWindow;
char
* lpClientBitmap;
char
* dibits;
int
bufLimit;
int
lpbmpDataSize;
int
dataType;
UNIQUECLIENTSYMBOL unique;
STREMOTECONTROLPARAMS param;
}REMOTE_CONTROL_PARAM, * LPREMOTE_CONTROL_PARAM;
typedef
struct
{
SOCKET hSockClient;
sockaddr_in stAddrClient;
HWND
hwndWindow;
char
* lpClientBitmap;
char
* dibits;
int
bufLimit;
int
lpbmpDataSize;
int
dataType;
UNIQUECLIENTSYMBOL unique;
STREMOTECONTROLPARAMS param;
}REMOTE_CONTROL_PARAM, * LPREMOTE_CONTROL_PARAM;
int
GetScreenFrame(
int
ibits,
char
* szScreenDCName,
int
left,
int
top,
int
ScrnResolutionX,
int
ScrnResolutionY,
char
* lpBuf,
char
** lppixel,
int
* pixelsize) {
int
iRes = 0;
HWND
hwnd = lpGetDesktopWindow();
HDC
hdc = lpGetDC(hwnd);
if
(hdc == 0)
{
writeLog(
"GetScreenFrame lpCreateDCA error:%d\r\n"
, GetLastError());
return
FALSE;
}
HDC
hdcmem = lpCreateCompatibleDC(hdc);
HBITMAP
hbitmap = lpCreateCompatibleBitmap(hdc, ScrnResolutionX, ScrnResolutionY);
lpSelectObject(hdcmem, hbitmap);
iRes = lpBitBlt(hdcmem, 0, 0, ScrnResolutionX, ScrnResolutionY, hdc, 0, 0, SRCCOPY);
if
(hbitmap == 0)
{
lpReleaseDC(0, hdc);
lpDeleteDC(hdcmem);
lpDeleteObject(hbitmap);
writeLog(
"GetScreenFrame lpCreateCompatibleBitmap error:%d\r\n"
, GetLastError());
return
FALSE;
}
int
wbitcount = 0;
if
(ibits <= 1) {
wbitcount = 1;
}
else
if
(ibits <= 4) {
wbitcount = 4;
}
else
if
(ibits <= 8) {
wbitcount = 8;
}
else
if
(ibits <= 16) {
wbitcount = 16;
}
else
if
(ibits <= 24) {
wbitcount = 24;
}
else
{
wbitcount = 32;
}
DWORD
dwpalettesize = 0;
if
(wbitcount <= 8)
{
dwpalettesize = (1 << wbitcount) *
sizeof
(RGBQUAD);
}
DWORD
dwbmbitssize = ((ScrnResolutionX * wbitcount + 31) / 32) * 4 * ScrnResolutionY;
DWORD
dwBufSize = dwbmbitssize + dwpalettesize +
sizeof
(BITMAPINFOHEADER) +
sizeof
(BITMAPFILEHEADER);
LPBITMAPFILEHEADER bmfhdr = (LPBITMAPFILEHEADER)lpBuf;
bmfhdr->bfType = 0x4d42;
bmfhdr->bfSize = dwBufSize;
bmfhdr->bfReserved1 = 0;
bmfhdr->bfReserved2 = 0;
bmfhdr->bfOffBits = (
DWORD
)
sizeof
(BITMAPFILEHEADER) + (
DWORD
)
sizeof
(BITMAPINFOHEADER) + dwpalettesize;
LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)(lpBuf +
sizeof
(BITMAPFILEHEADER));
lpbi->biSize =
sizeof
(BITMAPINFOHEADER);
lpbi->biWidth = ScrnResolutionX;
lpbi->biHeight = ScrnResolutionY;
lpbi->biPlanes = 1;
lpbi->biBitCount = wbitcount;
lpbi->biCompression = BI_RGB;
lpbi->biSizeImage = 0;
lpbi->biXPelsPerMeter = 0;
lpbi->biYPelsPerMeter = 0;
lpbi->biClrUsed = 0;
lpbi->biClrImportant = 0;
char
* lpData = lpBuf +
sizeof
(BITMAPINFOHEADER) +
sizeof
(BITMAPFILEHEADER) + dwpalettesize;
iRes = lpGetDIBits(hdcmem, hbitmap, 0, ScrnResolutionY, lpData, (BITMAPINFO*)lpbi, DIB_RGB_COLORS);
lpDeleteDC(hdcmem);
lpDeleteObject(hbitmap);
lpReleaseDC(0, hdc);
if
(iRes == 0)
{
writeLog(
"lpGetDIBits error:%d\r\n"
, GetLastError());
return
FALSE;
}
*lppixel = lpData;
*pixelsize = dwbmbitssize;
return
dwBufSize;
}
int
GetScreenFrame(
int
ibits,
char
* szScreenDCName,
int
left,
int
top,
int
ScrnResolutionX,
int
ScrnResolutionY,
char
* lpBuf,
char
** lppixel,
int
* pixelsize) {
int
iRes = 0;
HWND
hwnd = lpGetDesktopWindow();
HDC
hdc = lpGetDC(hwnd);
if
(hdc == 0)
{
writeLog(
"GetScreenFrame lpCreateDCA error:%d\r\n"
, GetLastError());
return
FALSE;
}
HDC
hdcmem = lpCreateCompatibleDC(hdc);
HBITMAP
hbitmap = lpCreateCompatibleBitmap(hdc, ScrnResolutionX, ScrnResolutionY);
lpSelectObject(hdcmem, hbitmap);
iRes = lpBitBlt(hdcmem, 0, 0, ScrnResolutionX, ScrnResolutionY, hdc, 0, 0, SRCCOPY);
if
(hbitmap == 0)
{
lpReleaseDC(0, hdc);
lpDeleteDC(hdcmem);
lpDeleteObject(hbitmap);
writeLog(
"GetScreenFrame lpCreateCompatibleBitmap error:%d\r\n"
, GetLastError());
return
FALSE;
}
int
wbitcount = 0;
if
(ibits <= 1) {
wbitcount = 1;
}
else
if
(ibits <= 4) {
wbitcount = 4;
}
else
if
(ibits <= 8) {
wbitcount = 8;
}
else
if
(ibits <= 16) {
wbitcount = 16;
}
else
if
(ibits <= 24) {
wbitcount = 24;
}
else
{
wbitcount = 32;
}
DWORD
dwpalettesize = 0;
if
(wbitcount <= 8)
{
dwpalettesize = (1 << wbitcount) *
sizeof
(RGBQUAD);
}
DWORD
dwbmbitssize = ((ScrnResolutionX * wbitcount + 31) / 32) * 4 * ScrnResolutionY;
DWORD
dwBufSize = dwbmbitssize + dwpalettesize +
sizeof
(BITMAPINFOHEADER) +
sizeof
(BITMAPFILEHEADER);
LPBITMAPFILEHEADER bmfhdr = (LPBITMAPFILEHEADER)lpBuf;
bmfhdr->bfType = 0x4d42;
bmfhdr->bfSize = dwBufSize;
bmfhdr->bfReserved1 = 0;
bmfhdr->bfReserved2 = 0;
bmfhdr->bfOffBits = (
DWORD
)
sizeof
(BITMAPFILEHEADER) + (
DWORD
)
sizeof
(BITMAPINFOHEADER) + dwpalettesize;
LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)(lpBuf +
sizeof
(BITMAPFILEHEADER));
lpbi->biSize =
sizeof
(BITMAPINFOHEADER);
lpbi->biWidth = ScrnResolutionX;
lpbi->biHeight = ScrnResolutionY;
lpbi->biPlanes = 1;
lpbi->biBitCount = wbitcount;
lpbi->biCompression = BI_RGB;
lpbi->biSizeImage = 0;
lpbi->biXPelsPerMeter = 0;
lpbi->biYPelsPerMeter = 0;
lpbi->biClrUsed = 0;
lpbi->biClrImportant = 0;
char
* lpData = lpBuf +
sizeof
(BITMAPINFOHEADER) +
sizeof
(BITMAPFILEHEADER) + dwpalettesize;
iRes = lpGetDIBits(hdcmem, hbitmap, 0, ScrnResolutionY, lpData, (BITMAPINFO*)lpbi, DIB_RGB_COLORS);
lpDeleteDC(hdcmem);
lpDeleteObject(hbitmap);
lpReleaseDC(0, hdc);
if
(iRes == 0)
{
writeLog(
"lpGetDIBits error:%d\r\n"
, GetLastError());
return
FALSE;
}
*lppixel = lpData;
*pixelsize = dwbmbitssize;
return
dwBufSize;
}
typedef
struct
tagBITMAPINFOHEADER{
DWORD
biSize;
LONG
biWidth;
LONG
biHeight;
WORD
biPlanes;
WORD
biBitCount;
DWORD
biCompression;
DWORD
biSizeImage;
LONG
biXPelsPerMeter;
LONG
biYPelsPerMeter;
DWORD
biClrUsed;
DWORD
biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
typedef
struct
tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO, FAR *LPBITMAPINFO, *PBITMAPINFO;
typedef
struct
tagBITMAPINFOHEADER{
DWORD
biSize;
LONG
biWidth;
LONG
biHeight;
WORD
biPlanes;
WORD
biBitCount;
DWORD
biCompression;
DWORD
biSizeImage;
LONG
biXPelsPerMeter;
LONG
biYPelsPerMeter;
DWORD
biClrUsed;
DWORD
biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
typedef
struct
tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO, FAR *LPBITMAPINFO, *PBITMAPINFO;
else
if
(mapit->second->dataType == REMOTE_CLIENT_SCREEN)
{
char
* lpClientBitmap = mapit->second->lpClientBitmap;
HDC
hdcScr = CreateDCA(
"DISPLAY"
, NULL, NULL, NULL);
HDC
hdcSource = CreateCompatibleDC(hdcScr);
LPBITMAPFILEHEADER pBMFH = (LPBITMAPFILEHEADER)lpClientBitmap;
void
* pDibts = (
void
*)(lpClientBitmap + pBMFH->bfOffBits);
LPBITMAPINFOHEADER pBMIH = (LPBITMAPINFOHEADER)(lpClientBitmap +
sizeof
(BITMAPFILEHEADER));
DWORD
dwDibtsSize = ((pBMIH->biWidth * pBMIH->biBitCount + 31) / 32) * 4 * pBMIH->biHeight;
char
* pRemoteSrnData = 0;
HBITMAP
hRemoteBM = CreateDIBSection(0, (LPBITMAPINFO)pBMIH, DIB_RGB_COLORS, (
void
**)&pRemoteSrnData, 0, 0);
if
(hRemoteBM)
{
memcpy
(pRemoteSrnData, pDibts, dwDibtsSize);
HBITMAP
hSrcBM = (
HBITMAP
)SelectObject(hdcSource, hRemoteBM);
int
iX = pBMIH->biWidth;
int
iY = pBMIH->biHeight;
RECT stRect = { 0 };
int
iRet = GetClientRect(hWnd, &stRect);
iRet = StretchBlt(hdcDst, 0, 0, stRect.right - stRect.left, stRect.bottom - stRect.top, hdcSource, 0, 0, iX, iY, SRCCOPY);
DeleteObject(hSrcBM);
}
else
{
WriteLog(
"RemoteControl CreateDIBSection error:%u\r\n"
, GetLastError());
}
DeleteObject(hRemoteBM);
DeleteDC(hdcScr);
DeleteDC(hdcSource);
mapit->second->dataType = 0;
EndPaint(hWnd, &stPS);
return
TRUE;
}
else
if
(mapit->second->dataType == REMOTE_CLIENT_SCREEN)
{
char
* lpClientBitmap = mapit->second->lpClientBitmap;
HDC
hdcScr = CreateDCA(
"DISPLAY"
, NULL, NULL, NULL);
HDC
hdcSource = CreateCompatibleDC(hdcScr);
LPBITMAPFILEHEADER pBMFH = (LPBITMAPFILEHEADER)lpClientBitmap;
void
* pDibts = (
void
*)(lpClientBitmap + pBMFH->bfOffBits);
LPBITMAPINFOHEADER pBMIH = (LPBITMAPINFOHEADER)(lpClientBitmap +
sizeof
(BITMAPFILEHEADER));
DWORD
dwDibtsSize = ((pBMIH->biWidth * pBMIH->biBitCount + 31) / 32) * 4 * pBMIH->biHeight;
char
* pRemoteSrnData = 0;
HBITMAP
hRemoteBM = CreateDIBSection(0, (LPBITMAPINFO)pBMIH, DIB_RGB_COLORS, (
void
**)&pRemoteSrnData, 0, 0);
if
(hRemoteBM)
{
memcpy
(pRemoteSrnData, pDibts, dwDibtsSize);
HBITMAP
hSrcBM = (
HBITMAP
)SelectObject(hdcSource, hRemoteBM);
int
iX = pBMIH->biWidth;
int
iY = pBMIH->biHeight;
RECT stRect = { 0 };
int
iRet = GetClientRect(hWnd, &stRect);
iRet = StretchBlt(hdcDst, 0, 0, stRect.right - stRect.left, stRect.bottom - stRect.top, hdcSource, 0, 0, iX, iY, SRCCOPY);
DeleteObject(hSrcBM);
}
else
{
WriteLog(
"RemoteControl CreateDIBSection error:%u\r\n"
, GetLastError());
}
DeleteObject(hRemoteBM);
DeleteDC(hdcScr);
DeleteDC(hdcSource);
mapit->second->dataType = 0;
EndPaint(hWnd, &stPS);
return
TRUE;
}
typedef
struct
{
int
screenx;
int
screeny;
int
bitsperpix;
}STREMOTECONTROLPARAMS, * LPSTREMOTECONTROLPARAMS;
typedef
struct
{
POINT pos;
POINT size;
}REMOTECONTROLMOUSEPOS, * LPREMOTECONTROLMOUSEPOS;
typedef
struct
{
unsigned
char
key;
unsigned
char
shiftkey;
}REMOTECONTROLKEY, * LPREMOTECONTROLKEY;
typedef
struct
{
int
delta;
int
xy;
}REMOTECONTROLWHEEL, * LPREMOTECONTROLWHEEL;
typedef
struct
{
DWORD
dwType;
POINT stPT;
DWORD
dwTickCnt;
int
iDelta;
}STMOUSEACTION, * LPMOUSEACTION;
typedef
struct
{
int
screenx;
int
screeny;
int
bitsperpix;
}STREMOTECONTROLPARAMS, * LPSTREMOTECONTROLPARAMS;
typedef
struct
{
POINT pos;
POINT size;
}REMOTECONTROLMOUSEPOS, * LPREMOTECONTROLMOUSEPOS;
typedef
struct
{
unsigned
char
key;
unsigned
char
shiftkey;
}REMOTECONTROLKEY, * LPREMOTECONTROLKEY;
typedef
struct
{
int
delta;
int
xy;
}REMOTECONTROLWHEEL, * LPREMOTECONTROLWHEEL;
typedef
struct
{
DWORD
dwType;
POINT stPT;
DWORD
dwTickCnt;
int
iDelta;
}STMOUSEACTION, * LPMOUSEACTION;
if
(dwCommand == REMOTE_MOUSE_POS)
{
DWORD
dwDataSize = lphdr->packlen;
if
(dwDataSize =
sizeof
(POINT) +
sizeof
(POINT))
{
LPREMOTECONTROLPOS pos = (LPREMOTECONTROLPOS)(lpBuf +
sizeof
(NETWORKPACKETHEADER));
POINT stServerCurrent = pos->pos;
POINT stPtServerMax = pos->size;
int
iXLocalMax = ScrnResolutionX;
int
iYLocalMax = ScrnResolutionY;
POINT stPtLocalCurrent = { 0 };
if
(stPtServerMax.x != 0 && stPtServerMax.y != 0)
{
stPtLocalCurrent.x = (iXLocalMax * stServerCurrent.x) / stPtServerMax.x;
stPtLocalCurrent.y = (iYLocalMax * stServerCurrent.y) / stPtServerMax.y;
lpSetCursorPos(stPtLocalCurrent.x, stPtLocalCurrent.y);
}
}
actionInterval(&dwSleepTimeValue);
continue
;
}
else
if
(dwCommand == REMOTE_KEYBOARD)
{
if
(lphdr->packlen == 2)
{
LPREMOTECONTROLKEY lpkey = (LPREMOTECONTROLKEY)(lpBuf +
sizeof
(NETWORKPACKETHEADER));
unsigned
char
key = lpkey->key;
unsigned
char
keyshift = lpkey->shiftkey;
if
(keyshift)
{
keybd_event(VK_SHIFT, 0, 0, 0);
keybd_event(key, 0, 0, 0);
keybd_event(key, 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
}
else
{
keybd_event(key, 0, 0, 0);
keybd_event(key, 0, KEYEVENTF_KEYUP, 0);
}
}
actionInterval(&dwSleepTimeValue);
continue
;
}
else
if
(dwCommand == REMOTE_LEFTBUTTONDOWN || dwCommand == REMOTE_LEFTBUTTONDOUBLECLICK ||
dwCommand == REMOTE_RBUTTONDOWN || dwCommand == REMOTE_RBUTTONDOUBLECLICK)
{
DWORD
dwDataSize = lphdr->packlen;
if
(dwDataSize =
sizeof
(POINT) +
sizeof
(POINT))
{
LPREMOTECONTROLPOS pos = (LPREMOTECONTROLPOS)(lpBuf +
sizeof
(NETWORKPACKETHEADER));
POINT stServerCurrent = pos->pos;
POINT stPtServerMax = pos->size;
int
iXLocalMax = ScrnResolutionX;
int
iYLocalMax = ScrnResolutionY;
POINT stPtLocalCur = { 0 };
if
(stPtServerMax.x != 0 && stPtServerMax.y != 0)
{
stPtLocalCur.x = (iXLocalMax * stServerCurrent.x) / stPtServerMax.x;
stPtLocalCur.y = (iYLocalMax * stServerCurrent.y) / stPtServerMax.y;
lpSetCursorPos(stPtLocalCur.x, stPtLocalCur.y);
if
(dwCommand == REMOTE_LEFTBUTTONDOWN)
{
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
else
if
(dwCommand == REMOTE_RBUTTONDOWN)
{
mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
}
else
if
(dwCommand == REMOTE_RBUTTONDOUBLECLICK)
{
mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
lpSleep(0);
mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
}
else
if
(dwCommand == REMOTE_LEFTBUTTONDOUBLECLICK)
{
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
lpSleep(0);
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
}
}
actionInterval(&dwSleepTimeValue);
continue
;
}
else
if
(dwCommand == REMOTE_MOUSEWHEEL)
{
DWORD
dwDataSize = lphdr->packlen;
if
(dwDataSize =
sizeof
(
DWORD
) +
sizeof
(
DWORD
))
{
LPREMOTECONTROLWHEEL wheel = (LPREMOTECONTROLWHEEL)(lpBuf +
sizeof
(NETWORKPACKETHEADER));
int
key = wheel->delta & 0xffff;
int
delta = wheel->delta >> 16;
int
x = wheel->xy & 0xffff;
int
y = wheel->xy & 0xffff0000;
mouse_event(MOUSEEVENTF_WHEEL, x, y, delta, 0);
lpSleep(0);
}
actionInterval(&dwSleepTimeValue);
continue
;
}
else
if
(dwCommand == RECV_DATA_OK || dwCommand == REMOTE_DUMMY_PACKET)
{
freeInterval(&dwSleepTimeValue);
}
else
if
(dwCommand == REMOTECONTROL_END)
{
writeLog(
"remotecontrol shutdown by server\r\n"
);
break
;
}
else
{
writeLog(
"RemoteControlProc unrecognized command:%u\r\n"
, dwCommand);
}
checkTime(&dwSleepTimeValue);
if
(dwCommand == REMOTE_MOUSE_POS)
{
DWORD
dwDataSize = lphdr->packlen;
if
(dwDataSize =
sizeof
(POINT) +
sizeof
(POINT))
{
LPREMOTECONTROLPOS pos = (LPREMOTECONTROLPOS)(lpBuf +
sizeof
(NETWORKPACKETHEADER));
POINT stServerCurrent = pos->pos;
POINT stPtServerMax = pos->size;
int
iXLocalMax = ScrnResolutionX;
int
iYLocalMax = ScrnResolutionY;
POINT stPtLocalCurrent = { 0 };
if
(stPtServerMax.x != 0 && stPtServerMax.y != 0)
{
stPtLocalCurrent.x = (iXLocalMax * stServerCurrent.x) / stPtServerMax.x;
stPtLocalCurrent.y = (iYLocalMax * stServerCurrent.y) / stPtServerMax.y;
lpSetCursorPos(stPtLocalCurrent.x, stPtLocalCurrent.y);
}
}
actionInterval(&dwSleepTimeValue);
continue
;
}
else
if
(dwCommand == REMOTE_KEYBOARD)
{
if
(lphdr->packlen == 2)
{
LPREMOTECONTROLKEY lpkey = (LPREMOTECONTROLKEY)(lpBuf +
sizeof
(NETWORKPACKETHEADER));
unsigned
char
key = lpkey->key;
unsigned
char
keyshift = lpkey->shiftkey;
if
(keyshift)
{
keybd_event(VK_SHIFT, 0, 0, 0);
keybd_event(key, 0, 0, 0);
keybd_event(key, 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
}
else
{
keybd_event(key, 0, 0, 0);
keybd_event(key, 0, KEYEVENTF_KEYUP, 0);
}
}
actionInterval(&dwSleepTimeValue);
continue
;
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2023-6-26 18:16
被satadrover编辑
,原因: