contextfree / winrt-rust

Use and (eventually) make Windows Runtime APIs with Rust
Apache License 2.0
142 stars 10 forks source link

Interop with c++/cx #40

Closed beark closed 6 years ago

beark commented 7 years ago

I was wondering if there's some good way of calling into rust code I've written with winrt-rust from, eg, a XAML control I wrote in C++?

Trivial interop I've already managed, but I'd like to be able to write, say, an IAsyncAction in rust that I can call from C++, and this has so far proved difficult.

Specifically, I wrote the following fairly trivial thing in Rust and compiled as a static library:

#[no_mangle]
pub extern fn test() -> HString {
    HString::new("Hello from rust!")
}

#[no_mangle]
pub extern fn do_async_stuff() -> ComPtr<IAsyncOperation<StorageFile>> {
    let uri = Uri::create_uri(FastHString::new("ms-appx:///Assets/file.txt").deref()).unwrap();
    StorageFile::get_file_from_application_uri_async(&uri).unwrap()
}

And in my C++ control I have

extern "C" {
    extern HSTRING test();
    extern WhatGoesHere? do_async_stuff();
}

MainPage::MainPage()
{
    InitializeComponent();
    this->TXT->Text = ref new String(test());
}

I' don't think it's possible to express the type needed in C terms, so I suppose I would have to get around that by using, say, a void pointer and re-interpret cast it. But to what? I assume the hat pointer types are not what's used on the Rust side...

Boddlnagg commented 7 years ago

This is an interesting use case that I haven't thought about before (actually, I haven't thought much about use cases of winrt-rust at all 😄 ).

Unfortunately I don't know much about C++/CX, so I'm not sure how the hat pointers (^) there look at the ABI level. This documentation suggests that they are ABI compatible with raw pointers, so it might be possible to just write ^IAsyncOperation<StorageFile> on the C++/CX side where you have ComPtr<IAsyncOperation<StorageFile>> (both are just *IAsyncOperation<StorageFile> with additional "magic"). So maybe this works out of the box?

You might also be able to use the new C++/WinRT projection (https://github.com/Microsoft/cppwinrt) instead of C++/CX, but this depends on what you want to do from the C++ side. GUI development is probably hard with C++/WinRT because there is no GUI designer yet as far as I know.

beark commented 7 years ago

Interesting, it seems it does indeed work to just cast directly to the expected C++/CX types. It's a bit ugly, since the rust functions need to be declared in "C" on the C++-side, but it works. For reference, I now have:

Static rust lib

#[no_mangle]
pub extern fn do_async_stuff() -> ComPtr<IAsyncOperation<StorageFile>> {
    let uri = Uri::create_uri(FastHString::new("ms-appx:///Assets/file.txt").deref()).unwrap();
    StorageFile::get_file_from_application_uri_async(&uri).unwrap()
}

C++ "Universal application"

extern "C" {
    extern void* do_async_stuff();
}

MainPage::MainPage()
{
    InitializeComponent();

    auto temp = do_async_stuff();
    auto storageFileTask = reinterpret_cast<IAsyncOperation<Windows::Storage::StorageFile^>^>(temp);

    auto t = concurrency::create_task(storageFileTask);
    t.then([this](Windows::Storage::StorageFile^ file) {
        TXT->Text = file->FileType;
    });
}

And the XAML-defined TextBlock named TXT gets populated by the .filetype of the asset. Pretty cool.

Boddlnagg commented 6 years ago

I'm closing this, since it seems solved.