远程控制项目第四天 功能实现

发送屏幕内容

代码详解

1. 创建 CImage 对象并获取屏幕内容

首先,我们创建一个 CImage 对象,用于接收屏幕上的内容。要获取屏幕内容,我们需要先获取当前设备上下文(DC)。调用 ::GetDC(NULL) 函数,参数 NULL 表示我们要获取整个屏幕的设备上下文。

cpp 复制代码
CImage screen;
HDC hScreen = ::GetDC(NULL);
int nBitPerPixel = GetDeviceCaps(hScreen, BITSPIXEL);  // 获取屏幕颜色深度
int nWidth = GetDeviceCaps(hScreen, HORZRES);  // 获取屏幕宽度
int nHeight = GetDeviceCaps(hScreen, VERTRES);  // 获取屏幕高度
screen.Create(nWidth, nHeight, nBitPerPixel);  // 创建 CImage 对象

2. 使用 BitBlt 获取屏幕内容

通过 BitBlt 函数,将当前屏幕的内容复制到 CImage 对象上。BitBlt 的本质就是将源图像的一部分搬运到目标图像的指定位置。

cpp 复制代码
CImage screen;
HDC hScreen = ::GetDC(NULL);
int nBitPerPixel = GetDeviceCaps(hScreen, BITSPIXEL);  // 获取屏幕颜色深度
int nWidth = GetDeviceCaps(hScreen, HORZRES);  // 获取屏幕宽度
int nHeight = GetDeviceCaps(hScreen, VERTRES);  // 获取屏幕高度
screen.Create(nWidth, nHeight, nBitPerPixel);  // 创建 CImage 对象

3. 释放设备上下文

完成屏幕内容复制后,我们需要释放设备上下文,避免内存泄露。调用 ReleaseDC 函数来释放资源。

cpp 复制代码
ReleaseDC(NULL, hScreen);  // 释放设备上下文

4. 创建全局内存块和流对象

接下来,我们创建一个空的全局内存块,使用 GlobalAlloc 分配内存。GMEM_MOVEABLE 标志表示这块内存是可移动的。然后,我们创建一个 IStream 流对象,使用 CreateStreamOnHGlobal 将全局内存块与流对象绑定。

cpp 复制代码
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, 0);  // 创建全局内存块
if (hMem == NULL) return -1;  // 内存分配失败

IStream* pStream = NULL;
HRESULT ret = CreateStreamOnHGlobal(hMem, TRUE, &pStream);  // 创建流对象
if (ret != S_OK) return -1;  // 创建失败

5. 将 CImage 数据保存到流对象

现在,我们可以通过 CImage::Save 方法将图像数据保存到流对象中。保存时,我们指定保存的格式(如 JPEG)。

cpp 复制代码
screen.Save(pStream, Gdiplus::ImageFormatJPEG);  // 将图像保存到流对象中

6. 流指针调整

调用 screen.Save 后,流指针已经指向了数据的末尾。如果不重置指针,接下来的读取操作可能会从流的末尾开始,这样读取到的数据可能为空。为了确保后续可以正确读取数据,我们需要通过 Seek 函数将流指针重置到开头。

cpp 复制代码
LARGE_INTEGER bg = {0};
pStream->Seek(bg, STREAM_SEEK_SET, NULL);  // 将流指针移回开头

7. 锁定内存块并读取数据

接下来,我们通过 GlobalLock 锁定全局内存块,获取指向内存的指针。这允许我们直接操作内存中的数据。在操作完数据后,我们使用 GlobalUnlock 解锁内存。

cpp 复制代码
PBYTE pData = (PBYTE)GlobalLock(hMem);  // 锁定内存并获取指针
SIZE_T nSize = GlobalSize(hMem);  // 获取内存大小

8. 发送数据

使用获取的内存数据,构造数据包并发送。发送完成后,解锁内存。

cpp 复制代码
CPacket packet(6, pData, nSize);
CServerSocket::getInstance()->Send(packet);  // 发送数据

GlobalUnlock(hMem);  // 解锁内存

9. 释放资源

最后,我们需要释放流对象、全局内存块以及 CImage 对象的设备上下文。资源的释放顺序应该是先释放流对象,再释放全局内存块,最后释放设备上下文。

cpp 复制代码
pStream->Release();  // 释放流对象
GlobalFree(hMem);  // 释放全局内存块
screen.ReleaseDC();  // 释放 CImage 对象的设备上下文

总结

  • 获取屏幕内容 :通过 GetDC 获取设备上下文,使用 BitBlt 将屏幕内容复制到 CImage 对象。
  • 流操作 :使用 GlobalAlloc 创建全局内存块,使用 CreateStreamOnHGlobal 将内存块和流绑定。使用 CImage::Save 保存数据到流对象。
  • 内存操作 :使用 GlobalLock 锁定内存,获取数据后调用 GlobalUnlock 解锁内存。注意流指针位置,要确保数据从流的起始位置读取。
  • 资源释放:确保按顺序释放资源:先释放流对象,再释放内存块,最后释放设备上下文。

这样就能确保屏幕截图数据能够被正确保存、读取和发送,同时避免资源泄露。

相关推荐
王老师青少年编程6 小时前
csp信奥赛C++高频考点专项训练之字符串 --【子串查找】:[NOIP 2009 提高组] 潜伏者
c++·字符串·csp·高频考点·信奥赛·子串查找·潜伏者
初願致夕霞6 小时前
基于系统调用的Linux网络编程——UDP与TCP
linux·网络·c++·tcp/ip·udp
小小de风呀8 小时前
de风——【从零开始学C++】(五):内存管理
开发语言·c++
CHANG_THE_WORLD9 小时前
C语言中的 %*s 和 %.*s 和C++的字符串格式化输出
c语言·c++·c#
螺丝钉的扭矩一瞬间产生高能蛋白10 小时前
QT的C++接口基础用法
c++·qt·嵌入式软件·嵌入式linux·linux应用
智者知已应修善业11 小时前
【51单片机模拟生日蜡烛】2023-10-10
c++·经验分享·笔记·算法·51单片机
智者知已应修善业11 小时前
【51单片机如何让LED灯从一亮到八,再从八亮到一】2023-10-13
c++·经验分享·笔记·算法·51单片机
qeen8711 小时前
【数据结构】二叉树相关经典函数C语言实现
c语言·数据结构·c++·笔记·学习·算法·二叉树
良木生香11 小时前
【C++初阶】STL——List从入门到应用完全指南(1)
开发语言·数据结构·c++·程序人生·算法·蓝桥杯·学习方法