我正在尝试使用现有的预览处理程序来显示文件的预览。
我编写了一个简单的测试程序,以 1) 查找给定文件的预览处理程序的 CLSID,2) 实例化预览处理程序,3) 通过流或文件初始化它,4) 在基本窗口上渲染预览。
这有效。或多或少。
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).
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();
}
}