0

This is a follow-on question to Guarang Dave's question. With @Simon_Mourier's help, he figured it out, but I still haven't. I'm trying to do what ought to be a simple task: open a FileOpenPicker from MainWindow. The straightforward code is this:

    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();
        }
    }

The result is an "invalid window handle" exception. Window handle!? What window handle? This sent me down the rabbit hole of trying to figure out how to get PickSingleFileAsync() associated with the correct file handle.

Here's my next attempt, based on Guarang Dave's question:

        // 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();

Unfortunately, this still gives the "invalid window handle" exception. Replacing fire_and_forget with Windows::Foundation::IAsyncAction doesn't help either. My next step was to implement Simon Mournier's GetProcessFirstWindowHandle(). Here's the code:

        // 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();

The result is the same exception.

Finally, I implemented a method of having a static Window member of MainWindow, and a convoluted one of having a Window member of App based onthis C# code, but neither helped. Here's my code with all sorts of non-working methods commented out:

    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();
        }
    }

So far, everything I've tried leads to an "invalid window handle" exception on the first line of the try block. One thing that's still totally opaque to me is Simon's reminder that the FileOpenPicker must run on the UI thread. I have not intentionally created any other threads, so is any MainWindow::function on the UI thread or not? The main question is, of course, how do I get a valid window handle?

7
  • Why are you calling MainWindow::Current() when you already have a this? You have the correct Main window in your hand and you ignore it and get some random Main window instead. Commented May 29, 2024 at 3:30
  • 2
    You never check whether windowNative->get_WindowHandle(&hWnd); or initializeWithWindow->Initialize(hWnd); succeed, so the problem could be happening long before you call PickSingleFileAsync. Here's an example of getting an HWND from a MainWindow: github.com/microsoft/Windows-classic-samples/blob/… Commented May 29, 2024 at 22:21
  • 1
    Could you please post a minimal reproducible example? Commented May 29, 2024 at 22:29
  • @JesperJuhl Thanks for asking about the minimal reproducible example. I created a C++ WinUI 3 app in Desktop and replaced the "myButton_Click" handler with the FileOpenClickHandler code shown above. It worked! Now I know that either I messed up my code somewhere else or I'm getting the exception because I'm calling FileOpenClickHandler from a MenuBarItem. It's easier to test the latter, so I'll add a MenuBar to the dummy app. Commented May 30, 2024 at 20:28
  • @JesperJuhl Good news/bad news: The good news is that the FileOpenPicker worked from the MenuBarItem. The bad news is that I'll have to rewrite my app from scratch to eiliminate whatever garbage is causing the problem. Many thanks. Commented May 30, 2024 at 20:38

2 Answers 2

0

Here's the final working code:

#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();
        }
    }

Although I don't know why the earlier code doesn't work and this does, I hope it's useful to someone else.

Sign up to request clarification or add additional context in comments.

Comments

-3

1,I suggest you could refer to the thread:https://stackoverflow.com/a/75440195

Only Window implements IWindowNative.

2,FileOpenPicker is not under Windows::UI.

I suggest you could try to use IInitializeWithWindow refer to the Blog: https://devblogs.microsoft.com/oldnewthing/20190412-00/?p=102413.

And you could refer to the thread: "Invalid window handle" error when using FileOpenPicker from C++/WinRT without UWP

2 Comments

A link to a solution is welcome, but please ensure your answer is useful without it: add context around the link so your fellow users will have some idea what it is and why it’s there, then quote the most relevant part of the page you're linking to in case the target page is unavailable. Answers that are little more than a link may be deleted.
@Jeaninez Thanks for responding. Your first link is an answer referenced in my code comments; I've already tried that. Your second link is interesting, but it doesn't tell me how to get an HWND for the MainWindow in a WinUI 3 app. Regarding your third link, there's one line in John London's answer to his own question that simplifies my code, but I still get the "Invalid window handle" exception. I'm still looking for an answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.