Question

I am trying to access the image data of a win32 thumbnail to use it with another library using the following steps:

  1. Get the thumbnail (an ISharedBitmap) from the windows thumbnail cache
  2. Get the HBITMAP of the thumbnail
  3. Get the pixel data using GetDIBits

Thumbnail retrieval is based on https://stackoverflow.com/a/19529633/3165122

Pixel data retrieval is based on https://stackoverflow.com/a/3688682/3165122

This is what I got so far:

// error checks omitted
LPCTSTR path = TEXT("<path-to-jpg-image>");

HRESULT hr = CoInitialize(nullptr);

// Get the thumbnail
IShellItem* item = nullptr;
hr = SHCreateItemFromParsingName(path, nullptr, IID_PPV_ARGS(&item));

IThumbnailCache* cache = nullptr;
hr = CoCreateInstance(
    CLSID_LocalThumbnailCache,
    nullptr,
    CLSCTX_INPROC,
    IID_PPV_ARGS(&cache)); 

ISharedBitmap* shared_bitmap;
hr = cache->GetThumbnail(
    item,
    1024,
    WTS_EXTRACT,
    &shared_bitmap,
    nullptr,
    nullptr);

// Retrieve thumbnail HBITMAP
HBITMAP hbitmap = NULL;
hr = shared_bitmap->GetSharedBitmap(&hbitmap);

HDC dc = GetDC(NULL);
HDC dc_mem = CreateCompatibleDC(dc);

// Get required buffer size
BITMAPINFO bmi;
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
GetDIBits(dc_mem, hbitmap, 0, 0, nullptr, &bmi, DIB_RGB_COLORS); // <- this fails

WTS_ALPHATYPE alpha_type;
hr = shared_bitmap->GetFormat(&alpha_type);

bmi.bmiHeader.biBitCount = alpha_type == WTSAT_RGB ? 24 : 32;
bmi.bmiHeader.biHeight = std::abs(bmi.bmiHeader.biHeight);
bmi.bmiHeader.biCompression = BI_RGB;

// Get image data
std::vector<char> buffer(bmi.bmiHeader.biSizeImage);
GetDIBits(dc_mem, hbitmap, 0, bmi.bmiHeader.biHeight,
        &buffer[0], &bmi, DIB_RGB_COLORS);

// use buffer...

The first call to GetDIBits to retrieve the required buffer size fails with return value 0. I am guessing I'm using an incompatible DC? How do I get a compatible DC?

https://stackoverflow.com/a/14207674/3165122 states "[...] if you don't know what your HBITMAP is pointing to, you can't expect to do anything useful with it."

Is there really no way? Am I doing something wrong? Is there a different approach?

Was it helpful?

Solution

According to the GetDIBits docs: "If lpvBits is NULL and the bit count member of BITMAPINFO is initialized to zero, GetDIBits fills in a BITMAPINFOHEADER structure [...]"

In my case the bit count member wasn't initialized to zero. Calling ZeroMemory(&bmi, sizeof(BITMAPINFO)) just after its declaration fixed it. To my defense: BITMAPINFO is an output argument ;)

Many thanks to Stuart!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top