zoukankan      html  css  js  c++  java
  • 远程控制编写之屏幕传输 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



  • 相关阅读:
    1024X768大图 (Wallpaper)
    (Mike Lynch)Application of linear weight neural networks to recognition of hand print characters
    瞬间模糊搜索1000万基本句型的语言算法
    单核与双核的竞争 INTEL P4 670对抗820
    FlashFTP工具的自动缓存服务器目录的功能
    LDAP over SSL (LDAPS) Certificate
    Restart the domain controller in Directory Services Restore Mode Remotely
    How do I install Active Directory on my Windows Server 2003 server?
    指针与指针变量(转)
    How to enable LDAP over SSL with a thirdparty certification authority
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4500522.html
Copyright © 2011-2022 走看看