Windows-rs: Application::start thread marshalling

Created on 25 Nov 2020  路  22Comments  路  Source: microsoft/windows-rs

When calling Application::start with a callback of type ApplicationInitializationCallback, regardless of the contents of the passed closure, I receive the following errors:

onecoreuap\shell\coreapplication\application\lib\ApartmentHelpers.h(22)\twinapi.appcore.dll!00007FFE952A84D6: (caller: 00007FFE95274C82) ReturnHr(1) tid(2b10) 8001010E The application called an interface that was marshalled for a different thread.
    CallContext:[\AppRunOrActivateView] 
onecoreuap\shell\coreapplication\application\lib\coreapplication.cpp(77)\twinapi.appcore.dll!00007FFE952A84F1: (caller: 00007FFE95274C82) ReturnHr(2) tid(2b10) 8001010E The application called an interface that was marshalled for a different thread.
    CallContext:[\AppRunOrActivateView] 
onecoreuap\shell\coreapplication\application\lib\coreapplicationfactory.cpp(147)\twinapi.appcore.dll!00007FFE952C4D54: (caller: 00007FFE76696C9E) ReturnHr(3) tid(2b10) 8001010E The application called an interface that was marshalled for a different thread.

This error occurs before the contents of the passed closure are executed. This would appear to be a threading issue, with the callback being run off the UI thread, but I don't see why this should occur given the code:

use winrt::*;
use bindings::windows::ui::xaml::{Application, ApplicationInitializationCallback};

fn main() -> Result<()> {
    Application::start(ApplicationInitializationCallback::new(|_| {
        Ok(())
    }))?;

    Ok(())
}

UWP app initialization information can be rather tough to come by, so I'm not sure if I'm doing something wrong here, or if perhaps there's some threading going on behind the scenes (or the error message is misleading).

Most helpful comment

@DJankauskas If you bother to deal with low-level IUnknown implementation, please take a look at how I "derive" an Application here https://github.com/bdbai/firstuwp-rs/blob/master/app/src/app.rs .
Just as @kennykerr said, you basically need to create an instance from IApplicationFactory as a base object, and then delegate QueryInterface calls to it. Then only your object behaves like a real Application instance.
Anyway, the ultimate solution is to wait for object inheritance in winrt-rs.

All 22 comments

Application::start assumes the caller is running within an app container. There's also something called Xaml Islands that lets you host Xaml from a regular desktop app, but if you want to use the app container approach (which is simpler in some ways) then you'll need to set up the app container. I haven't tried this myself but I believe @robmikh has had some success and may be able to point you to an example or some instructions.

I'm actually wrestling with something similar. The current uwp branch of Minesweeper attempts to build a frameworkless Composition application. The issue hits when trying to call CoreApplication::Run, with the same RPC_E_WRONG_THREAD error. I'll report back when I've had a chance to debug it to see if this is the platform or the projection.

Here's what I've tried so far: in .cargo/config I've added the configuration rustflags = ["-C", "link-args=/APPCONTAINER"], and I register the resulting exe with the standard Add-AppxPackage -Register AppxManifest.xml (with an appropriate manifest file). Is there some other step I'm missing?

The trick is that the VC CRT creates an explicit MTA before entering main. This is what CoreApplication and Xaml's Application require. Rust/WinRT and C++/WinRT will merely attempt to create an implicit MTA on demand as this is enough for most APIs. For some reason, this isn't enough for CoreApplication/Xaml. The solution is simple enough. Just call CoInitializeEx(0, COINIT_MULTITHREADED) before making any API calls. Hope that helps.

Note that the VC CRT only does this for the "app CRT" and not the vanilla CRT.

Thanks, I'll try that out right now. Does winrt export that, or do I need to bring in the winapi crate?

No, you'll have to import that yourself.

Let me shamelessly share this again 馃槀 https://github.com/bdbai/firstuwp-rs . This is an working UWP example that you can take a look at.
*-uwp-windows-msvc targets are used so that /APPCONTAINER and some UWP-specific libraries are preconfigured for you.
At the entry point, RoIntialize is manually called for subsequent WinRT invocations to work.

Something like this:

#[link(name = "ole32")]
extern "system" {
    fn CoInitializeEx(_: usize, apartment: u32) -> winrt::ErrorCode;
}

fn main() -> Result<()> {
    unsafe {
        CoInitializeEx(0, 0);
    }

    // Your code...

    Ok(())
}

Thanks @bdbai - I've been meaning to look at your sample. 馃槈

Yes, RoIntialize will also do - there's no practical difference when it comes to MTA so either way.

@kennykerr The CoInitializeEx trick worked, except now I got another error, I'll see if bdbai's code helps with that issue.

@bdbai I haven't yet implemented weak references (required for Xaml but not CoreApplication) but I'd love any early feedback on the implement macro https://github.com/microsoft/winrt-rs/pull/363 - that should simplify this kind of thing substantially. I'll get on to weak references asap. https://github.com/microsoft/winrt-rs/issues/366

@kennykerr thanks! I will try it once I get free.

@DJankauskas IIRC AppContainer or the targets doesn't matter for debugging. The thing is you need to register your app and launch it from Start.

Thanks, @bdbai! Your instructions for building for the uwp-windows-msvc target was exactly what I needed. I was trying to get away with the pc version along with some hacks, and it wasn't working out. Once running:

cargo +nightly build -Z build-std=std,panic_abort --target x86_64-uwp-windows-msvc

Everything worked! I also didn't need to call RoInitialize/CoInitializeEx.

This is a bit unrelated to the thread, but might be useful to keep initialization information in one place: thanks to all your help, I've been able to get a Rust UWP app to run without crashing, but the UWP runtime does not seem to recognize my App type as an Application object, despite the fact that it implements IApplication through the implement attribute macro. Is there a way to explicitly provide my struct as an Application to the runtime? Or does App need to be a WinRT class?

Are you missing a cast? For example, MinesweeperAppSource implements IFrameworkViewSource. To provide it to CoreApplication::Run, I do the following:

let app_source = MinesweeperAppSource {};
let view_source: IFrameworkViewSource = app_source.into();
CoreApplication::run(&view_source)?;

I haven't taken a deep look at your minesweeper app, but I take it you're doing some Direct2D or composition work which only requires CoreApplication, but I'm trying to get Xaml UI elements running, which I believe requires creating a class implementing IApplication (and possibly inheriting from Application). That involves the Application::start() method, not CoreApplication::run(). (Application::start() takes a closure in which you need to instantiate such a class, so I can't just pass my struct as a parameter. The runtime somehow seems to detect when you create it, perhaps hooking into Application's constructor.)

Right, I was just giving an example where I had to cast. I'm not sure if there's enough implemented for XAML support.

Yeah, I suspect it might be that I'll need to wait for class support, hopefully @kennykerr can confirm that's indeed the problem.

Right, the CoreApplication approach works today because it only requires you to implement two interfaces. The Xaml Application won't work out-the-box because it requires you to logically "derive" from the Xaml Application class. Under the hood that involves COM aggregation where you logically inherit the IApplication implementation provided by Xaml. Implementing that yourself won't work. So this isn't something I have support for yet, but it is on the way. If you're impatient you can try to hook it up yourself by looking at what C++/WinRT does. Basically you need to use IApplicationFactory::create_instance and provide your implementation of IApplicationOverrides etc and hold on to the inner provided by Xaml to satisfy aggregation. This is definitely non-trivial so you may want to wait for me to plumb it in directly into Rust/WinRT.

@DJankauskas If you bother to deal with low-level IUnknown implementation, please take a look at how I "derive" an Application here https://github.com/bdbai/firstuwp-rs/blob/master/app/src/app.rs .
Just as @kennykerr said, you basically need to create an instance from IApplicationFactory as a base object, and then delegate QueryInterface calls to it. Then only your object behaves like a real Application instance.
Anyway, the ultimate solution is to wait for object inheritance in winrt-rs.

Thanks for all of your help! I think I'll wait for inheritance support, because on the face of it, it seems that winrt::factory has been made private, and there is probably a whole can of worms past that. I did learn a bit about the UWP application model though, so I'm still glad to have experimented with this at this stage.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ctaggart picture ctaggart  路  4Comments

rylev picture rylev  路  3Comments

joverwey picture joverwey  路  3Comments

13r0ck picture 13r0ck  路  4Comments

06393993 picture 06393993  路  3Comments