Как правильно использовать и создавать экземпляры существующих обработчиков предварительного просмотра
Я пытаюсь использовать существующие обработчики предварительного просмотра для отображения предварительного просмотра файлов.
Я написал простую тестовую программу, чтобы 1) найти CLSID обработчика предварительного просмотра для данного файла, 2) создать экземпляр обработчика предварительного просмотра, 3) инициализировать его либо потоком, либо файлом и 4) отобразить предварительный просмотр в базовом окне.
Это работает. Более менее.
Он отлично работает с обработчиком предварительного просмотра PDF, предоставляемым Adobe Acrobat Reader, но не работает с обработчиком предварительного просмотра PDF, предоставленным Windows (CLSID
{3A84F9C2-6164-485C-A7D9-4B27F8AC009E}
, предоставленный edge в PdfPreviewHandler.dll, просто для справки). (Нигде не глючит, просто не работает и не рендерит превью, см. изображения).
Та же ситуация с обработчиками предварительного просмотра Microsoft Office для файлов Excel (.xlsx) и Power Point (.pptx).
Для файлов Word (.docx) он полностью терпит неудачу. Вызов IInitializeWithFile в строке 106 завершился сбоем с «неуказанной ошибкой» (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();
}
}
1 ответ
Вам просто нужно добавить вызов SetRect после инициализации:
RECT rect;
GetClientRect(window, &rect);
iPreviewHandler->SetWindow(window, &rect);
iPreviewHandler->DoPreview();
// add this
iPreviewHandler->SetRect(&rect);