Search code examples
c++comthumbnailsgdiatl

ATL: OnDrawThumbnail hDrawDC seems to be monochrome in IThumbnailProvider


I'm working on a C++ ATL COM thumbnail/preview/search project, but its bitmap displaying code behavior is monochrome during the Thumbnail process instead of the colored. The Preview process is colored as expected, using the same function.

I used the ATL Wizard to create the IThumbnailProvider and his friends. My small changes are: I replaced the color from black to pink in the document::OnDrawThumbnail and I wrote the document::OnDrawThumbnail into CPreviewCtrl::DoPaint. I've read the "new DC always monochrome" thing in the MS spec but I could not get colored DC even if a changed the original ATL code OnDrawThumbnail(GetDC(NULL), &rcBounds);. The CreateCompatibleDC(NULL) and CreateCompatibleDC(hDrawDC) were dead-end too.

document.cpp (changed)

// 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 (changed, it is called by the Preview)

// 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 (unchanged, from VS SDK \atlmfc\include\ which is called be the thumbnail provider)

    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


Solution

  • Github helped me. It is definitely an ATL SDK bug.

    BUG report on the VS developer community

    Solution on the www.patthoyts.tk

    And the github repo which helped me: abhimanyusirohi/ThumbFish

    In the atlhandlerimpl.h provided GetThumbnail must be override:

    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;
    }