Это дополнительный вопрос к вопросу Гуаранг Дэйв. С помощью @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::в потоке пользовательского интерфейса или нет? Главный вопрос, конечно, в том, как мне получить действительный дескриптор окна?
Вы никогда не проверяете, удалось ли выполнить windowNative->get_WindowHandle(&hWnd); или initializeWithWindow->Initialize(hWnd);, поэтому проблема может возникнуть задолго до вызова PickSingleFileAsync. Вот пример получения HWND из MainWindow: github.com/microsoft/Windows-classic-samples/blob/…
Не могли бы вы опубликовать минимально воспроизводимый пример ?
@JesperJuhl Спасибо, что спросили о минимально воспроизводимом примере. Я создал приложение C++ WinUI 3 на рабочем столе и заменил обработчик «myButton_Click» кодом FileOpenClickHandler, показанным выше. Это сработало! Теперь я знаю, что либо я где-то испортил свой код, либо получаю исключение, потому что вызываю FileOpenClickHandler из MenuBarItem. Последнее проще протестировать, поэтому я добавлю MenuBar в фиктивное приложение.
@JesperJuhl Хорошие/плохие новости: хорошая новость заключается в том, что FileOpenPicker работает из MenuBarItem. Плохая новость заключается в том, что мне придется переписать свое приложение с нуля, чтобы устранить весь мусор, вызывающий проблему. Большое спасибо.
@JesperJuhl Спасибо за подсказки. Я скопировал соответствующий код в свое фиктивное приложение, убедился, что оно работает, а затем скопировал его обратно. Теперь я больше не получаю недопустимых дескрипторов окон. Почему? Я не знаю. Но это работает.
@RaymondChen Спасибо за предложение проверить HWND. У меня есть действительные дескрипторы окон (проверено с помощью ::IsWindow()), но проверка — хорошая идея.





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. Что касается вашей третьей ссылки, в ответе Джона Лондона на его собственный вопрос есть одна строка, которая упрощает мой код, но я все равно получаю исключение «Неверный дескриптор окна». Я все еще ищу ответ.
Вот окончательный рабочий код:
#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();
}
}
Хотя я не знаю, почему предыдущий код не работает, а этот работает, я надеюсь, что он будет полезен кому-то еще.
Зачем ты звонишь
MainWindow::Current(), когда у тебя уже естьthis? У вас в руках правильное главное окно, и вы игнорируете его и вместо этого получаете какое-то случайное главное окно.