Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PickSingleFileAsync await call hangs app #1708

Closed
MRahilly opened this issue Apr 21, 2022 · 9 comments
Closed

PickSingleFileAsync await call hangs app #1708

MRahilly opened this issue Apr 21, 2022 · 9 comments
Labels
question Further information is requested

Comments

@MRahilly
Copy link

MRahilly commented Apr 21, 2022

Does a xaml app require additional API calls before showing a file picker that are not required by the C++/WinRT equivalent?

[package]
name = "xaml_code"
version = "0.0.0"
edition = "2018"

[dependencies]
futures = "0.3"

[dependencies.windows]
version = "0.35.0"
features = [
    "implement",
    "ApplicationModel_Activation",
    "Win32_System_Com",
    "UI_Xaml_Controls",
    "UI_Xaml",
    "UI_Xaml_Media",
    "UI",
    "Storage_Pickers",
    "Foundation_Collections",
    "Storage",
    "Foundation",
    "Storage_Streams",
    "Graphics_Imaging",
    "Media_Ocr",
    "ApplicationModel_Core",
    "UI_Core",
]

#![windows_subsystem = "windows"]
#![allow(non_snake_case)]

use windows::{
    core::{
        Result,
        HSTRING,
        implement,
    },
    ApplicationModel::Activation::LaunchActivatedEventArgs,
    Win32::System::Com::{
        CoInitializeEx,
        COINIT_MULTITHREADED,
    },
    UI::{
        Colors,
        Xaml::{
            Controls::{
                TextBlock,
            },
            Application,
            ApplicationInitializationCallback,
            Window,
            Media::{
                FontFamily,
                SolidColorBrush,
            },
            TextAlignment,
            TextWrapping,
            VerticalAlignment,
            FrameworkElement,
            IApplicationOverrides,
            IApplicationOverrides_Impl,
        },
    },
    Storage::{
        StorageFile,
        Pickers::{
            FileOpenPicker,
            PickerLocationId,
        },
    },
};

#[implement(IApplicationOverrides)]
struct App();

impl IApplicationOverrides_Impl for App {
    fn OnLaunched(&self, _: &Option<LaunchActivatedEventArgs>) -> Result<()> {
        let block = TextBlock::new()?;

        block.SetFontFamily(FontFamily::CreateInstanceWithName(HSTRING::from("Segoe UI Semibold"))?)?;
        block.SetFontSize(72.0)?;
        block.SetForeground(SolidColorBrush::CreateInstanceWithColor(Colors::Orange()?)?)?;
    
        FrameworkElement::from(&block).SetVerticalAlignment(VerticalAlignment::Center)?;
        block.SetTextAlignment(TextAlignment::Center)?;
        block.SetTextWrapping(TextWrapping::Wrap)?;
        
        let window = Window::Current()?;
        window.SetContent(&block)?;
        window.Activate()?;

        block.SetText(HSTRING::from("Thursday earlier..."))?;

        futures::executor::block_on(main_async(&block))
    }
}

async fn main_async(block: &TextBlock) -> Result<()> {
    block.SetText(HSTRING::from("Thursday later..."))?;

    let picker = FileOpenPicker::new()?;
    picker.FileTypeFilter()?.Append(HSTRING::from(".png"))?;
    picker.SetSuggestedStartLocation(PickerLocationId::PicturesLibrary)?;
    
    //let result: Result<StorageFile> = picker.PickSingleFileAsync()?.await; // picker does not appear, app hangs
    //let file: StorageFile = picker.PickSingleFileAsync()?.await?;          // picker does not appear, app hangs

    Ok(())
}

fn main() -> Result<()> {
    unsafe {
        CoInitializeEx(std::ptr::null(), COINIT_MULTITHREADED)?;
    }
    Application::Start(ApplicationInitializationCallback::new(|_| {
        Application::compose(App())?;

        Ok(())
    }))
}
@kennykerr
Copy link
Collaborator

futures::executor::block_on blocks the calling thread, the UI thread in this case, which is likely why the app hangs.

@MRahilly
Copy link
Author

So a thread::spawn() maybe using an Arc<Mutex<>> could be the way to go here?

@kennykerr
Copy link
Collaborator

The problem is in the difference between C++/WinRT coroutines and Rust async functions. WinRT async assumes hot-start whereas async functions are cold-start, meaning the Rust async function needs a driver whereas WinRT async is meant to be self-driving. This is something I'm still noodling on, but that's the gist of the problem.

@kennykerr
Copy link
Collaborator

And no, using another thread won't help because then the UI calls will originate from a non-UI thread and that is not typically supported.

@kennykerr kennykerr added the question Further information is requested label Apr 21, 2022
@MRahilly
Copy link
Author

Thanks for the explanation.
We await resume_background_ferris() and resume_foreground_ferris()

@aWeinzierl
Copy link

And no, using another thread won't help because then the UI calls will originate from a non-UI thread and that is not typically supported.

What about using RunAsync?

Schedules the provided callback on the UI thread from a worker thread, and returns the results asynchronously.

@kennykerr
Copy link
Collaborator

Yes, that's what C++/WinRT uses behind the scenes for resume_foreground - that may work.

@MRahilly
Copy link
Author

MRahilly commented Apr 21, 2022

The closures for RunAsync lead to 'static lifetime requirements on the xaml app Window - leading to cumbersome code.

@kennykerr
Copy link
Collaborator

Yes, there are already issues tracking improvements to delegates (including from you). #1110 #332 #313 #1465

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants