发送屏幕内容
代码详解
1. 创建 CImage 对象并获取屏幕内容
首先,我们创建一个 CImage
对象,用于接收屏幕上的内容。要获取屏幕内容,我们需要先获取当前设备上下文(DC)。调用 ::GetDC(NULL)
函数,参数 NULL
表示我们要获取整个屏幕的设备上下文。
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
的本质就是将源图像的一部分搬运到目标图像的指定位置。
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
函数来释放资源。
ReleaseDC(NULL, hScreen); // 释放设备上下文
4. 创建全局内存块和流对象
接下来,我们创建一个空的全局内存块,使用 GlobalAlloc
分配内存。GMEM_MOVEABLE
标志表示这块内存是可移动的。然后,我们创建一个 IStream
流对象,使用 CreateStreamOnHGlobal
将全局内存块与流对象绑定。
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)。
screen.Save(pStream, Gdiplus::ImageFormatJPEG); // 将图像保存到流对象中
6. 流指针调整
调用 screen.Save
后,流指针已经指向了数据的末尾。如果不重置指针,接下来的读取操作可能会从流的末尾开始,这样读取到的数据可能为空。为了确保后续可以正确读取数据,我们需要通过 Seek
函数将流指针重置到开头。
LARGE_INTEGER bg = {0};
pStream->Seek(bg, STREAM_SEEK_SET, NULL); // 将流指针移回开头
7. 锁定内存块并读取数据
接下来,我们通过 GlobalLock
锁定全局内存块,获取指向内存的指针。这允许我们直接操作内存中的数据。在操作完数据后,我们使用 GlobalUnlock
解锁内存。
PBYTE pData = (PBYTE)GlobalLock(hMem); // 锁定内存并获取指针
SIZE_T nSize = GlobalSize(hMem); // 获取内存大小
8. 发送数据
使用获取的内存数据,构造数据包并发送。发送完成后,解锁内存。
CPacket packet(6, pData, nSize);
CServerSocket::getInstance()->Send(packet); // 发送数据GlobalUnlock(hMem); // 解锁内存
9. 释放资源
最后,我们需要释放流对象、全局内存块以及 CImage
对象的设备上下文。资源的释放顺序应该是先释放流对象,再释放全局内存块,最后释放设备上下文。
pStream->Release(); // 释放流对象
GlobalFree(hMem); // 释放全局内存块
screen.ReleaseDC(); // 释放 CImage 对象的设备上下文
总结
- 获取屏幕内容:通过
GetDC
获取设备上下文,使用BitBlt
将屏幕内容复制到CImage
对象。 - 流操作:使用
GlobalAlloc
创建全局内存块,使用CreateStreamOnHGlobal
将内存块和流绑定。使用CImage::Save
保存数据到流对象。 - 内存操作:使用
GlobalLock
锁定内存,获取数据后调用GlobalUnlock
解锁内存。注意流指针位置,要确保数据从流的起始位置读取。 - 资源释放:确保按顺序释放资源:先释放流对象,再释放内存块,最后释放设备上下文。
这样就能确保屏幕截图数据能够被正确保存、读取和发送,同时避免资源泄露。