簡體   English   中英

Windows 預覽處理程序不尊重使用 SetRect 設置的繪圖區域

[英]Windows preview handlers don't respect drawing area set with SetRect

我一直在嘗試在我的應用程序中使用 Windows 預覽處理程序,並注意到一些預覽處理程序不遵守使用SetRect(&rect)設置的繪圖矩形。

例如,對於 Edge PDF 預覽處理程序 (CLSID {3A84F9C2-6164-485C-A7D9-4B27F8AC009E} ),調用SetRect僅在第一次調用時正常工作。 在連續的調用中,只有矩形尺寸被更新,而不是 position:

RECT rect{0,0,500,500}; // 500x500 rectangle at position (0,0)
preview_handler->SetWindow(hwnd, &rect); // Sets parent window and initial drawing rectangle correctly
rect = {100, 100, 700, 700}; // 600x600 rect at position (100,100)
preview_handler->SetRect(&rect); // Updates rectangle size to 600x600, but stays at position (0,0)

Powerpoint 預覽處理程序 (CLSID {65235197-874B-4A07-BDC5-E65EA825B718} ) 的行為類似,只是它根本不遵守 position,它始終位於 (0,0)。

Word 預覽處理程序 (CLSID {84F66100-FF7C-4fb4-B0C0-02CD7FB668FE} ) 在第一次獲得焦點時自動將繪圖矩形擴展到父 Window 的整個客戶區域(例如,當您單擊“Word”區域時,加載預覽后)。 以后的SetRect調用行為正常。

根據 MSDN, SetRect

指示預覽處理程序更改其繪制到的父 hwnd 中的區域

...僅呈現在此方法的 prc 所描述的區域中...

我已經檢查了文件資源管理器的作用。 它似乎以預期的大小創建了一個子 window 和 position,然后將預覽放在該 window 中。在這種情況下,上面列出的問題是無關緊要的,因為預覽填充了整個 window。我現在的問題是預覽處理程序是否只是行為不當,或者我遺漏了什么。

以下是演示該問題的示例應用程序,它需要一個文件路徑作為第一個參數,創建一個新的 window 並預覽該文件。 它首先將繪圖矩形在 position (0,0) 處設置為 500x500,然后在 (100,100) 處設置為 600x600。 大多數錯誤處理被省略。

cl /std:c++20 /EHsc main.cpp編譯

#include <Windows.h>
#include <shlwapi.h>
#include <objbase.h>
#include <Shobjidl.h>
#include <thread>
#include <string>
#include <array>
#include <filesystem>
#include <iostream>

#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "User32.lib")
#pragma comment(lib, "Ole32.lib")

void WindowThreadProc(bool& ready, HWND& hwnd) {
    SetProcessDPIAware();
    HINSTANCE hinst = GetModuleHandleW(nullptr);

    auto wnd_proc = [](HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) ->LRESULT {
        switch (message) {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
    };

    WNDCLASS wc{};
    wc.lpfnWndProc = wnd_proc;
    wc.hInstance = hinst;
    wc.lpszClassName = "Test Window";

    RegisterClass(&wc);

    HWND handle = CreateWindowExW(
        0, L"Test Window", L"Test Window", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        nullptr, nullptr, hinst, nullptr);

    ShowWindow(handle, true);

    hwnd = handle;
    ready = true;

    MSG msg{};
    while (GetMessage(&msg, nullptr, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
}

//return clsid of preview handler for the passed extension, throw if no suitable preview handler is installed
CLSID getShellExClsidForType(
    const std::wstring& extension,
    const GUID& interfaceClsid) {
    std::array<wchar_t, 39> interfaceClsidWstr;
    StringFromGUID2(
        interfaceClsid,
        interfaceClsidWstr.data(),
        static_cast<DWORD>(interfaceClsidWstr.size()));

    std::array<wchar_t, 39> extensionClsidWstr;
    DWORD extensionClsidWstrSize = static_cast<DWORD>(extensionClsidWstr.size());
    HRESULT res;
    res = AssocQueryStringW(
        ASSOCF_INIT_DEFAULTTOSTAR,
        ASSOCSTR_SHELLEXTENSION,
        extension.c_str(),
        interfaceClsidWstr.data(),
        extensionClsidWstr.data(),
        &extensionClsidWstrSize);
    if (res != S_OK) {
        throw "no preview handler found";
    };

    CLSID extensionClsid;
    IIDFromString(extensionClsidWstr.data(), &extensionClsid);

    std::wcout << L"Extension: " << extension << L" - Preview Handler CLSID: " << extensionClsidWstr.data() << std::endl;

    return(extensionClsid);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        return 0;
    }

    //initialize as STA
    CoInitialize(nullptr);

    bool ready = false;
    HWND hwnd;
    //create and run message pump in different thread
    std::thread window_thread(WindowThreadProc, std::ref(ready), std::ref(hwnd));

    //wait for window to be ready
    while (!ready) {}

    //create preview handler, use first argument as path
    std::filesystem::path path(argv[1]);
    CLSID clsid = getShellExClsidForType(path.extension(), __uuidof(IPreviewHandler));
    IPreviewHandler *preview_handler;
    CoCreateInstance(
        clsid, 
        nullptr, 
        CLSCTX_LOCAL_SERVER, 
        __uuidof(IPreviewHandler), 
        reinterpret_cast<void**>(&preview_handler));

    IInitializeWithStream* init_with_stream;
    HRESULT res;
    //initialize previewhandler with stream or with file path
    IInitializeWithFile* init_with_file;
    res = preview_handler->QueryInterface(&init_with_file);
    if (res == S_OK) {
        init_with_file->Initialize(path.c_str(), STGM_READ);
        init_with_file->Release();
    }
    else {
        IInitializeWithStream* init_with_stream;
        res = preview_handler->QueryInterface(&init_with_stream);
        if (res == S_OK) {
            IStream* stream;
            SHCreateStreamOnFileEx(
                path.c_str(),
                STGM_READ | STGM_SHARE_DENY_WRITE,
                0, false, nullptr,
                &stream);
            init_with_stream->Initialize(stream, STGM_READ);
            stream->Release();
            init_with_stream->Release();
        }
        else {
            throw "neither InitializeWithFile nor InitializeWithStream supported";
        }
    }

    auto print_rect = [](RECT& rect) {
        std::wcout << L"Setting Rect to: (" << rect.left << L", " << rect.top << ", " << rect.right << ", " << rect.bottom << ")" << std::endl;
    };

    //initial rect
    RECT rect{ 0,0,500,500 };
    print_rect(rect);
    preview_handler->SetWindow(hwnd, &rect);
    preview_handler->DoPreview();
    preview_handler->SetRect(&rect);

    //new rect
    rect = { 200, 200, 800, 800 };
    print_rect(rect);
    preview_handler->SetRect(&rect);


    window_thread.join();

    preview_handler->Release();
    
    return(0);
}

大多數開發人員可能只在資源管理器中進行測試,如果資源管理器使用子 window 來托管處理程序,則預覽處理程序中不會注意到這些錯誤 go。

您最好的選擇是對您自己的應用程序執行相同的操作。

眾所周知,托管 shell 擴展是一項艱巨的任務,一些擴展甚至會搞砸QueryInterface ,因此Explorer 必須同時檢查 HRESULT 和指針

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM