Исключение FileOpenPicker «неверный дескриптор окна»

Это дополнительный вопрос к вопросу Гуаранг Дэйв. С помощью @Simon_Mourier он это понял, а я до сих пор нет. Я пытаюсь выполнить то, что должно быть простой задачей: открыть FileOpenPicker из MainWindow. Простой код таков:

    fire_and_forget MainWindow::FileOpenClickHandler(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
    {
        auto lifetime = get_strong();

        // Create the file picker
        auto picker = winrt::Windows::Storage::Pickers::FileOpenPicker();
        picker.SuggestedStartLocation(winrt::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary);
        picker.FileTypeFilter().Append(L"*");

        // Open the file picker
        try {
            auto file = co_await picker.PickSingleFileAsync();
            if (file != nullptr)
            {
                // read the file
            }
            else
            {
                // The user did not pick a file
            }
        }
        catch (winrt::hresult_error const& ex)
        {
            // Open a window to show the error message
            winrt::Microsoft::UI::Xaml::Controls::ContentDialog dialog;
            dialog.Title(winrt::box_value(L"Error"));
            dialog.Content(winrt::box_value(ex.message().c_str()));
            dialog.CloseButtonText(L"OK");
            dialog.XamlRoot(canvasControl().XamlRoot());
            dialog.ShowAsync();
        }
    }

Результатом является исключение «неверный дескриптор окна». Оконная ручка!? Какая оконная ручка? Это заставило меня погрузиться в кроличью нору, пытаясь выяснить, как связать PickSingleFileAsync() с правильным дескриптором файла.

Вот моя следующая попытка, основанная на вопросе Гуаранга Дэйва:

        // Create the file picker
        auto picker = winrt::Windows::Storage::Pickers::FileOpenPicker();
        picker.SuggestedStartLocation(winrt::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary);
        picker.FileTypeFilter().Append(L"*");

        auto windowNative{ this->try_as<::IWindowNative>() };
        winrt::check_bool(windowNative);
        HWND hWnd{ 0 };
        windowNative->get_WindowHandle(&hWnd);
        auto initializeWithWindow{ picker.as<IInitializeWithWindow>() };
        initializeWithWindow->Initialize(hWnd);

        // Open the file picker
        try {
            auto file = co_await picker.PickSingleFileAsync();

К сожалению, это по-прежнему дает исключение «неверный дескриптор окна». Замена fire_and_forget на Windows::Foundation::IAsyncAction тоже не помогает. Следующим моим шагом было внедрение GetProcessFirstWindowHandle() Саймона Мурнье. Вот код:

        // Create the file picker
        auto picker = winrt::Windows::Storage::Pickers::FileOpenPicker();
        picker.SuggestedStartLocation(winrt::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary);
        picker.FileTypeFilter().Append(L"*");

        auto hWnd = GetProcessFirstWindowHandle();
        auto initializeWithWindow{ picker.as<IInitializeWithWindow>() };
        initializeWithWindow->Initialize(hWnd);

        // Open the file picker
        try {
            auto file = co_await picker.PickSingleFileAsync();

Результатом является то же исключение.

Наконец, я реализовал метод создания статического Window члена MainWindow и запутанный метод использования члена Window в App на основеэтого кода C#, но ни один из них не помог. Вот мой код со всякими закомментированными нерабочими методами:

    fire_and_forget MainWindow::FileOpenClickHandler(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
    {
        auto lifetime = get_strong();

        // ref: https://stackoverflow.com/questions/75436438/how-to-get-main-window-handle-on-page-in-winui-3-using-c
        //auto hWnd = GetProcessFirstWindowHandle();    // invalid window handle
        // ref: CoPilot, possibly based on https://stackoverflow.com/questions/71432263/how-to-retrieve-the-window-handle-of-the-current-winui-3-mainwindow-from-a-page/71440820#71440820
        //auto hWnd = App().MainWindow().as<::IWindowNative>()->WindowHandle();    // invalid window handle
        // ref: https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/win32/microsoft.ui.xaml.window/nf-microsoft-ui-xaml-window-iwindownative-get_windowhandle
        auto window = MainWindow::Current();
        auto windowNative{ window.as<::IWindowNative>() };
        HWND hWnd{ 0 };
        windowNative->get_WindowHandle(&hWnd);    // invalid window handle

        // Create the file picker
        auto picker = winrt::Windows::Storage::Pickers::FileOpenPicker();
        picker.SuggestedStartLocation(winrt::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary);
        picker.FileTypeFilter().Append(L"*");

        // Simplest option: Get the IInitializeWithWindow interface
        //auto initializeWithWindow{ picker.as<IInitializeWithWindow>() };
      
        // Option 2: Query for the IInitializeWithWindow interface
        winrt::com_ptr<IUnknown> unknownPicker = picker.as<IUnknown>();
        winrt::com_ptr<IInitializeWithWindow> initializeWithWindow;
        unknownPicker->QueryInterface(IID_PPV_ARGS(&initializeWithWindow));

        // Initialize the file picker with the window handle
        initializeWithWindow->Initialize(hWnd);

        // Open the file picker
        try {
            auto file = co_await picker.PickSingleFileAsync();
            if (file != nullptr)
            {
                path = file.Path();
                // read the file
            }
            else
            {
                // The user did not pick a file
            }
        }
        catch (winrt::hresult_error const& ex)
        {
            // Open a window to show the error message
            winrt::Microsoft::UI::Xaml::Controls::ContentDialog dialog;
            dialog.Title(winrt::box_value(L"Error"));
            dialog.Content(winrt::box_value(ex.message().c_str()));
            dialog.CloseButtonText(L"OK");
            dialog.XamlRoot(rootPanel().XamlRoot());
            dialog.ShowAsync();
        }
    }

До сих пор все, что я пробовал, приводило к исключению «неверный дескриптор окна» в первой строке блока try. Одна вещь, которая для меня до сих пор совершенно непонятна, — это напоминание Саймона о том, что FileOpenPicker должен запускаться в потоке пользовательского интерфейса. Я не создавал намеренно никаких других потоков, так есть ли какая-либо функция MainWindow::в потоке пользовательского интерфейса или нет? Главный вопрос, конечно, в том, как мне получить действительный дескриптор окна?

Зачем ты звонишь MainWindow::Current(), когда у тебя уже есть this? У вас в руках правильное главное окно, и вы игнорируете его и вместо этого получаете какое-то случайное главное окно.

Raymond Chen 29.05.2024 05:30

Вы никогда не проверяете, удалось ли выполнить windowNative->get_WindowHandle(&hWnd); или initializeWithWindow->Initialize(hWnd);, поэтому проблема может возникнуть задолго до вызова PickSingleFileAsync. Вот пример получения HWND из MainWindow: github.com/microsoft/Windows-classic-samples/blob/…

Raymond Chen 30.05.2024 00:21

Не могли бы вы опубликовать минимально воспроизводимый пример ?

Jesper Juhl 30.05.2024 00:29

@JesperJuhl Спасибо, что спросили о минимально воспроизводимом примере. Я создал приложение C++ WinUI 3 на рабочем столе и заменил обработчик «myButton_Click» кодом FileOpenClickHandler, показанным выше. Это сработало! Теперь я знаю, что либо я где-то испортил свой код, либо получаю исключение, потому что вызываю FileOpenClickHandler из MenuBarItem. Последнее проще протестировать, поэтому я добавлю MenuBar в фиктивное приложение.

dr_eck 30.05.2024 22:28

@JesperJuhl Хорошие/плохие новости: хорошая новость заключается в том, что FileOpenPicker работает из MenuBarItem. Плохая новость заключается в том, что мне придется переписать свое приложение с нуля, чтобы устранить весь мусор, вызывающий проблему. Большое спасибо.

dr_eck 30.05.2024 22:38

@JesperJuhl Спасибо за подсказки. Я скопировал соответствующий код в свое фиктивное приложение, убедился, что оно работает, а затем скопировал его обратно. Теперь я больше не получаю недопустимых дескрипторов окон. Почему? Я не знаю. Но это работает.

dr_eck 31.05.2024 00:36

@RaymondChen Спасибо за предложение проверить HWND. У меня есть действительные дескрипторы окон (проверено с помощью ::IsWindow()), но проверка — хорошая идея.

dr_eck 31.05.2024 00:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
99
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

1, я предлагаю вам обратиться к теме: https://stackoverflow.com/a/75440195

Только Window реализует IWindowNative.

2, FileOpenPicker не находится в Windows::UI.

Я предлагаю вам попробовать использовать IInitializeWithWindow ссылку на блог: https://devblogs.microsoft.com/oldnewthing/20190412-00/?p=102413.

И вы можете обратиться к теме: Ошибка «Неверный дескриптор окна» при использовании FileOpenPicker из C++/WinRT без UWP

@Jeaninez Спасибо за ответ. Ваша первая ссылка — это ответ, указанный в моих комментариях к коду; Я уже пробовал это. Ваша вторая ссылка интересна, но она не говорит мне, как получить HWND для MainWindow в приложении WinUI 3. Что касается вашей третьей ссылки, в ответе Джона Лондона на его собственный вопрос есть одна строка, которая упрощает мой код, но я все равно получаю исключение «Неверный дескриптор окна». Я все еще ищу ответ.

dr_eck 29.05.2024 23:31
Ответ принят как подходящий

Вот окончательный рабочий код:

#include "shobjidl_core.h"                  // for IInitializeWithWindow
#include <Microsoft.UI.Xaml.Window.h>       // for IWindowNative

    HWND MainWindow::GetWindowHandleFromThis()
    {
        HWND hWnd{ 0 };
        auto windowNative{ this->try_as<::IWindowNative>() };
        winrt::check_bool(windowNative);
        winrt::check_hresult(windowNative->get_WindowHandle(&hWnd));
        return hWnd;
    }
    fire_and_forget MainWindow::FileOpenClickHandler(IInspectable const&, RoutedEventArgs const&)
    {
        auto lifetime = get_strong();

        HWND hWnd = GetWindowHandleFromThis();
        assert(::IsWindow(hWnd) != 0);

        // Create the file picker
        auto picker = winrt::Windows::Storage::Pickers::FileOpenPicker();
        picker.as<IInitializeWithWindow>()->Initialize(hWnd);   // ref: https://devblogs.microsoft.com/oldnewthing/20190412-00/?p=102413
        picker.SuggestedStartLocation(winrt::Windows::Storage::Pickers::PickerLocationId::DocumentsLibrary);
        picker.FileTypeFilter().Append(L"*");

        // Open the file picker
        try {
            auto file = co_await picker.PickSingleFileAsync();
            if (file != nullptr)
            {
                // read the file
            }
            else
            {
                // The user did not pick a file
            }
        }
        catch (winrt::hresult_error const& ex)
        {
            // Open a window to show the error message
            winrt::Microsoft::UI::Xaml::Controls::ContentDialog dialog;
            dialog.Title(winrt::box_value(L"Error"));
            dialog.Content(winrt::box_value(ex.message().c_str()));
            dialog.CloseButtonText(L"OK");
            dialog.XamlRoot(OpenBtn().XamlRoot());
            dialog.ShowAsync();
        }
    }

Хотя я не знаю, почему предыдущий код не работает, а этот работает, я надеюсь, что он будет полезен кому-то еще.

Другие вопросы по теме