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

来源:互联网 发布:销售数据统计好处 编辑:程序博客网 时间:2024/06/15 09:23

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



2 0
原创粉丝点击