4.2核心算法的实现由于整个系统基本使用MFC开发,而且涉及到很多方面,代码量比较大,不可能一一介绍,下面选择几个较为重要的核心功能的实现进行介绍。 4.2.1客户端和服务器端的通信无论是屏幕截图还是锁定屏幕,都是在服务器端管理员进行命令,在客户端进行响应。因此,之间涉及大量的网络通信。在整个系统中,网络通信至关重要,主要通过两个类实现:CClient和CServer。 class CServer { public: bool Check(char* name, char* pass); bool UnlockScreen(char* ip); bool LockScreen(char* ip); bool SendMsg(char* ip, char* msg); CString GetNextClientIP(); void PreEnum(); bool SnapScreen(char *ip, CxImage& x); void AliveAll(); void AddClient(const Client &client); void Run(); CServer() { InitializeCriticalSection(&cs); }; ~CServer() { DeleteCriticalSection(&cs); closesocket(srvsock); WSACleanup(); }; BOOL Init(int port);
private: static DWORD WINAPI ListenThreadPrc(LPVOID lpParam); SOCKET srvsock; SOCKADDR_IN srvaddr; vector<Client> clients; CRITICAL_SECTION cs; int pos; }; class CClient { private: SOCKET local; sockaddr_in remote_addr; int remote_addr_len; HWND hWnd; BOOL bLocked; public: BOOL bConnected; bool UnlockScreen(); bool LockSrceen(); static DWORD WINAPI ResponseThreadPrc(LPVOID lpParam); void Run(); CClient() { WSADATA wsaData; WSAStartup(MAKEWORD(2,2),&wsaData); remote_addr_len=sizeof(remote_addr); local=INVALID_SOCKET; bLocked = FALSE; bConnected = FALSE; };
~CClient() { closesocket(local); WSACleanup(); }; void Attach(HWND hWnd){this->hWnd=hWnd;}; bool Connect(char* ip, int port, char* name, char* pass); }; 服务器端有一个CServer的对象m_server,该对象创建后: m_server.Init(22221); //设置监听端口为22221 m_server.Run(); //进行监听 而如果服务器要进行某种操作,比如,锁定屏幕,则这样使用m_server: if(theApp.m_server.LockScreen(ip.GetBuffer(0))) { AfxMessageBox(ip+"屏幕已锁住"); } else { AfxMessageBox(ip+"屏幕未锁住"); } 在客户端,CClient有个对象m_client,该对象创建后: m_client.Attach(this->m_hWnd); //绑定屏幕锁定下可以操作的窗口 m_client.LockSrceen(); //锁定窗口 首先把自身窗口设置为屏幕锁定下可以操作的窗口,然后把屏幕锁定,等待用户登录。 在用户进行登录时: if(!m_client.bConnected) //若未连接 { if(m_client.Connect(m_strIP.GetBuffer(0), 20001, m_strName.GetBuffer(0), m_strPass.GetBuffer(0))) { //连接成功 TRACE(m_strName+"\r\n"); PostMessage( MYWM_SHOWAPPICONIC ); m_client.UnlockScreen(); //屏幕解除锁定 m_client.Run(); //开启监听线程 m_strInfo = "登录成功!"; } else { m_strInfo = "登录失败!"; } } else { m_strInfo = "重复登录!"; } 首先检查是否已经建立了连接,避免重复登录。然后调用Connect,连接向指定的服务器端,如果连接成功,屏幕解锁并开启监听线程,准备接收来自服务器的命令并进行响应。 4.2.2 屏幕截图屏幕截图的基本功能是通过两个函数来实现的:Bmp2Stream和Screen2Bitmap。正如他们的名字所示,Screen2Bitmap的作用是将屏幕截图,并保存到一个位图中,然后返回这个位图的句柄;而Bmp2Stream则是为了网络传输作准备,它接收一个位图句柄,对这个位图进行编码,并拷贝到一个缓冲区里。下面是这两个函数的具体实现: HBITMAP Screen2Bitmap() { HDC hscrdc, hmemdc;// 屏幕和内存设备描述表 HBITMAP hbitmap, holdbitmap;// 位图句柄 int nwidth, nheight;// 位图宽度和高度 int xscrn, yscrn;// 屏幕分辨率 //为屏幕创建设备描述表 hscrdc = CreateDC("display", NULL, NULL, NULL); //为屏幕设备描述表创建兼容的内存设备描述表 hmemdc = CreateCompatibleDC(hscrdc); // 获得屏幕分辨率 xscrn = GetDeviceCaps(hscrdc, HORZRES); yscrn = GetDeviceCaps(hscrdc, VERTRES);
nwidth = xscrn; nheight = yscrn;
// 创建一个与屏幕设备描述表兼容的位图 hbitmap = CreateCompatibleBitmap(hscrdc, nwidth, nheight);
// 把新位图选到内存设备描述表中 holdbitmap = (HBITMAP)SelectObject(hmemdc, hbitmap); // 把屏幕设备描述表拷贝到内存设备描述表中 BitBlt(hmemdc, 0, 0, nwidth, nheight, hscrdc, 0, 0, SRCCOPY); //得到屏幕位图的句柄 hbitmap = (HBITMAP)SelectObject(hmemdc, holdbitmap);
//清除 DeleteDC(hscrdc); DeleteDC(hmemdc);
// 返回位图句柄 return hbitmap; } BOOL Bmp2Stream( HBITMAP& hBmp, BYTE* &bBuf, long &size, DWORD type = CXIMAGE_FORMAT_JPG, int nWidth=0, int nHeight=0 ) { if(hBmp != INVALID_HANDLE_VALUE) { CxImage img; if(img.CreateFromHBITMAP(hBmp)) //位图句柄构建对象 { if(nWidth <= 0) { nWidth = img.GetWidth(); } if(nHeight <= 0) { nHeight = img.GetHeight(); } if(!img.Resample(nWidth, nHeight)) //缩放位图 { return FALSE; } if(img.Encode(bBuf, size, type)) //编码 return TRUE; } } return FALSE; } 补充一点,普通情况下,屏幕截图只能获得一幅位图,而这幅位图的大小一般都在1M以上,这不利于网络传输,因此,应该进行图像压缩,即图像格式转换。在本系统中,使用CxImage(CxImage类库是一个优秀的图像操作类库。它可以快捷地存取、显示、转换各种图像)这个开源项目提供的强大的库进行编码,将位图转化为Jpg格式再进行传输,这正是Bmp2Stream函数存在的目的。 |