前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >截全屏时如何过滤部分窗口

截全屏时如何过滤部分窗口

作者头像
gaigai
发布2019-11-24 00:51:08
4.1K7
发布2019-11-24 00:51:08
举报
文章被收录于专栏:Windows开发Windows开发

系统学习Windows客户端开发

在某些业务场景下希望截全屏时不显示某些窗口特别是自身应用的窗口,比如在屏幕共享时不希望将自己应用的主界面、工具条等共享给对方。

Windows有个特性Magnification(放大镜)特性,它允许将屏幕(或屏幕某个指定区域)进行放大,如果不设置放大比例等同于截屏,其支持选择窗口过滤,利用该特性就可以实现过滤部分窗口下截屏。该特性从Vista开始支持,如果产品需要支持Win XP系统就不能使用该方案。

使用Magnification进行截屏的流程如下:

笔者编写类CScreenCapture,用来实现过滤部分窗口截图,结合MSDN仔细阅读理解就容易掌握其使用。CScreenCapture类提供三个接口SetFilterWindowList()指定过滤窗口列表,SetFrameRate()指定每秒帧数,SetScreenImageArriveCallback()设置回调接收图片。内部开启一个UI线程定期执行截屏,线程创建运行使用 一个简单实用的线程基类CThreadBase,最后DEMO演示如何在接收图片回调中将其保存成BITMAP格式的图片。

类CScreenCapture头文件

代码语言:javascript
复制
#pragma once
#include <Magnification.h>
#include <vector>
#include <map>
#include "ThreadBase.h"

class IScreenImageArriveCallback
{
public:
    // called when screen was captured
    // param is same as MagImageScalingCallback
    virtual void OnScreenImageArriveCallback(MAGIMAGEHEADER srcheader, void *srcdata) = 0;
};

class CScreenCapturer : public CThreadBase
{
public:
    CScreenCapturer();
    ~CScreenCapturer();

public:
    // set the callback of screen image arrived
    void SetScreenImageArriveCallback(IScreenImageArriveCallback* pCallback);

    // set filter window list that will not show on the screen image
    void SetFilterWindowList(const std::vector<HWND>& vecFilterWindow);
    
    // frame rate represent times of capturing screen per second
    bool SetFrameRate(unsigned int nFrameRate);

protected:  // override CThreadBase
    virtual bool OnStart(const std::string& strParam) override;
    virtual void OnRun(const std::string& strParam) override;
    virtual void OnStop() override;

private:
    static BOOL MagImageScaling(HWND hwnd, void *srcdata, MAGIMAGEHEADER srcheader, void *destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty);
    static LRESULT CALLBACK MsgWndWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    void CaptureScreen();

private:
    CRITICAL_SECTION m_syncObject;
    DWORD m_nThreadId = 0;
    HWND m_hwndMagnification = NULL;
    RECT m_rectScreen;
    IScreenImageArriveCallback* m_pScreenImageArriveCallback = nullptr;
    HWND* m_pFilterWindows = nullptr;
    unsigned int m_nFilterWindowsCount = 0;
    unsigned int m_nFrameRate = 15;
};

类CScreenCapture源文件

代码语言:javascript
复制
#include "ScreenCapturer.h"

#pragma comment(lib, "Magnification.lib")

#define WINDOW_CLASS_NAME  L"magnification_host"
#define TIMER_ID_CAPTURE_SCREEN     100

CScreenCapturer::CScreenCapturer()
{
    InitializeCriticalSection(&m_syncObject);
}

CScreenCapturer::~CScreenCapturer()
{
    if (m_pFilterWindows)
    {
        delete[] m_pFilterWindows;
        m_pFilterWindows = nullptr;
    }

    DeleteCriticalSection(&m_syncObject);
}

void CScreenCapturer::SetScreenImageArriveCallback(IScreenImageArriveCallback* pCallback)
{
    m_pScreenImageArriveCallback = pCallback;
}

void CScreenCapturer::SetFilterWindowList(const std::vector<HWND>& vecFilterWindow)
{
    EnterCriticalSection(&m_syncObject);    
    m_nFilterWindowsCount = vecFilterWindow.size();
    if (m_pFilterWindows)
    {
        delete[] m_pFilterWindows;
        m_pFilterWindows = nullptr;
    }
    if (m_nFilterWindowsCount > 0)
    {
        m_pFilterWindows = new HWND[m_nFilterWindowsCount];
        for (unsigned int i = 0; i < m_nFilterWindowsCount; i++)
        {
            m_pFilterWindows[i] = vecFilterWindow[i];
        }
    }
    LeaveCriticalSection(&m_syncObject);
}

bool CScreenCapturer::SetFrameRate(unsigned int nFrameRate)
{
    if (nFrameRate == 0 || nFrameRate > 60)
    {
        return false;
    }

    m_nFrameRate = nFrameRate;
    return true;
}

bool CScreenCapturer::OnStart(const std::string& strParam)
{
    return true;
}

void CScreenCapturer::OnRun(const std::string& strParam)
{
    m_nThreadId = GetCurrentThreadId();

    // Init magnification
    if (!MagInitialize())
    {
        return;
    }    

    // Register magnification host window class
    WNDCLASS wc = { 0 };
    wc.lpszClassName = WINDOW_CLASS_NAME;
    wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpfnWndProc = MsgWndWindowProc;
    wc.hInstance = NULL;
    if (RegisterClass(&wc) == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
    {        
        return;
    }

    // Get screen resolution
    HWND hDesktop = ::GetDesktopWindow();
    ::GetWindowRect(hDesktop, &m_rectScreen);
    int nScreenX = m_rectScreen.right - m_rectScreen.left;
    int nScreenY = m_rectScreen.bottom - m_rectScreen.top;

    // Create host window
    HWND hHostWindow = CreateWindowEx(WS_EX_LAYERED, WINDOW_CLASS_NAME, L"", WS_POPUP, 0, 0, nScreenX, nScreenY, GetDesktopWindow(), 0, NULL, 0);
    if (hHostWindow == NULL)
    {        
        return;
    }
    SetLayeredWindowAttributes(hHostWindow, 0, 255, LWA_ALPHA);
    SetWindowLong(hHostWindow, GWL_USERDATA, (LONG_PTR)this);

    // Create magnification window
    m_hwndMagnification = CreateWindow(WC_MAGNIFIER, L"MagnifierWindow", WS_CHILD | WS_VISIBLE, 0, 0, nScreenX, nScreenY, hHostWindow, NULL, NULL, NULL);
    if (m_hwndMagnification == NULL)
    {        
        DestroyWindow(hHostWindow);
        return;
    }

    // Set capture callback
    if (!MagSetImageScalingCallback(m_hwndMagnification, (MagImageScalingCallback)&CScreenCapturer::MagImageScaling))
    {
        DestroyWindow(hHostWindow);
        return;
    }

    // Set timer to capture screen
    SetTimer(hHostWindow, TIMER_ID_CAPTURE_SCREEN, 1000/m_nFrameRate, nullptr);

    MSG msg;
    while (GetMessage(&msg, 0, 0, 0))
    {
        DispatchMessage(&msg);
    }

    KillTimer(hHostWindow, TIMER_ID_CAPTURE_SCREEN);
    DestroyWindow(hHostWindow);
    m_hwndMagnification = NULL;
}

void CScreenCapturer::OnStop()
{
    if (m_nThreadId > 0)
    {
        PostThreadMessage(m_nThreadId, WM_QUIT, 0, 0);
        m_nThreadId = 0;
    }
}

BOOL CScreenCapturer::MagImageScaling(HWND hwnd, void *srcdata, MAGIMAGEHEADER srcheader, void *destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty)
{
    HWND hParent = hwnd;
    while (true)
    {
        hParent = GetParent(hParent);
        if (hParent == NULL)
        {
            break;
        }

        wchar_t szClassName[200];
        memset(szClassName, 0, sizeof(szClassName));
        GetClassName(hParent, szClassName, 200);
        if (wcscmp(szClassName, WINDOW_CLASS_NAME) == 0)
        {
            break;
        }
    }
    if (hParent == NULL)
    {
        return TRUE;
    }
    
    CScreenCapturer* pScreenCapture = (CScreenCapturer*)GetWindowLong(hParent, GWL_USERDATA);
    if (pScreenCapture && pScreenCapture->m_pScreenImageArriveCallback)
    {
        pScreenCapture->m_pScreenImageArriveCallback->OnScreenImageArriveCallback(srcheader, srcdata);
    }

    return TRUE;
}

LRESULT CALLBACK CScreenCapturer::MsgWndWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if (message == WM_TIMER && wParam == TIMER_ID_CAPTURE_SCREEN)
    {        
        CScreenCapturer* pScreenCapture = (CScreenCapturer*)GetWindowLong(hWnd, GWL_USERDATA);
        if (pScreenCapture)
        {
            pScreenCapture->CaptureScreen();
        }
        
        return 0L;
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}

void CScreenCapturer::CaptureScreen()
{
    EnterCriticalSection(&m_syncObject);
    MagSetWindowFilterList(m_hwndMagnification, MW_FILTERMODE_EXCLUDE, m_nFilterWindowsCount, m_pFilterWindows);
    LeaveCriticalSection(&m_syncObject);

    MagSetWindowSource(m_hwndMagnification, m_rectScreen);
}

接收图片回调中保存成BITMAP格式的图片

代码语言:javascript
复制
void CMFCApplicationDlg::OnScreenImageArriveCallback(MAGIMAGEHEADER srcheader, void *srcdata)
{
    // construct bitmap
    BITMAPINFOHEADER bmif;
    bmif.biSize = sizeof(BITMAPINFOHEADER);
    bmif.biHeight = srcheader.height;
    bmif.biWidth = srcheader.width;
    bmif.biSizeImage = bmif.biWidth*bmif.biHeight * 4;
    bmif.biPlanes = 1;
    bmif.biBitCount = (WORD)(bmif.biSizeImage / bmif.biHeight / bmif.biWidth * 8);
    bmif.biCompression = BI_RGB;

    BITMAPFILEHEADER bmfh;
    LONG offBits = sizeof(BITMAPFILEHEADER) + bmif.biSize;
    bmfh.bfType = 0x4d42; // "BM"
    bmfh.bfOffBits = offBits;
    bmfh.bfSize = offBits + bmif.biSizeImage;
    bmfh.bfReserved1 = 0;
    bmfh.bfReserved2 = 0;

    // 返回的图片数据存储是从上到下,位图要求是从下到上,所以需要调整
    LPBYTE pData = (BYTE*)new BYTE[bmif.biSizeImage];
    memcpy(pData, (LPBYTE)srcdata+srcheader.offset, bmif.biSizeImage);
    LONG nLineSize = bmif.biWidth * bmif.biBitCount / 8;
    BYTE* pLineData = new BYTE[nLineSize];
    LONG nLineStartIndex = 0;
    LONG nLineEndIndex = bmif.biHeight - 1;
    while (nLineStartIndex < nLineEndIndex)
    {
        BYTE* pStart = pData + (nLineStartIndex * nLineSize);
        BYTE* pEnd = pData + (nLineEndIndex * nLineSize);
        memcpy(pLineData, pStart, nLineSize);
        memcpy(pStart, pEnd, nLineSize);
        memcpy(pEnd, pLineData, nLineSize);
        nLineStartIndex++;
        nLineEndIndex--;
    }
    delete[] pLineData;
    pLineData = nullptr;

    std::ofstream ofs(L"C:\\1.bmp", fstream::out|fstream::binary);
    if (ofs.is_open())
    {
        ofs.write((const char*)&bmfh, sizeof(BITMAPFILEHEADER));
        ofs.write((const char*)&bmif, sizeof(BITMAPINFOHEADER));
        ofs.write((const char*)pData, bmif.biSizeImage);
        ofs.close();
    }
    
    delete[] pData;
    pData = nullptr;
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Windows开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 系统学习Windows客户端开发
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档