博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
远程控制编写之屏幕传输 MFC实现 屏幕截图 发送bmp数据 显示bmp图像
阅读量:7247 次
发布时间:2019-06-29

本文共 7642 字,大约阅读时间需要 25 分钟。

远程控制编写之屏幕传输  MFC实现  屏幕截图 发送bmp数据 显示bmp图像:

一 :

首先要了解bmp图像的结构 详情请看我转载的一篇文章http://blog.csdn.net/hnust_xiehonghao/article/details/37656281

二: 被控端的代码 

  注意以下代码要放到一个线程中去 由于用到了while死循环 表示一直发送消息  直到对方关闭接收,发送失败后自己主动退出! 一定要放进线程

DWORD __stdcall SendScreen(LPVOID lparam)//线程处理屏幕传输{	DWORD *pParam   =   (DWORD   *)lparam; 	SOCKET MainSocket =*pParam; 	DWORD dwLastSend;	HWND hWnd = GetDesktopWindow();//获得屏幕的HWND.	HDC hScreenDC = GetDC(hWnd);   //获得屏幕的HDC.	HDC MemDC = CreateCompatibleDC(hScreenDC);	RECT rect; 	//该函数返回指定窗体的边框矩形的尺寸。该尺寸以相对于屏幕坐标左上角的屏幕坐标给出。	GetWindowRect(hWnd,&rect);	SIZE screensize;	screensize.cx=rect.right-rect.left;	screensize.cy=rect.bottom-rect.top;	//CreateCompatibleBitmap该函数创建与指定的设备hScreenDC环境相关的设备兼容的位图。	HBITMAP hBitmap = ::CreateCompatibleBitmap(hScreenDC,screensize.cx,screensize.cy); 	while(1)	{		dwLastSend = GetTickCount();		HGDIOBJ hOldBMP = ::SelectObject(MemDC,hBitmap);		//该函数对hScreenDc环境区域中的像素进行位块转换,以传送到目标设备MemDC环境。		::BitBlt(MemDC,0,0,screensize.cx,screensize.cy,hScreenDC,rect.left,rect.top,SRCCOPY);		::SelectObject(MemDC,hOldBMP);		/***************************************************************/		HDC hDC =::CreateDC("DISPLAY",NULL,NULL,NULL); 		int iBits = ::GetDeviceCaps(hDC, BITSPIXEL) * ::GetDeviceCaps(hDC, PLANES);//当前分辨率下每一个像素所占字节数  		::DeleteDC(hDC);		WORD   wBitCount;   //位图中每一个像素所占字节数    		if (iBits <= 1)			wBitCount = 1;		else if (iBits <= 4)			wBitCount = 4;		else if (iBits <= 8)			wBitCount = 8;		else if (iBits <= 24)			wBitCount = 24;		else			wBitCount = iBits;		DWORD   dwPaletteSize=0;	//调色板大小, 位图中像素字节大小 		if (wBitCount <= 8)					dwPaletteSize = (1 << wBitCount) *	sizeof(RGBQUAD);			BITMAP  bm;        //位图属性结构		::GetObject(hBitmap, sizeof(bm), (LPSTR)&bm);  		BITMAPINFOHEADER   bi;       //位图信息头结构     		bi.biSize            = sizeof(BITMAPINFOHEADER);  		bi.biWidth           = bm.bmWidth;		bi.biHeight          = bm.bmHeight;		bi.biPlanes          = 1;		bi.biBitCount        = wBitCount;		bi.biCompression     = BI_RGB; //BI_RGB表示位图没有压缩		bi.biSizeImage       = 0;		bi.biXPelsPerMeter   = 0;		bi.biYPelsPerMeter   = 0;		bi.biClrUsed         = 0;		bi.biClrImportant    = 0;		DWORD dwBmBitsSize = ((bm.bmWidth * wBitCount+31)/32) * 4 * bm.bmHeight;    		HANDLE hDib  = ::GlobalAlloc(GHND,dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));  //为位图内容分配内存		//HANDLE hDib  = ::GlobalAlloc(GHND,3686440*3);  //为位图内容分配内存		//锁定内存中指定的内存块,并返回一个地址值,令其指向内存块的起始处。除非用 GlobalUnlock 函数将内存块解锁,否则地址会一直保持有效。		LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);		*lpbi = bi;		HANDLE hPal = ::GetStockObject(DEFAULT_PALETTE);  // 处理调色板 		HANDLE  hOldPal=NULL; 		if (hPal)		{			hDC = ::GetDC(NULL);			hOldPal = SelectPalette(hDC,(HPALETTE)hPal, FALSE);			RealizePalette(hDC);		}		//将数据保存在指针(LPSTR)lpbi + sizeof(BITMAPINFOHEADER)+dwPaletteSize指向的位置		::GetDIBits(hDC, hBitmap, 0, (UINT) bm.bmHeight,(LPSTR)lpbi + sizeof(BITMAPINFOHEADER)+dwPaletteSize,(BITMAPINFO*)lpbi,DIB_RGB_COLORS);// 获取该调色板下新的像素值		if (hOldPal)//恢复调色板		{			SelectPalette(hDC, (HPALETTE)hOldPal, TRUE);			RealizePalette(hDC);			::ReleaseDC(NULL, hDC);		}			BITMAPFILEHEADER   bmfHdr; //位图文件头结构     		bmfHdr.bfType = 0x4D42;  // "BM"  	// 设置位图文件头		DWORD dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize;  		bmfHdr.bfSize = dwDIBSize;		bmfHdr.bfReserved1 = 0;		bmfHdr.bfReserved2 = 0;		bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize;		MsgHead MsgSend;		MsgSend.dwCmd = CMD_SCREEN_TO_SHOW;		MsgSend.dwSize = dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER);		MsgSend.dwExtend1 = bmfHdr.bfSize;		MsgSend.dwExtend2 = bmfHdr.bfOffBits;		if(!SendMsg(MainSocket, (char*)lpbi, &MsgSend))//自己定义函数 其内进行2次发送数据。一次用send发送MsgHead结构体 一次发送字符buffer此处为lpbi 		{//使用本代码时  把SendMsg换成你自己的发送函数 本人的函数先发送一个自己定义结构体再发送lpbi  分2次发送 故以下主控端分2次接受			::DeleteObject(MemDC);			::ReleaseDC(hWnd,hScreenDC);			GlobalUnlock(hDib);   //清除   			GlobalFree(hDib);			//::MessageBox(NULL, "发送失败","",MB_OK);			return 0;		}		/*		//将得到的屏幕截屏保存到E://mybitmap.bmp  		char strFilePath[111] = "E://mybitmap.bmp";		HANDLE hFile = CreateFile(strFilePath , GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);//创建位图文件   		DWORD dwWritten;		WriteFile(hFile, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);	// 写入位图文件头		WriteFile(hFile, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL);// 写入位图文件其余内容		*/		GlobalUnlock(hDib);   //清除   		GlobalFree(hDib);		//CloseHandle(hFile); 		if ((GetTickCount() - dwLastSend) < 110)			Sleep(100);	}	return 0;}
三  

主控端的代码:  

1 因为主控端也要一个循环进行一直接收屏幕消息  所以以下这个函数的代码一定也要放进一个线程中去!也能够让一个线程去调用处理函数 我採用的是另外一种 用线程去调用此函数!

void CScreenDlg::GetFirstScreen(){	MsgHead  MsgSend;	MsgSend.dwCmd = CMD_GETFIRST_SCREEN;	MsgSend.dwSize = 0;	if(!SendMsg(m_MainSocket, NULL, &MsgSend))//发出请求屏幕传输的要求	{		::MessageBox(NULL, "屏幕传输请求失败", "出错", MB_OK);		closesocket(m_MainSocket);		return ;	}	//下面为屏幕的获取, 一直获取并显示  直到接收不到  或设置计时器 一定时间后退出	DWORD dwLastSend;	MsgHead MsgRecv; 	 	while(m_MainSocket != INVALID_SOCKET)	{			if(!RecvData(m_MainSocket, (char *)&MsgRecv, sizeof(MsgHead)))//自己定义接受函数  其内是用的recv函数  如使用本代码 替换为recv函数就可以		{			::MessageBox(NULL, "屏幕数据接收,命令接收失败", "出错", MB_OK);			closesocket(m_MainSocket);			m_MainSocket = INVALID_SOCKET;			return ;		}		bmfHdr.bfType = 0x4D42;  // "BM"  	// 设置位图文件头 成员变量bmfHdr类型为 BITMAPFILEHEADER   		bmfHdr.bfSize = MsgRecv.dwExtend1;		bmfHdr.bfReserved1 = 0;		bmfHdr.bfReserved2 = 0;		bmfHdr.bfOffBits = MsgRecv.dwExtend2;		m_InfoSize = MsgRecv.dwExtend2 - sizeof(BITMAPFILEHEADER);//m_InfoSize为info信息头和调色板的大小的和		if(!RecvData(m_MainSocket,(char *)pData,MsgRecv.dwSize))//自己定义接受函数 使用时换成你自己的函数 或者recv函数就可以		{			::MessageBox(NULL, "屏幕数据接收,数据接收失败", "出错", MB_OK);			closesocket(m_MainSocket);			m_MainSocket = INVALID_SOCKET;			return ;			    		}	/*	//将bitmap数据写入文件里  创建一个bmp图像文件	    strcpy(strFilePath,"E://hehe.bmp");//strFilePath类型为char strFilePath[111] ;		if(hFile !=  INVALID_HANDLE_VALUE)  CloseHandle(hFile); //HANDLE hFile;  	    hFile = CreateFile(strFilePath , GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);//创建位图文件   		DWORD dwWritten;		WriteFile(hFile, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);	// 写入位图文件头		WriteFile(hFile, (LPSTR)pData, bmfHdr.bfSize, &dwWritten, NULL);// 写入位图文件其余内容		CloseHandle(hFile); 			*/		Invalidate(TRUE);//发送重绘系统消息		Sleep(10);	}}

上面的代码收到了bmp图像的信息保存到了pData中!

2 以下利用pData中的信息创建bmp图像的句柄

 

HBITMAP CScreenDlg::GetBitmapFromData(){	HBITMAP hBitmap;	PBITMAPINFO lpBmpInfo; //位图信息	lpBmpInfo = PBITMAPINFO(pData);	HDC hDC = CreateDC("DISPLAY", NULL, NULL, NULL);	// 创建DDB位图	hBitmap = CreateDIBitmap(		      hDC,			  &lpBmpInfo->bmiHeader,			  CBM_INIT,			  pData + m_InfoSize,			  lpBmpInfo,			  DIB_RGB_COLORS) ;	DeleteDC(hDC);	return hBitmap;}
关于DDB DIB 的差别 请參考本人博客MFC分栏    或者百度

3   以下为调用Invalidate后  重画图像     以下使用双缓冲技术   关于此技术參考以下文章中的第五条

http://blog.csdn.net/hnust_xiehonghao/article/details/37741307

以下的函数是在类向导中加入的消息响应函数  用来擦除窗体的

BOOL CScreenDlg::OnEraseBkgnd(CDC* pDC){	// TODO: 在此加入消息处理程序代码和/或调用默认值	//双缓存防止闪烁    CDC DCmem;	DCmem.CreateCompatibleDC(pDC);	CBitmap bitmap;	//下面也能够从一个文件里加载bitmap图像  如凝视中的语句	bitmap.m_hObject  = GetBitmapFromData();//(HBITMAP)LoadImage(AfxGetInstanceHandle(), strFilePath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);	CRect rect;	GetClientRect(&rect);	CBitmap *pOldBit=DCmem.SelectObject(&bitmap);    //DCmem.FillSolidRect(rect,pDC->GetBkColor());//按原来背景填充客户区,不然会是黑色	pDC->BitBlt(0,0,rect.Width(),rect.Height(),&DCmem,0,0,SRCCOPY);    DCmem.DeleteDC();     //删除DC    bitmap.DeleteObject(); //删除位图	//return CDialog::OnEraseBkgnd(pDC);	return TRUE;}

by hnust_xiehonghao

你可能感兴趣的文章
初探psutil
查看>>
yii2 中布局文件的 设置方法
查看>>
C语言+Modbus+NXP整体规划
查看>>
排序----归并排序
查看>>
vue二级联动select
查看>>
解析·NOIP·冷门 CLZ最小环
查看>>
创建节点--DOM树
查看>>
(KMP 根据循环节来计算)Period -- hdu -- 1358
查看>>
C++十进制到任意进制
查看>>
浏览器中 大部分API
查看>>
购物系统②
查看>>
MySQL聚集索引和非聚集索引
查看>>
javascript——事件兼容(部分)
查看>>
[模板] 容斥原理: 二项式反演 / Stirling 反演 / min-max 容斥 / 子集反演 / 莫比乌斯反演...
查看>>
jquery easyui 1.2.6 api documentation
查看>>
238. Product of Array Except Self
查看>>
Uva 10392 - Factoring Large Numbers
查看>>
内存泄漏处理
查看>>
Linux运维之——每日小技巧,获取网站请求数的前20个IP
查看>>
实现一个类型判断函数,需要鉴别出基本类型、function、null、NaN、数组、对象?...
查看>>