Thank you for this interesting works! I have a question.
The codes (main.rs) below hang when running with cargo run, probably because we are blocking the UI thread.
use winrt::*;
use tokio;
// Now use the `import` macro to import the desired winmd files and types:
import!(
dependencies
os
types
windows::application_model::data_transfer::*
);
#[tokio::main]
async fn main() -> Result<()> {
use windows::application_model::data_transfer::*;
let content = DataPackage::new()?;
content.set_text("Rust/WinRT")?;
Clipboard::set_content(content)?;
Clipboard::flush()?;
let view = Clipboard::get_content()?;
for format in view.available_formats()? {
println!("{}", format);
}
let text = view.get_text_async()?.await?;
println!("{}", text);
Ok(())
}
use winrt::*;
// Now use the `import` macro to import the desired winmd files and types:
import!(
dependencies
os
types
windows::application_model::data_transfer::*
);
async fn async_main() -> Result<()> {
use windows::application_model::data_transfer::*;
let content = DataPackage::new()?;
content.set_text("Rust/WinRT")?;
Clipboard::set_content(content)?;
Clipboard::flush()?;
let view = Clipboard::get_content()?;
for format in view.available_formats()? {
println!("{}", format);
}
let text = view.get_text_async()?.await?;
println!("{}", text);
Ok(())
}
fn main() -> Result<()> {
futures::executor::block_on(async_main())
}
I could get clipboard history contents, but getting clipboard content still blocks maybe because clipboard history API does not require UI thread.
use winrt::*;
// Now use the `import` macro to import the desired winmd files and types:
import!(
dependencies
os
types
windows::application_model::data_transfer::*
windows::foundation::collections::*
);
async fn async_main() -> Result<()> {
use windows::application_model::data_transfer::*;
let content = DataPackage::new()?;
content.set_text("Rust/WinRT")?;
Clipboard::set_content(content)?;
Clipboard::flush()?;
println!("{}", Clipboard::is_history_enabled()?);
if Clipboard::is_history_enabled()? != true {
return Ok(())
}
let history_items = Clipboard::get_history_items_async()?.await?.items()?;
for idx in 0.. history_items.size()? {
let history_item = history_items.get_at(idx)?.content()?;
if history_item.available_formats()?.size()? == 0 {
continue;
}
// succeeded!
let item_str = history_item.get_text_async()?.await?;
println!("{}", item_str);
}
let view = Clipboard::get_content()?;
for format in view.available_formats()? {
println!("{}", format);
}
// blocks here
let text = view.get_text_async()?.await?;
println!("{}", text);
Ok(())
}
fn main() -> Result<()> {
futures::executor::block_on(async_main())
}
@jonwis do you know whether the clipboard APIs work on a non-UI thread? I know basic set_content(value) ... flush() works on an MTA, but I'm not sure whether this is officially supported and whether other clipboard capabilities ought to work.
Paging @metathinker - do you know offhand? I'd expect that they don't, but I'd also expect them to fail deterministically with a "you are on the wrong thread" error if that's the case. :)
@kennykerr @jonwis Clipboard.SetContent() should _not_ work on a UWP app's non-UI thread, or indeed any thread that previously CoInitializeEx'd itself into the multi-threaded apartment, and I'm surprised that it does.
Clipboard's activation factory is not agile, so attempting to call any static methods of Clipboard from an MTA thread _should_ fail with a "you are on the wrong thread" error (specifically, RoGetActivationFactory would fail with HRESULT status code RO_E_UNSUPPORTED_FROM_MTA (0x8000001d)).
If we can call SetContent() from the MTA, then wow, that's something interesting indeed.
Just as an update and clarification, only get_text_async API call hangs as far as I know.
get_text_async also hangs with LocalPool thread, which must be the same as the first thread.
fn main() {
let mut pool = futures::executor::LocalPool::new();
pool.run_until(async_main()); // here
}
Looping futures::executor::block_on with timeout also does not work (infinite loop).
(inspired by but actually irrelevant to Matts966's previous comments)
Hmm... maybe I was completely confused when I said no Windows.ApplicationModel.DataTransfer.Clipboard methods should work from the MTA. Maybe @chazgo might understand better?
I see that we deliberately removed the statement that Clipboard is not agile in this doc change: https://github.com/MicrosoftDocs/winrt-api/commit/24629b9c005b7c6a5937fdf993b1aed18c1b8f95#diff-685977be3a7587b238b7c073217a2240 - so maybe it is agile after all?
If so, it could conceivably be called from the MTA even though it always lives in an STA.
Then again, we still say there that apps can only use the clipboard when in foreground and on the UI thread - which is not specific to W.AM.DT.Clipboard, and is only true for UWP apps, as far as I know. Classic desktop apps that run with all normal user rights ("full trust") aren't subject to the foreground restriction, and the UI thread restriction is just the UWP way of stating the STA restriction. (Desktop apps that run in AppContainers are subject to the foreground restriction.) And desktop apps definitely are able to call W.AM.DT.Clipboard methods - I've done it myself.
Just as an update and clarification, only
get_text_asyncAPI call hangs as far as I know.
If only DataPackageView::GetTextAsync fails, it's not _technically_ a Clipboard method that's failing, but rather DataPackageView - and I know for a fact that that class _is_ agile.
So if I take Kenny's question about calling Clipboard from a non-UI thread and replace Clipboard with DataPackageView, I can confidently answer: Yes, that should work. If it doesn't, that's a bug somewhere - in the calling app, the projection, or the DataPackageView implementation.
Minor correction: it works on an implicit but not explicit MTA. So this API is configured (if not designed) only to work on a UI thread. That is unfortunate.
@Matts966 Sorry for the delay as I've been away. Practically speaking, you will need to use this API from an STA thread (with a message pump) in order to get it to work reliably. I would probably wrap it up in a helper library that creates a background STA thread and does the work so that the main console app doesn't require this complexity.