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


    远程控制编写之屏幕传输  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



  • 相关阅读:
    CocosIDE导出Android APK的注意事项
    C++14尝鲜:Generic Lambdas(泛型lambda)
    silverlight调用WebService传递json接收绑定数据
    解决考试系统高并发数据载入不对问题
    汇编入门学习笔记 (九)—— call和ret
    Java SerialPort SDK
    how tomcat works 总结 二
    linux下多线程的调试
    垃圾回收GC:.Net自己主动内存管理 上(二)内存算法
    HDU-4973-A simple simulation problem.(线段树)
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4500522.html
Copyright © 2020-2023  润新知