ATL:OnDrawThumbnail hDrawDC 在 IThumbnailProvider 中似乎是单色的

ATL: OnDrawThumbnail hDrawDC seems to be monochrome in IThumbnailProvider

我正在处理一个 C++ ATL COM thumbnail/preview/search 项目,但它的位图显示代码行为在缩略图过程中是单色的,而不是彩色的。使用相同的功能,预览过程按预期着色。

我使用 ATL 向导创建了 IThumbnailProvider 和他的朋友们。我的小改动是:我将 document::OnDrawThumbnail 中的颜色从黑色替换为粉红色,并将 document::OnDrawThumbnail 写入 CPreviewCtrl::DoPaint。我已经阅读了 MS 规范中的“新 DC 始终是单色”的内容,但即使更改了原始 ATL 代码 OnDrawThumbnail(GetDC(NULL), &rcBounds);,我也无法获得彩色 DC。 CreateCompatibleDC(NULL)CreateCompatibleDC(hDrawDC) 也是死胡同。

document.cpp(已更改)

// Almost the default sample code, but hDrawBrush is changed to pink
void document::OnDrawThumbnail(HDC hDrawDC, LPRECT lprcBounds)
{
  HBRUSH hDrawBrush = CreateSolidBrush(RGB(255, 0, 255)); // pink
  FillRect(hDrawDC, lprcBounds, hDrawBrush);

  HFONT hStockFont = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
  LOGFONT lf;

  GetObject(hStockFont, sizeof(LOGFONT), &lf);
  lf.lfHeight = 34;

  HFONT hDrawFont = CreateFontIndirect(&lf);
  HFONT hOldFont = (HFONT) SelectObject(hDrawDC, hDrawFont);

  CString strText = _T("TODO: implement thumbnail drawing here");
  DrawText(hDrawDC, strText, strText.GetLength(), lprcBounds, DT_CENTER | DT_WORDBREAK);

  SelectObject(hDrawDC, hDrawFont);
  SelectObject(hDrawDC, hOldFont);

  DeleteObject(hDrawBrush);
  DeleteObject(hDrawFont);
}

PreviewHandler.h(已更改,由预览调用)

// CPreviewCtrl implementation
class CPreviewCtrl : public CAtlPreviewCtrlImpl
{
protected:
    virtual void DoPaint(HDC hdc)
    {
        // you can obtain a pointer to IDocument as follows
        // CMyDoc* pDoc = (CMyDoc*)m_pDocument;
        /*
        CString strData = _T("Draw Rich Preview content here.");
        TextOut(hdc, 10, 20, strData, strData.GetLength());
        */

        RECT rc{};
        rc.right = 290;
        rc.bottom = 290;
        dynamic_cast<document*>(m_pDocument)->OnDrawThumbnail(hdc, &rc);
    }
};

atlhandlerimpl.h(未更改,来自 VS SDK \atlmfc\include\ 称为缩略图提供程序)

    ATLPREFAST_SUPPRESS(6101)
    _Success_(return != FALSE) BOOL GetThumbnail(
        _In_ UINT cx,
        _Out_ HBITMAP* phbmp,
        _Out_ WTS_ALPHATYPE* /* pdwAlpha */)
    {
        HDC hdc = ::GetDC(NULL);
        RECT rcBounds;

        SetRect(&rcBounds, 0, 0, cx, cx);

        HDC hDrawDC = CreateCompatibleDC(hdc);
        if (hDrawDC == NULL)
        {
            ReleaseDC(NULL, hdc);
            return FALSE;
        }

        HBITMAP hBmp = CreateCompatibleBitmap(hDrawDC, cx, cx);
        if (hBmp == NULL)
        {
            ReleaseDC(NULL, hdc);
            DeleteDC(hDrawDC);
            return FALSE;
        }
        
        HBITMAP hOldBitmap = (HBITMAP) SelectObject(hDrawDC, hBmp);

        // Here you need to draw the document's data
        OnDrawThumbnail(hDrawDC, &rcBounds);

        SelectObject(hDrawDC, hOldBitmap);

        DeleteDC(hDrawDC);
        ReleaseDC(NULL, hdc);

        *phbmp = hBmp;
        return TRUE;
    }
    ATLPREFAST_UNSUPPRESS()

Sample thumbnail in the File Explorer

Github 帮助了我。这绝对是一个 ATL SDK 错误。

BUG report on the VS developer community

Solution on the www.patthoyts.tk

And the github repo which helped me: abhimanyusirohi/ThumbFish

atlhandlerimpl.h中提供的GetThumbnail必须被覆盖:

BOOL document::GetThumbnail(
  _In_ UINT cx,
  _Out_ HBITMAP * phbmp,
  _Out_ WTS_ALPHATYPE* /* pdwAlpha */)
{
  BOOL br = FALSE;
  HDC hdc = ::GetDC(NULL);
  HDC hDrawDC = CreateCompatibleDC(hdc);
  if (hDrawDC != NULL)
  {
    void* bits = 0;
    RECT rcBounds;
    SetRect(&rcBounds, 0, 0, cx, cx);

    BITMAPINFO bi = { 0 };
    bi.bmiHeader.biWidth = cx;
    bi.bmiHeader.biHeight = cx;
    bi.bmiHeader.biPlanes = 1;
    bi.bmiHeader.biBitCount = 32;
    bi.bmiHeader.biSizeImage = 0;
    bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bi.bmiHeader.biClrUsed = 0;
    bi.bmiHeader.biClrImportant = 0;

    HBITMAP hBmp = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, &bits, NULL, 0);
    if (hBmp != NULL)
    {
      HBITMAP hOldBitmap = (HBITMAP)SelectObject(hDrawDC, hBmp);
      OnDrawThumbnail(hDrawDC, &rcBounds);
      SelectObject(hDrawDC, hOldBitmap);
      *phbmp = hBmp;
      br = TRUE;
    }
    DeleteDC(hDrawDC);
  }

  ReleaseDC(NULL, hdc);
  return br;
}