Search code examples
c++clipboardwic

Reading Clipboard with WIC


I'm trying to write a function to read images copied to the clipboard with Windows Imaging Component. Currently my code is based off how I load resources but modified to work with the clipboard. everything up to initializing the stream works fine but once I try to use CreateDecoderFromeStream it fails. I've tried copying images from numerous places to no avail. Is there something different about the copied image format that can't be read by WIC?

Here's my code below...

Creating a memory object to read from the clipboard

COMStreamSPtr WIC::createStreamFromClipboard() {
    IStream* ipStream = NULL;
    COMStreamSPtr stream = nullptr;
    CoInitialize(nullptr);
    if (!IsClipboardFormatAvailable(CF_BITMAP) && !IsClipboardFormatAvailable(CF_DIB) && !IsClipboardFormatAvailable(CF_DIBV5))
        goto Return;

    if (!OpenClipboard(NULL))
        goto Return;

    // Load the clipboard
    HGLOBAL hMem = GetClipboardData(CF_BITMAP);
    if (hMem == NULL || hMem == INVALID_HANDLE_VALUE)
        hMem = GetClipboardData(CF_DIB);
    if (hMem == NULL || hMem == INVALID_HANDLE_VALUE)
        hMem = GetClipboardData(CF_DIBV5);
    if (hMem == NULL || hMem == INVALID_HANDLE_VALUE)
        goto CloseClipboard;

    // Lock the clipboard, getting a pointer to its data
    LPVOID pvSourceClipboardData = GlobalLock(hMem);
    if (pvSourceClipboardData == NULL)
        goto CloseClipboard;

    // Read the clipboard data size
    DWORD dwClipboardSize = GlobalSize(hMem);
    if (dwClipboardSize == 0)
        goto GlobalUnlock;

    // Allocate memory to hold the clipboard data
    HGLOBAL hgblClipboardData = GlobalAlloc(GMEM_MOVEABLE, dwClipboardSize);
    if (hgblClipboardData == NULL)
        goto GlobalUnlock;

    // Get a pointer to the allocated memory
    LPVOID pvClipboardData = GlobalLock(hgblClipboardData);
    if (pvClipboardData == NULL)
        goto FreeData;

    // Copy the data from the clipboard to the new memory block
    CopyMemory(pvClipboardData, pvSourceClipboardData, dwClipboardSize);
    GlobalUnlock(hgblClipboardData);

    // Create a stream on the HGLOBAL containing the data
    if (SUCCEEDED(CreateStreamOnHGlobal(hgblClipboardData, TRUE, &ipStream))) {
        stream = std::make_shared<COMStream>(ipStream);
        goto CloseClipboard;
    }

FreeData:
    // Couldn't create stream; free the memory
    GlobalFree(hgblClipboardData);
GlobalUnlock:
    // Unlock the clipboard data
    GlobalUnlock(hMem);
CloseClipboard:
    // Close the clipboard
    CloseClipboard();
Return:
    // No need to unlock or free the resource
    CoUninitialize(); // CoInialize is called by my COMObjects in the constructor so this is just decrementing the count.
    return stream;
}

Elsewhere in the code using the returned stream.

WICBitmapDecoderSPtr WIC::createDecoderFromStream(COMStreamSPtr stream) {
    if (isCOMObjectNull(stream))
        return nullptr;

    IWICImagingFactory* ipFactory = NULL;
    IWICBitmapDecoder* ipDecoder = NULL;
    WICBitmapDecoderSPtr decoder = nullptr;

    // Create the factory
    if (FAILED(CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&ipFactory))))
        goto Return;

    // Create the decoder
    if (FAILED(ipFactory->CreateDecoderFromStream(stream->ipObject, NULL, WICDecodeMetadataCacheOnDemand, &ipDecoder)))
        goto ReleaseFactory; // FAILS HERE

    decoder = std::make_shared<WICBitmapDecoder>(ipDecoder);
ReleaseFactory:
    ipFactory->Release();
Return:
    return decoder;
}

Solution

  • I've finally figured out how to do this. What I did is I used IWICImagingFactory's CreateBitmapFromHBITMAP function. Since reading an HBITMAP from the clipboard work trivial, the solution becomes pretty straight forward.

    IWICBitmapSource* WIC::readSourceFromClipboard() {
        IWICBitmapSource* ipSource = NULL;
        if (Clipboard::containsFormat(CF_BITMAP)) {
            if (OpenClipboard(NULL)) {
                HBITMAP hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
                ipSource = createSourceFromHBitmap(hBitmap);
                CloseClipboard();
            }
        }
        return ipSource;
    }
    
    IWICBitmapSource* WIC::createSourceFromHBitmap(HBITMAP hBitmap) {
        if (hBitmap == NULL)
            return NULL;
    
        IWICImagingFactory* ipFactory = NULL;
        IWICBitmap* ipBitmap = NULL;
    
        CoInitialize(NULL);
    
        // Create the factory
        if (FAILED(CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&ipFactory))))
            goto Return;
    
        // Create the bitmap
        if (FAILED(ipFactory->CreateBitmapFromHBITMAP(hBitmap, NULL, WICBitmapIgnoreAlpha, &ipBitmap)))
            goto ReleaseFactory;
    
    ReleaseFactory:
        ipFactory->Release();
    Return:
        CoUninitialize();
        return ipBitmap;
    }