如何正确使用和实例化现有的预览处理程序

How to Properly Use and Instantiate existing Preview Handlers

我正在尝试使用现有的预览处理程序来显示文件预览。

我编写了一个简单的测试程序来 1) 找到给定文件的预览处理程序的 CLSID,2) 实例化预览处理程序,3) 通过流或文件对其进行初始化,以及 4) 渲染预览一个基本的 window.

这行得通。或多或少。

它适用于 adobe acrobat reader 提供的 pdf 预览处理程序,但不适用于 windows 提供的 pdf 预览处理程序(CLSID {3A84F9C2-6164-485C-A7D9-4B27F8AC009E},由PdfPreviewHandler.dll 中的边,仅供参考)。 (它不会在任何地方失败,它只是不起作用并且不会呈现预览,请参见图像)。

excel (.xlsx) 和 power point (.pptx) 文件的 Microsoft Office 预览处理程序情况相同。

对于 word (.docx) 文件,它完全失败。第 106 行中的 IInitializeWithFile 调用失败并出现“未指定错误”(HRESULT 0x80004005)。

许多其他预览处理程序工作得很好,一些由流初始化,一些由文件初始化(例如 windows 为 html 和文本文件提供处理程序)。

我真的不知道问题出在哪里,或者我什至应该从哪里开始寻找,如有任何意见,我们将不胜感激。

使用 cl /std:c++20 test.cpp ole32.lib shlwapi.lib user32.lib /EHsc 编译,期望文件路径作为第一个可执行参数。

#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();
    }
}

你只需要在初始化后添加一个SetRect调用:

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

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