在视频会议中,有一个场景是屏幕分享,屏幕分享的时候要预览屏幕和窗口的缩略图,然后选中对应缩略图的窗口进行分享,本篇博客主要记录一下在 Windows 平台下面获取缩略图的一些方法,如果你有更好的方法欢迎交流
二、获取和显示缩略图
方法一:使用 DwmUpdateThumbnailProperties 在指定窗口显示缩略图
可以参考即构科技的官方文档说明 ,他们注册缩略图的 API 也是需要将位置传进去,猜测他们使用的也是 DwmUpdateThumbnailProperties 方式显示缩略图的
客户端提供顶层窗口和显示区域,用来显示缩略图,SDK将会把源窗口缩略图绘制到对应区域
zego_windowthumbnail_register((ZegoWindowHandle)this->winId(), info[i].thumbnail_id, &rect)
具体的方法为,也就是告诉 dwm,让把缩略图给到你指定的位置,注意这里有一个点就是 render_wnd 必须是顶层窗口,不能是 child window
int ThumbnailImage::RegisterThumbnailImage(HWND capture_wnd, HWND render_wnd, RECT rect) {
HRESULT hr = S_OK;
HTHUMBNAIL thumbnail = NULL;
hr = DwmRegisterThumbnail(render_wnd, capture_wnd, &thumbnail);
if (FAILED(hr)) {
return -1;
DWM_THUMBNAIL_PROPERTIES dskThumbProps;
ZeroMemory(&dskThumbProps, sizeof(dskThumbProps));
dskThumbProps.dwFlags = DWM_TNP_SOURCECLIENTAREAONLY | DWM_TNP_VISIBLE | DWM_TNP_OPACITY | DWM_TNP_RECTDESTINATION;
dskThumbProps.fSourceClientAreaOnly = FALSE;
dskThumbProps.fVisible = TRUE;
dskThumbProps.opacity = 255;
dskThumbProps.rcDestination = rect;
hr = DwmUpdateThumbnailProperties(thumbnail, &dskThumbProps);
if (FAILED(hr)) {
DwmUnregisterThumbnail(thumbnail);
return -1;
int id = reinterpret_cast<int>(thumbnail);
thumbnail_id_map_[id] = 1;
return id;
显示结果如下:
这里获取到的窗口都是实时显示的,窗口动,对应的缩略图也会动
这里的问题就是,我这边这能显示 9 个窗口,多了就不知道怎么显示了,还有就是我使用的是 ::FindWindow("Progman", "Program Manager"); 获取的桌面窗口句柄,但是如果有多个屏幕的话这里就不知道怎么去获取了其它屏幕的窗口句柄了
方法二,使用 BitBlt 或者是 PrintWindow 获取缩略图,然后使用 DuiLib::COptionUI 显示到指定位置1
1、使用 BitBlt 或者 PrintWindow 获取到对应的 HBITMAP
int32_t width = source.rect.right - source.rect.left;
int32_t height = source.rect.bottom - source.rect.top;
BITMAPINFO bi = { 0 };
BITMAPINFOHEADER *bih = &bi.bmiHeader;
bih->biSize = sizeof(BITMAPINFOHEADER);
bih->biBitCount = 32;
bih->biWidth = width;
bih->biHeight = -height;
bih->biPlanes = 1;
// HDC dest_hdc = CreateCompatibleDC(NULL);
BYTE* bits = nullptr;
HBITMAP hbitmap = CreateDIBSection(dest_hdc_, &bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
if (!hbitmap || bits == nullptr) {
std::cout << "CreateDIBSection Failed" << std::endl;
return "";
HGDIOBJ gdiobj = SelectObject(dest_hdc_, hbitmap);
HDC source_hdc = GetDC(NULL);
BOOL ret = BitBlt(dest_hdc_, 0, 0, width, height, source_hdc, source.rect.left, source.rect.top, SRCCOPY);
ReleaseDC(NULL, source_hdc);
2、将 HBITMAP 转成 png 格式的数据传出去,这里使用了 GDI+
std::string GetWindowPNGImage(HBITMAP hbitmap, int width, int height) {
ULONG_PTR m_token;
Gdiplus::GdiplusStartupInput input;
Gdiplus::GdiplusStartupOutput output;
Gdiplus::Status status = GdiplusStartup(&m_token, &input, &output);
Gdiplus::Bitmap* bitmap = NULL;
bitmap = Gdiplus::Bitmap::FromHBITMAP(hbitmap, NULL);
if (bitmap == nullptr) {
DWORD error = GetLastError();
std::cout << "bitmap is nullptr: " << error << std::endl;
return "";
Gdiplus::Bitmap* scale_bitmap = ScaleBitmap(bitmap, width, height);
if (bitmap) {
delete bitmap;
bitmap = nullptr;
if (!scale_bitmap) {
return "";
IStream *stream = nullptr;
HRESULT hRe = CreateStreamOnHGlobal(NULL, TRUE, &stream);
CLSID clsidPNG;
CLSIDFromString(L"{557cf406-1a04-11d3-9a73-0000f81ef32e}", &clsidPNG);
scale_bitmap->Save(stream, &clsidPNG);
HGLOBAL hg = NULL;
GetHGlobalFromStream(stream, &hg);
std::vector<CHAR> data;
SIZE_T size = GlobalSize(hg);
data.resize(size);
LPVOID ps = GlobalLock(hg);
memcpy(&data[0], ps, size);
GlobalUnlock(hg);
std::string thumbnail_image(data.begin(), data.end());
if (scale_bitmap) {
delete scale_bitmap;
scale_bitmap = nullptr;
Gdiplus::GdiplusShutdown(m_token);
return thumbnail_image;
3、上面代码中有一个点是将图片缩小到指定的位置,因为只是显示缩略图,大小不用太大
Gdiplus::Bitmap* ScaleBitmap(Gdiplus::Bitmap * pBitmap, UINT nWidth, UINT nHeight) {
Gdiplus::Bitmap * pTemp = new Gdiplus::Bitmap(nWidth, nHeight, pBitmap->GetPixelFormat());
if (pTemp) {
Gdiplus::Graphics* graphics = Gdiplus::Graphics::FromImage(pTemp);
if (graphics) {
// use the best interpolation mode
graphics->SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
graphics->DrawImage(pBitmap, 0, 0, nWidth, nHeight);
delete graphics;
return pTemp;
4、结果显示如下图所示
滚动条可以滚动查看其它的画面