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

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

时间: 2024-10-08 11:21:14

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

再来一篇装逼老文章:屏幕传输算法

仍然是以前写的一篇老文章,从其它站点拷贝回来的.此文写于07年,思想幼稚,特别是后期说教味道特别严重,仅供参考.另外,从Vista后,实际上操作系统已经在应用层提供了一个类似Mirro的接口,程序员已经没必要再自己进行变化判断了.============================================ 引用页:http://hi.baidu.com/cxmlqkoyadbekqd/item/4f3a361bdf32d28d89a9561b 老陈-为什么黑洞远程控制的屏幕传输算法是

MFC中显示 .bmp格式的位图

最近在看VisualC++ 图像处理的书籍,表示一直在从基础做起,今天就记录一个简单功能的实现,显示.bmp格式的位图. 首先需要理解的是窗口创建的过程包括两个步骤:首先擦除窗口的背景,然后在对窗口进行重新绘制. 一般而言,对于单文档或多文档的MFC程序,显示图像的代码要放在OnDraw函数之中.刚刚说过,窗口重绘时,要先将窗口的背景擦除,也就是发送WM_ERASEBKGND消息,然后用OnEraseBkgnd()函数处理这个消息,所以我们的显示图像的代码也可以放在这个函数之中.当然,这里只是为

MFC对话框显示BMP图片

1.MFC对话框显示BMP图片我们先从简单的开始吧.先分一个类: (一) 非动态显示图片(即图片先通过资源管理器载入,有一个固定ID) (二) 动态载入图片(即只需要在程序中指定图片的路径即可载入) 为方便说明,我们已经建好一个基于对话框的工程,名为Ttest. 对话框类为CTestDlg (一)    非动态载入图片. 方法1.先从最简单的开始,用picture 控件来实现. 步骤: 先在资源里Import一张图片,ID为IDB_BITMAP2 然后在对话框上添加一个picture控件,右键点

【OpenCV2】MFC中用OPENCV如何将图片显示在指定位置?

在opencv在开发过程中,我想最频繁的操作(之一)莫过于显示图片了. 学过OpenCV的同学都知道,调用cv::imshow()(或者C版本的ShowImage())可以轻而易举的将图片显示出来. 但是,它会创建一个不这么协调的对话框来显示这个图片,那么在MFC中我们如何让图片显示在指定的位置上呢? 先给个结果图(我使用的是VS2010+OpenCV2.4.8): 第一步: 创建一个基于MFC的基本对话框框架了.配置OpenCV环境 第二步:(如果是使用2.2版本以下的跳过) 我们要使用一个非

再谈MFC学习——模态对话框的数据传递

学习c++的目的就是为了使用封装好的类,而最最经典的封装类当然是微软的MFC窗口程序类.学习MFC编程之前要学习c++的主要知识,掌握c++的基本编程思想.下面就看下我学习的MFC模态对话框的数据传递. 首先,什么叫做模态对话框呢?模态对话框指的是当弹出这样的对话框的时候,程序不能相应除了此对话框外其他的窗口的响应,直到关闭了这个对话框,程序才能响应其他的对话框的响应.非模态对话框即使弹出也能响应程序其他的窗口而不需要等到此窗口的关闭.那么MFC中怎么才能弹出模态对话框呢?其实很简单,首先创建这

程序一 用记事本建立文件src.dat,其中存放若干字符。编写程序,从文件src.dat中读取数据,统计其中的大写字母、小写字母、数字、其它字符的个数,并将这些数据写入到文件test.dat中。

用记事本建立文件src.dat,其中存放若干字符.编写程序,从文件src.dat中读取数据,统计其中的大写字母.小写字母.数字.其它字符的个数,并将这些数据写入到文件test.dat中. #include<stdio.h>#include<stdlib.h>#include<string.h>int main(){ FILE*fp1,*fp2; char ch; int da=0,xiao=0,shuzi=0,qita=0; if((fp1=fopen("sr

WPF 获取元素(Visual)相对于屏幕设备的缩放比例,可用于清晰显示图片

原文:WPF 获取元素(Visual)相对于屏幕设备的缩放比例,可用于清晰显示图片 我们知道,在 WPF 中的坐标单位不是屏幕像素单位,所以如果需要知道某个控件的像素尺寸,以便做一些与屏幕像素尺寸相关的操作,就需要经过一些计算(例如得到屏幕的 DPI). 更繁琐的是,我们的控件可能外面有一些其他的控件做了 RenderTransform 进行了一些缩放,于是了解到屏幕像素单位就更不容易了. 本文将提供一套计算方法,帮助计算某个 WPF 控件相比于屏幕像素尺寸的缩放比例,用于进行屏幕像素级别的渲染

VC获取并修改计算机屏幕分辨率(MFC)

//检测当前分辨率 int Width = GetSystemMetrics(SM_CXSCREEN); int Height = GetSystemMetrics(SM_CYSCREEN); DEVMODE lpDevMode0; lpDevMode0.dmBitsPerPel=32; lpDevMode0.dmPelsWidth=Width; lpDevMode0.dmPelsHeight=Height; lpDevMode0.dmSize=sizeof(lpDevMode0); lpDev

Windows环境下Python v2.7.9,NotePad++编写程序中涉及中文,执行时CMD中显示乱码的解决办法

例如: 1.使用NotePad++,以默认的"以 UTF-8无 BOM 格式编码"编写: print "一加一等于二" 直接运行后CMD报错: 但是在IDLE中显示正常: 在文件第一行加入注释: # _*_coding:utf-8_*_ 运行后CMD显示乱码,IDLE依然正常: 在字符串前加u(Unicode编码): # _*_coding:utf-8_*_ print u"一加一等于二" 运行后正常,IDLE依然正常: 2. 使用NotePad