难以创建连接到笔记本电脑的所有显示器的缩略图以填充列表控件

问题描述 投票:0回答:0

我有一些代码可以创建连接到 PC 的显示器的缩略图。它们被呈现为列表控件。

这是迭代监视器以创建缩略图的代码:

void CCenterCursorOnScreenDlg::DrawThumbnails()
{
    int monitorIndex = 0;

    // Stop redrawing the CListCtrl
    m_ListThumbnail.SetRedraw(FALSE);

    // Loop monitor info
    for (auto& strMonitor : m_monitors.strMonitorNames)
    {
        // Create the thumbnail image
        CImage monitorThumbnail;
        CreateMonitorThumbnail(monitorIndex, monitorThumbnail, true);

        // Convert it to a CBitmap
        CBitmap* pMonitorThumbnailBitmap = CBitmap::FromHandle(monitorThumbnail);

        // Add the CBitmap to the CImageList
        m_ImageListThumb.Add(pMonitorThumbnailBitmap, nullptr);

        // Build the caption description
        CString strMonitorDesc = m_monitors.strMonitorNames.at(monitorIndex);
        strMonitorDesc.AppendFormat(L" (Screen %d)", monitorIndex + 1);
        
        // Add the item to the CListCtrl
        m_ListThumbnail.InsertItem(monitorIndex, strMonitorDesc, monitorIndex);

        monitorIndex++;
    }

    // Start redrawing the CListCtrl again
    m_ListThumbnail.SetRedraw(TRUE);
}

m_monitors
变量是一个实例:

struct MonitorRects
{
    std::vector<RECT>   rcMonitors;
    std::vector<CString> strMonitorNames;

    static BOOL CALLBACK MonitorEnum(HMONITOR hMon, HDC hdc, LPRECT lprcMonitor, LPARAM pData)
    {
        MonitorRects* pThis = reinterpret_cast<MonitorRects*>(pData);
        pThis->rcMonitors.push_back(*lprcMonitor);

        MONITORINFOEX sMI{};
        sMI.cbSize = sizeof(MONITORINFOEX);
        GetMonitorInfo(hMon, &sMI);

        pThis->strMonitorNames.emplace_back(sMI.szDevice);

        return TRUE;
    }

    MonitorRects()
    {
        EnumDisplayMonitors(nullptr, nullptr, MonitorEnum, (LPARAM)this);
    }
};

初始缩略图大小在

OnInitDialog
中确定:

    // Use monitor 1 to work out the thumbnail sizes
    CRect rcMonitor = m_monitors.rcMonitors.at(0);

    m_iThumbnailWidth = rcList.Width();

    double dHt = ((double)rcMonitor.Height() / (double)rcMonitor.Width()) * (double)m_iThumbnailWidth;
    m_iThumbnailHeight = (int)dHt;

创建

CImageList
以显示图像时使用这些值。

最后,我有了制作缩略图的功能:

bool CCenterCursorOnScreenDlg::CreateMonitorThumbnail(const int iMonitorIndex, CImage &rImage, bool bResizeAsThumbnail)
{
    const CRect rcCapture = m_monitors.rcMonitors.at(iMonitorIndex);

    // Destroy the currently contained bitmap to create a new one
    rImage.Destroy();

    // Massage the dimensions as we want a thumbnail
    auto nWidth = rcCapture.Width();
    auto nHeight = rcCapture.Height();
    if (bResizeAsThumbnail)
    {
        nWidth = m_iThumbnailWidth;

        double dHt = ((double)rcCapture.Height() / (double)rcCapture.Width()) * (double)m_iThumbnailWidth;
        nHeight = (int)dHt;

        if (nHeight > m_iThumbnailHeight)
        {
            // Aspect ratio of this monitor is not the same as the primary monitor
        }
    }

    // Create bitmap and attach it to this object 
    if (!rImage.Create(nWidth, nHeight, 32, 0))
    {
        AfxMessageBox(L"Cannot create image!", MB_ICONERROR);
        return false;
    }


    // Create virtual screen DC
    CDC dcScreen;
    dcScreen.CreateDC(L"DISPLAY", nullptr, nullptr, nullptr);

    // Copy (or resize) the contents from the virtual screen DC 

    BOOL bRet = FALSE;
    auto dcImage = rImage.GetDC();
    if (bResizeAsThumbnail)
    {
        // Set the mode first!
        SetStretchBltMode(dcImage, COLORONCOLOR);

        int iTop = (m_iThumbnailHeight - nHeight) / 2;

        // Copy (and resize)
        bRet = ::StretchBlt(dcImage, 0, iTop,
            nWidth, 
            nHeight, 
            dcScreen.m_hDC, 
            rcCapture.left, 
            rcCapture.top, 
            rcCapture.Width(), 
            rcCapture.Height(), SRCCOPY | CAPTUREBLT);
    }
    else
    {
        // Copy
        bRet = ::BitBlt(dcImage, 0, 0, 
            rcCapture.Width(), 
            rcCapture.Height(),
            dcScreen.m_hDC, 
            rcCapture.left, 
            rcCapture.top, SRCCOPY | CAPTUREBLT);
    }

    // Do cleanup and return
    dcScreen.DeleteDC();
    rImage.ReleaseDC();

    return bRet;
}

在我的 PC 上,我有两个相同尺寸的显示器,它工作正常。但是当我在另一个站点尝试它时,有两台大电视连接到笔记本电脑,还有一个额外的显示器连接到笔记本电脑,缩略图呈现错误:

我会说第二个屏幕(电视)的缩略图大约是尺寸的 2/3。

我希望为所有监视器的列表控件创建一组缩略图,但没想到会这样。我做错了什么?

我想知道执行转换的

SetStretchBltMode
/
StretchBlt
逻辑是否不正确。


更新

我刚刚意识到:

  • GetMonitorInfo
    虚拟坐标提供屏幕数据。
  • StretchBlt
    使用逻辑屏幕坐标。

这就是我尝试使用另一个显示器屏幕并将其缩小时缩略图不正确的原因吗?

由于DC正在使用

MM_TEXT
我认为虚拟坐标v逻辑单元没有任何问题。他们是1:1。所以这不是问题。


更新

这些是缩略图显示不正确的计算机显示器的值:

Mon #1: \\.\DISPLAY1: T:     0 L:    0 B:  768 R: 1366
Mon #2: \\.\DISPLAY2: T: -1080 L: -566 B: -360 R:  714
Mon #3: \\.\DISPLAY3: T.   768 L: -564 B. 1848 R: 1356
Mon #4: \\.\DISPLAY5. T.     0 L. 1366 B.  900 R. 2966

我计算了他们各自的尺寸:

Mon #1: 1366 x  768
Mon #2: 1280 x  720 (this screen renders incorrectly)
Mon #3: 1920 x 1080
Mon #4: 1600 x  900

鉴于显示器的物理尺寸,我很惊讶显示器 2 在像素方面如此之小,因为它实际上是所有显示器中最大的。无论如何,我不知道这些数据是否可以帮助解释为什么我的代码无法为监视器 2 创建正确的缩略图。

visual-c++ mfc thumbnails monitor clistctrl
© www.soinside.com 2019 - 2024. All rights reserved.