How to Properly Use and Instantiate existing Preview Handlers

436 views Asked by At

I am trying to use existing preview handlers to display previews of files.

I wrote a simple test program to 1) find the CLSID of the preview handler for a given file, 2) instantiate the preview handler, 3) initialize it, either by stream or file and 4) render the preview on a basic window.

This works. More or less.

It works just fine for the pdf preview handler provided by adobe acrobat reader, but doesn't work with the windows provided pdf preview handler (CLSID {3A84F9C2-6164-485C-A7D9-4B27F8AC009E}, provided by edge in PdfPreviewHandler.dll, just for reference). (It doesn't fail anywhere, it just doesnt work and doesn't render a preview, see images). windows provided pdf preview handler acrobat reader pdf handler

Same situation for the microsoft office preview handlers for excel (.xlsx) and power point (.pptx) files.

For word (.docx) files, it fails completely. The IInitializeWithFile call in line 106 fails with "Unspecified Error" (HRESULT 0x80004005).

A bunch of other preview handlers work just fine, some initialized by stream, some by file (e.g. windows provided handler for html and text files).

I don't really know what the issue could be or where i even should start looking, any input on this would be appreciated.

compile with cl /std:c++20 test.cpp ole32.lib shlwapi.lib user32.lib /EHsc, expects file path as first executable argument.

#include <filesystem>
#include <array>
#include <cassert>
#include <stdexcept>
#include <iostream>

#include "Windows.h"
#include "ShObjIdl.h"
#include "shlwapi.h"
#include "objbase.h"

#define checkHresult(res) (checkHresult_(res, __LINE__, __FILE__))

void checkHresult_(HRESULT res, int line, const char *file){
    if(res != S_OK){
        std::stringstream msg;
        msg << file << ':' << line << ": 0x" << std::hex << res << ' ';
        LPSTR errMsg;
        if(FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 
                          nullptr, 
                          res, 
                          MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), 
                          reinterpret_cast<LPSTR>(&errMsg), 
                          0, 
                          nullptr)){
            msg << errMsg;
            LocalFree(errMsg);
        }
        throw std::runtime_error(msg.str());
    }
}

CLSID getShellExtensionClsidForFileType(const std::wstring& extension, const GUID& interfaceID){
    HRESULT res;
    std::array<wchar_t, 39> ifIdWStr;
    int written;
    written = StringFromGUID2(interfaceID, ifIdWStr.data(), ifIdWStr.size());
    if(written == 0){
        checkHresult(HRESULT_FROM_WIN32(GetLastError())); //StringFromGUID2 should not fail
    }

    std::array<wchar_t, 39> extIdWStr;
    DWORD extIdWStrSize = extIdWStr.size();
    res = AssocQueryStringW(ASSOCF_INIT_DEFAULTTOSTAR, 
                            ASSOCSTR_SHELLEXTENSION,
                            extension.c_str(),
                            ifIdWStr.data(),
                            extIdWStr.data(),
                            &extIdWStrSize);
    checkHresult(res);
    
    CLSID extId;
    res = IIDFromString(extIdWStr.data(), &extId);
    checkHresult(res); //IIDFromString should not fail
    std::wcout << "preview handler clsid: " << extIdWStr.data() << '\n';
    return(extId);
}


IPreviewHandler* getIPreviewHandlerInterfaceForType(const std::wstring& extension){
    HRESULT res;
    //get the CLSID for the preview handler for the specified fily type
    CLSID iPreviewHandlerClsid(getShellExtensionClsidForFileType(extension, IID_IPreviewHandler));
    IPreviewHandler *iPreviewHandler;
    res = CoCreateInstance(iPreviewHandlerClsid, 
                           nullptr, 
                           CLSCTX_LOCAL_SERVER, 
                           IID_IPreviewHandler, 
                           reinterpret_cast<LPVOID*>(&iPreviewHandler));
    checkHresult(res);
    return(iPreviewHandler);
}

int wmain(int argc, wchar_t *argv[]){
    try{
        if(argc != 2){
            return(1);
        }
        HRESULT res;

        res = CoInitialize(nullptr);
        checkHresult(res);

        std::filesystem::path filePath(argv[1]);
        filePath.make_preferred();

        //Instantiate the preview handler for the specified file type
        IPreviewHandler *iPreviewHandler = getIPreviewHandlerInterfaceForType(filePath.extension());

        IInitializeWithStream *iInitializeWithStream;
        IInitializeWithFile *iInitializeWithFile;

        iPreviewHandler->QueryInterface(IID_IInitializeWithStream, reinterpret_cast<LPVOID*>(&iInitializeWithStream));
        iPreviewHandler->QueryInterface(IID_IInitializeWithFile, reinterpret_cast<LPVOID*>(&iInitializeWithFile));

        //Initialize preview handler, preferably with a stream
        if(iInitializeWithStream){
            IStream *iStream;
            res = SHCreateStreamOnFileEx(filePath.c_str(), STGM_READ | STGM_SHARE_DENY_WRITE, 0, false, nullptr, &iStream);
            checkHresult(res);
            res = iInitializeWithStream->Initialize(iStream, STGM_READ);
            checkHresult(res);
            std::cout << "Initialized with Stream\n";
        }else if(iInitializeWithFile){
            res = iInitializeWithFile->Initialize(filePath.c_str(), STGM_READ);
            checkHresult(res);
            std::cout << "Initialized with File\n";
        }else{
            checkHresult(E_NOINTERFACE);
        }

        //create basic window
        WNDCLASSW wndClass;
        wndClass.style = 0;
        wndClass.lpfnWndProc = DefWindowProcW;
        wndClass.cbClsExtra = 0;
        wndClass.cbWndExtra = 0;
        wndClass.hInstance = GetModuleHandleA(nullptr);
        wndClass.hIcon = nullptr;
        wndClass.hCursor = nullptr;
        wndClass.hbrBackground = nullptr;
        wndClass.lpszMenuName = nullptr;
        wndClass.lpszClassName = L"test";

        ATOM wndAtom = RegisterClassW(&wndClass);
        if(wndAtom == 0){
            checkHresult(HRESULT_FROM_WIN32(GetLastError()));
        }
        HWND window = CreateWindowExW(0, 
                                      L"test", 
                                      L"", 
                                      WS_VISIBLE, 
                                      CW_USEDEFAULT, 
                                      CW_USEDEFAULT, 
                                      CW_USEDEFAULT, 
                                      CW_USEDEFAULT, 
                                      0, 
                                      0, 
                                      wndClass.hInstance, 
                                      nullptr);
        if(window == nullptr){
            checkHresult(HRESULT_FROM_WIN32(GetLastError()));
        }

        ShowWindow(window, SW_NORMAL);

        RECT rect;
        GetClientRect(window, &rect);
        res = iPreviewHandler->SetWindow(window, &rect);
        checkHresult(res);
        res = iPreviewHandler->DoPreview();

        MSG msg;
        while(GetMessageW(&msg, nullptr, 0, 0) > 0){
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }catch(std::runtime_error err){
        std::cout << err.what();
    }
}
1

There are 1 answers

5
Simon Mourier On BEST ANSWER

You just need to add a SetRect call after initialization:

RECT rect;
GetClientRect(window, &rect);
iPreviewHandler->SetWindow(window, &rect);
iPreviewHandler->DoPreview();

// add this
iPreviewHandler->SetRect(&rect);