From 263755005a25956ddf84a0854afa3618d244efd4 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 15 Feb 2022 11:59:32 +0100 Subject: [PATCH 01/13] Parse interface macro --- crates/libs/implement/Cargo.toml | 2 +- crates/libs/interface/Cargo.toml | 19 ++++ crates/libs/interface/src/lib.rs | 148 ++++++++++++++++++++++++++++ crates/libs/windows/Cargo.toml | 2 + crates/libs/windows/src/core/mod.rs | 3 + crates/tests/interface/Cargo.toml | 11 +++ crates/tests/interface/src/lib.rs | 1 + crates/tests/interface/tests/com.rs | 11 +++ 8 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 crates/libs/interface/Cargo.toml create mode 100644 crates/libs/interface/src/lib.rs create mode 100644 crates/tests/interface/Cargo.toml create mode 100644 crates/tests/interface/src/lib.rs create mode 100644 crates/tests/interface/tests/com.rs diff --git a/crates/libs/implement/Cargo.toml b/crates/libs/implement/Cargo.toml index 175b895ec0..09351be16c 100644 --- a/crates/libs/implement/Cargo.toml +++ b/crates/libs/implement/Cargo.toml @@ -4,7 +4,7 @@ version = "0.32.0" authors = ["Microsoft"] edition = "2018" license = "MIT OR Apache-2.0" -description = "Macros for the windows crate" +description = "The implement macro for the windows crate" repository = "https://github.com/microsoft/windows-rs" [package.metadata.docs.rs] diff --git a/crates/libs/interface/Cargo.toml b/crates/libs/interface/Cargo.toml new file mode 100644 index 0000000000..c946ee2e5c --- /dev/null +++ b/crates/libs/interface/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "windows-interface" +version = "0.32.0" +edition = "2021" +authors = ["Microsoft"] +license = "MIT OR Apache-2.0" +description = "The interface macro for the windows crate" +repository = "https://github.com/microsoft/windows-rs" + +[package.metadata.docs.rs] +default-target = "x86_64-pc-windows-msvc" +targets = [] + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "1.0", default-features = false, features = ["parsing", "proc-macro", "printing", "full", "derive"] } +tokens = { package = "windows-tokens", path = "../tokens", version = "0.32.0" } diff --git a/crates/libs/interface/src/lib.rs b/crates/libs/interface/src/lib.rs new file mode 100644 index 0000000000..52f9606d5d --- /dev/null +++ b/crates/libs/interface/src/lib.rs @@ -0,0 +1,148 @@ +use syn::parse::{Parse, ParseStream}; +use syn::spanned::Spanned; +use tokens::quote; + +#[proc_macro_attribute] +pub fn interface(attributes: proc_macro::TokenStream, original_type: proc_macro::TokenStream) -> proc_macro::TokenStream { + let attributes = syn::parse_macro_input!(attributes as Guid); + let typ = syn::parse_macro_input!(original_type as Interface); + let tokens = quote! { + compile_error!("`interface` macro is not yet implemented"); + }; + tokens.parse::().unwrap() +} +macro_rules! bail { + ($item:expr, $($msg:tt),*) => { + return Err(syn::Error::new($item.span(), std::fmt::format(format_args!($($msg),*)))); + }; + +} + +macro_rules! unexpected_token { + ($item:expr, $msg:expr) => { + if let Some(i) = $item { + bail!(i, "unexpected {}", $msg); + } + }; +} +macro_rules! expected_token { + ($sig:tt.$item:tt(), $msg:expr) => { + if let None = $sig.$item() { + bail!($sig, "expected {}", $msg); + } + }; +} + +/// Parsed interface guid attribute +/// +/// ```rust +/// #[interface("0000010c-0000-0000-C000-000000000046")] +/// //^ this function parses this +/// unsafe trait IFoo {} +/// ``` +struct Guid(syn::LitStr); + +impl Parse for Guid { + fn parse(cursor: ParseStream) -> syn::Result { + let string: syn::LitStr = cursor.parse()?; + + Ok(Self(string)) + } +} + +/// Parsed interface +/// +/// ```rust +/// #[interface("0000010c-0000-0000-C000-000000000046")] +/// unsafe trait IFoo {} +/// //^ this function parses this +/// ``` +struct Interface { + pub visibility: syn::Visibility, + pub name: syn::Ident, + pub parent: Option, + pub methods: Vec, + docs: Vec, +} + +impl Parse for Interface { + fn parse(input: ParseStream) -> syn::Result { + let attributes = input.call(syn::Attribute::parse_outer)?; + let mut docs = Vec::new(); + for attr in attributes.into_iter() { + let path = &attr.path; + let tokens = &attr.tokens; + if path.is_ident("doc") { + docs.push(attr); + } else { + return Err(syn::Error::new(path.span(), "Unrecognized attribute ")); + } + } + + let visibility = input.parse::()?; + let _ = input.parse::()?; + let interface = input.parse::()?; + let name = input.parse::()?; + let mut parent = None; + if name != "IUnknown" { + let _ = input.parse::().map_err(|_| syn::Error::new(name.span(), format!("Interfaces must inherit from another interface like so: `interface {}: IParentInterface`", name)))?; + parent = Some(input.parse::()?); + } + let content; + syn::braced!(content in input); + let mut methods = Vec::new(); + while !content.is_empty() { + methods.push(content.parse::()?); + } + Ok(Self { visibility, methods, name, parent, docs }) + } +} + +struct InterfaceMethod { + pub name: syn::Ident, + pub visibility: syn::Visibility, + pub args: Vec, + pub ret: syn::ReturnType, + pub docs: Vec, +} + +impl syn::parse::Parse for InterfaceMethod { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let docs = input.call(syn::Attribute::parse_outer)?; + let visibility = input.parse::()?; + let method = input.parse::()?; + unexpected_token!(docs.iter().find(|a| !a.path.is_ident("doc")), "attribute"); + unexpected_token!(method.default, "default method implementation"); + let sig = method.sig; + unexpected_token!(sig.abi, "abi declaration"); + unexpected_token!(sig.asyncness, "async declaration"); + unexpected_token!(sig.generics.params.iter().next(), "generics declaration"); + unexpected_token!(sig.constness, "const declaration"); + expected_token!(sig.receiver(), "the method to have &self as its first argument"); + unexpected_token!(sig.variadic, "variadic args"); + let args = sig + .inputs + .into_iter() + .filter_map(|a| match a { + syn::FnArg::Receiver(_) => None, + syn::FnArg::Typed(p) => Some(p), + }) + .map(|p| { + let mut filter = p.attrs.iter().filter(|a| a.path.is_ident("pass_through")).fuse(); + let pass_through = filter.next().is_some(); + + unexpected_token!(filter.next(), "function attribute"); + Ok(InterfaceMethodArg { ty: p.ty, pat: p.pat, pass_through }) + }) + .collect::, syn::Error>>()?; + + let ret = sig.output; + Ok(InterfaceMethod { name: sig.ident, visibility, args, ret, docs }) + } +} + +struct InterfaceMethodArg { + pub ty: Box, + pub pat: Box, + pub pass_through: bool, +} diff --git a/crates/libs/windows/Cargo.toml b/crates/libs/windows/Cargo.toml index fa6a58924d..34213f9690 100644 --- a/crates/libs/windows/Cargo.toml +++ b/crates/libs/windows/Cargo.toml @@ -46,12 +46,14 @@ windows_x86_64_gnu = { path = "../../targets/x86_64_gnu", version = "0.32.0" } [dependencies] windows-implement = { path = "../implement", version = "0.32.0", optional = true } +windows-interface = { path = "../interface", version = "0.32.0", optional = true } [features] default = [] deprecated = [] alloc = [] implement = ["windows-implement"] +interface = ["windows-interface"] AI = [] AI_MachineLearning = ["AI"] AI_MachineLearning_Preview = ["AI_MachineLearning"] diff --git a/crates/libs/windows/src/core/mod.rs b/crates/libs/windows/src/core/mod.rs index cf23f6f6e6..7891497dd8 100644 --- a/crates/libs/windows/src/core/mod.rs +++ b/crates/libs/windows/src/core/mod.rs @@ -82,6 +82,9 @@ pub type RawPtr = *mut core::ffi::c_void; #[cfg(feature = "implement")] pub use windows_implement::implement; +#[cfg(feature = "interface")] +pub use windows_interface::interface; + extern "C" { #[doc(hidden)] pub fn memcmp(left: *const core::ffi::c_void, right: *const core::ffi::c_void, len: usize) -> i32; diff --git a/crates/tests/interface/Cargo.toml b/crates/tests/interface/Cargo.toml new file mode 100644 index 0000000000..0fa79d7128 --- /dev/null +++ b/crates/tests/interface/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "test_interface" +version = "0.0.0" +authors = ["Microsoft"] +edition = "2018" + +[dependencies.windows] +path = "../../libs/windows" +features = [ + "interface", +] diff --git a/crates/tests/interface/src/lib.rs b/crates/tests/interface/src/lib.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/crates/tests/interface/src/lib.rs @@ -0,0 +1 @@ + diff --git a/crates/tests/interface/tests/com.rs b/crates/tests/interface/tests/com.rs new file mode 100644 index 0000000000..0139ab01f7 --- /dev/null +++ b/crates/tests/interface/tests/com.rs @@ -0,0 +1,11 @@ +use windows::core::*; + +#[interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] +unsafe trait IUIAnimationVariable: IUnknown { + fn GetValue(&self, value: *mut f64) -> HRESULT; +} + +#[test] +fn mix() -> Result<()> { + Ok(()) +} From 99a2ebe21ad339b70b498cef5cacad8145cba2f7 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 15 Feb 2022 15:59:41 +0100 Subject: [PATCH 02/13] Begin code generation --- crates/libs/interface/Cargo.toml | 3 +- crates/libs/interface/src/lib.rs | 137 +++++++++++++++++++++++-- crates/libs/tokens/src/token_stream.rs | 1 + crates/tests/interface/Cargo.toml | 2 +- 4 files changed, 132 insertions(+), 11 deletions(-) diff --git a/crates/libs/interface/Cargo.toml b/crates/libs/interface/Cargo.toml index c946ee2e5c..33635d51c7 100644 --- a/crates/libs/interface/Cargo.toml +++ b/crates/libs/interface/Cargo.toml @@ -16,4 +16,5 @@ proc-macro = true [dependencies] syn = { version = "1.0", default-features = false, features = ["parsing", "proc-macro", "printing", "full", "derive"] } -tokens = { package = "windows-tokens", path = "../tokens", version = "0.32.0" } +quote = "1.0" +proc-macro2 = "1.0" diff --git a/crates/libs/interface/src/lib.rs b/crates/libs/interface/src/lib.rs index 52f9606d5d..caabe4a37e 100644 --- a/crates/libs/interface/src/lib.rs +++ b/crates/libs/interface/src/lib.rs @@ -1,16 +1,18 @@ +use quote::quote; use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; -use tokens::quote; #[proc_macro_attribute] pub fn interface(attributes: proc_macro::TokenStream, original_type: proc_macro::TokenStream) -> proc_macro::TokenStream { - let attributes = syn::parse_macro_input!(attributes as Guid); - let typ = syn::parse_macro_input!(original_type as Interface); - let tokens = quote! { - compile_error!("`interface` macro is not yet implemented"); + let guid = syn::parse_macro_input!(attributes as Guid); + let interface = syn::parse_macro_input!(original_type as Interface); + let tokens = match interface.gen_tokens(&guid) { + Ok(t) => t, + Err(e) => return e.to_compile_error().into(), }; - tokens.parse::().unwrap() + tokens.into() } + macro_rules! bail { ($item:expr, $($msg:tt),*) => { return Err(syn::Error::new($item.span(), std::fmt::format(format_args!($($msg),*)))); @@ -42,6 +44,66 @@ macro_rules! expected_token { /// ``` struct Guid(syn::LitStr); +impl Guid { + fn chunks(&self) -> syn::Result<[String; 5]> { + fn ensure_length<'a>(part: Option<&'a str>, index: usize, length: usize, span: proc_macro2::Span) -> syn::Result { + let part = match part { + Some(p) => p, + None => return Err(syn::Error::new(span, format!("The IID missing part at index {}", index,))), + }; + + if part.len() != length { + return Err(syn::Error::new(span, format!("The IID part at index {} must be {} characters long but was {} characters", index, length, part.len()))); + } + + Ok(part.to_owned()) + } + + let guid_value = self.0.value(); + let mut delimited = guid_value.split('-').fuse(); + let chunks = [ensure_length(delimited.next(), 0, 8, self.0.span())?, ensure_length(delimited.next(), 1, 4, self.0.span())?, ensure_length(delimited.next(), 2, 4, self.0.span())?, ensure_length(delimited.next(), 3, 4, self.0.span())?, ensure_length(delimited.next(), 4, 12, self.0.span())?]; + + Ok(chunks) + } + + fn to_tokens(&self) -> syn::Result { + fn hex_lit(num: &str) -> syn::LitInt { + syn::LitInt::new(&format!("0x{}", num), proc_macro2::Span::call_site()) + } + + let chunks = self.chunks()?; + let data1 = hex_lit(&chunks[0]); + let data2 = hex_lit(&chunks[1]); + let data3 = hex_lit(&chunks[2]); + let (data4_1, data4_2) = chunks[3].split_at(2); + let data4_1 = hex_lit(data4_1); + let data4_2 = hex_lit(data4_2); + let (data4_3, rest) = chunks[4].split_at(2); + let data4_3 = hex_lit(data4_3); + + let (data4_4, rest) = rest.split_at(2); + let data4_4 = hex_lit(data4_4); + + let (data4_5, rest) = rest.split_at(2); + let data4_5 = hex_lit(data4_5); + + let (data4_6, rest) = rest.split_at(2); + let data4_6 = hex_lit(data4_6); + + let (data4_7, data4_8) = rest.split_at(2); + let data4_7 = hex_lit(data4_7); + let data4_8 = hex_lit(data4_8); + Ok(quote! { + ::windows::core::GUID { + data1: #data1, + data2: #data2, + data3: #data3, + data4: [#data4_1, #data4_2, #data4_3, #data4_4, #data4_5, #data4_6, #data4_7, #data4_8] + } + }) + } +} + impl Parse for Guid { fn parse(cursor: ParseStream) -> syn::Result { let string: syn::LitStr = cursor.parse()?; @@ -65,6 +127,65 @@ struct Interface { docs: Vec, } +impl Interface { + fn gen_tokens(&self, guid: &Guid) -> syn::Result { + let vis = &self.visibility; + let name = &self.name; + // TODO: support non-IUnknown parents + let parent = quote!(::windows::core::IUnknown); + let vtable_name = quote::format_ident!("{}_Vtbl", name); + let vtable = self.gen_vtable(&vtable_name); + let guid = guid.to_tokens()?; + Ok(quote! { + #[repr(transparent)] + #vis struct #name(#parent); + + unsafe impl ::windows::core::Interface for #name { + type Vtable = #vtable_name; + const IID: ::windows::core::GUID = #guid; + } + + #vtable + }) + } + + fn gen_vtable(&self, vtable_name: &syn::Ident) -> proc_macro2::TokenStream { + let vtable_entries = self + .methods + .iter() + .map(|m| { + let name = &m.name; + if m.args.iter().any(|a| !a.pass_through) { + panic!("TODO: handle methods with non-pass through arguments"); + } + let args = &m + .args + .iter() + .map(|a| { + let pat = &a.pat; + let typ = &a.ty; + quote! { + #pat: #typ + + } + }) + .collect::>(); + quote! { + pub #name: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, #(#args),*) -> ::windows::core::HRESULT, + } + }) + .collect::>(); + quote! { + #[repr(C)] + #[doc(hidden)] + pub struct #vtable_name { + #(#vtable_entries)* + } + + } + } +} + impl Parse for Interface { fn parse(input: ParseStream) -> syn::Result { let attributes = input.call(syn::Attribute::parse_outer)?; @@ -128,10 +249,8 @@ impl syn::parse::Parse for InterfaceMethod { syn::FnArg::Typed(p) => Some(p), }) .map(|p| { - let mut filter = p.attrs.iter().filter(|a| a.path.is_ident("pass_through")).fuse(); - let pass_through = filter.next().is_some(); + let pass_through = matches!(&*p.ty, syn::Type::Ptr(_)); - unexpected_token!(filter.next(), "function attribute"); Ok(InterfaceMethodArg { ty: p.ty, pat: p.pat, pass_through }) }) .collect::, syn::Error>>()?; diff --git a/crates/libs/tokens/src/token_stream.rs b/crates/libs/tokens/src/token_stream.rs index f2c631f0dd..dd2debc8d1 100644 --- a/crates/libs/tokens/src/token_stream.rs +++ b/crates/libs/tokens/src/token_stream.rs @@ -142,6 +142,7 @@ macro_rules! unsuffixed { } impl Literal { + unsuffixed!(u128 => u128_unsuffixed); unsuffixed!(usize => usize_unsuffixed); unsuffixed!(u32 => u32_unsuffixed); unsuffixed!(u16 => u16_unsuffixed); diff --git a/crates/tests/interface/Cargo.toml b/crates/tests/interface/Cargo.toml index 0fa79d7128..74241912a3 100644 --- a/crates/tests/interface/Cargo.toml +++ b/crates/tests/interface/Cargo.toml @@ -2,7 +2,7 @@ name = "test_interface" version = "0.0.0" authors = ["Microsoft"] -edition = "2018" +edition = "2021" [dependencies.windows] path = "../../libs/windows" From 957bec2b9794597605b341297e1a1935f823a93e Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 15 Feb 2022 16:57:40 +0100 Subject: [PATCH 03/13] Add implementation and conversions --- crates/libs/interface/src/lib.rs | 94 +++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/crates/libs/interface/src/lib.rs b/crates/libs/interface/src/lib.rs index caabe4a37e..b28164eb39 100644 --- a/crates/libs/interface/src/lib.rs +++ b/crates/libs/interface/src/lib.rs @@ -134,11 +134,14 @@ impl Interface { // TODO: support non-IUnknown parents let parent = quote!(::windows::core::IUnknown); let vtable_name = quote::format_ident!("{}_Vtbl", name); - let vtable = self.gen_vtable(&vtable_name); let guid = guid.to_tokens()?; + let implementation = self.gen_implementation(); + let vtable = self.gen_vtable(&vtable_name); + let conversions = self.gen_conversions(); Ok(quote! { #[repr(transparent)] #vis struct #name(#parent); + #implementation unsafe impl ::windows::core::Interface for #name { type Vtable = #vtable_name; @@ -146,9 +149,54 @@ impl Interface { } #vtable + + #conversions }) } + fn gen_implementation(&self) -> proc_macro2::TokenStream { + let name = &self.name; + let methods = self + .methods + .iter() + .map(|m| { + let vis = &m.visibility; + let name = &m.name; + + if m.args.iter().any(|a| !a.pass_through) { + panic!("TODO: handle methods with non-pass through arguments"); + } + let args = &m + .args + .iter() + .map(|a| { + let pat = &a.pat; + let ty = &a.ty; + quote! { #pat: #ty } + }) + .collect::>(); + let params = &m + .args + .iter() + .map(|a| { + let pat = &a.pat; + quote! { #pat } + }) + .collect::>(); + quote! { + #vis unsafe fn #name(&self, #(#args)*) -> ::windows::core::HRESULT { + (::windows::core::Interface::vtable(self).#name)(::core::mem::transmute_copy(self), #(#params)*) + } + } + }) + .collect::>(); + quote! { + impl #name { + #(#methods)* + } + } + } + fn gen_vtable(&self, vtable_name: &syn::Ident) -> proc_macro2::TokenStream { let vtable_entries = self .methods @@ -181,7 +229,51 @@ impl Interface { pub struct #vtable_name { #(#vtable_entries)* } + } + } + fn gen_conversions(&self) -> proc_macro2::TokenStream { + let name = &self.name; + let name_string = format!("{}", name); + quote! { + impl ::core::convert::From<#name> for ::windows::core::IUnknown { + fn from(value: #name) -> Self { + // TODO: handle when direct parent is not IUnknown + value.0 + } + } + impl ::core::convert::From<&#name> for ::windows::core::IUnknown { + fn from(value: &#name) -> Self { + ::core::convert::From::from(::core::clone::Clone::clone(value)) + } + } + impl<'a> ::windows::core::IntoParam<'a, ::windows::core::IUnknown> for #name { + fn into_param(self) -> ::windows::core::Param<'a, ::windows::core::IUnknown> { + ::windows::core::Param::Owned(self.into()) + } + } + impl<'a> ::windows::core::IntoParam<'a, ::windows::core::IUnknown> for &'a #name { + fn into_param(self) -> ::windows::core::Param<'a, ::windows::core::IUnknown> { + // TODO: handle when direct parent is not IUnknown + ::windows::core::Param::Borrowed(&self.0) + } + } + impl ::core::clone::Clone for #name { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + } + impl ::core::cmp::PartialEq for #name { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + impl ::core::cmp::Eq for #name {} + impl ::core::fmt::Debug for #name { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_tuple(#name_string).field(&self.0).finish() + } + } } } } From c8da85936ebb41d52c718ad03d51dccf7c0c0fc9 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 15 Feb 2022 17:46:26 +0100 Subject: [PATCH 04/13] Clean up --- crates/libs/interface/src/lib.rs | 220 ++++++++++++++----------- crates/libs/tokens/src/token_stream.rs | 1 - crates/tests/interface/tests/com.rs | 2 +- 3 files changed, 122 insertions(+), 101 deletions(-) diff --git a/crates/libs/interface/src/lib.rs b/crates/libs/interface/src/lib.rs index b28164eb39..87f235d9a8 100644 --- a/crates/libs/interface/src/lib.rs +++ b/crates/libs/interface/src/lib.rs @@ -2,6 +2,15 @@ use quote::quote; use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; +/// A COM interface definition +/// +/// # Example +/// ```rust +/// #[interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] +/// unsafe trait IUIAnimationVariable: IUnknown { +/// fn GetValue(&self, value: *mut f64) -> HRESULT; +/// } +/// ``` #[proc_macro_attribute] pub fn interface(attributes: proc_macro::TokenStream, original_type: proc_macro::TokenStream) -> proc_macro::TokenStream { let guid = syn::parse_macro_input!(attributes as Guid); @@ -35,89 +44,12 @@ macro_rules! expected_token { }; } -/// Parsed interface guid attribute -/// -/// ```rust -/// #[interface("0000010c-0000-0000-C000-000000000046")] -/// //^ this function parses this -/// unsafe trait IFoo {} -/// ``` -struct Guid(syn::LitStr); - -impl Guid { - fn chunks(&self) -> syn::Result<[String; 5]> { - fn ensure_length<'a>(part: Option<&'a str>, index: usize, length: usize, span: proc_macro2::Span) -> syn::Result { - let part = match part { - Some(p) => p, - None => return Err(syn::Error::new(span, format!("The IID missing part at index {}", index,))), - }; - - if part.len() != length { - return Err(syn::Error::new(span, format!("The IID part at index {} must be {} characters long but was {} characters", index, length, part.len()))); - } - - Ok(part.to_owned()) - } - - let guid_value = self.0.value(); - let mut delimited = guid_value.split('-').fuse(); - let chunks = [ensure_length(delimited.next(), 0, 8, self.0.span())?, ensure_length(delimited.next(), 1, 4, self.0.span())?, ensure_length(delimited.next(), 2, 4, self.0.span())?, ensure_length(delimited.next(), 3, 4, self.0.span())?, ensure_length(delimited.next(), 4, 12, self.0.span())?]; - - Ok(chunks) - } - - fn to_tokens(&self) -> syn::Result { - fn hex_lit(num: &str) -> syn::LitInt { - syn::LitInt::new(&format!("0x{}", num), proc_macro2::Span::call_site()) - } - - let chunks = self.chunks()?; - let data1 = hex_lit(&chunks[0]); - let data2 = hex_lit(&chunks[1]); - let data3 = hex_lit(&chunks[2]); - let (data4_1, data4_2) = chunks[3].split_at(2); - let data4_1 = hex_lit(data4_1); - let data4_2 = hex_lit(data4_2); - let (data4_3, rest) = chunks[4].split_at(2); - let data4_3 = hex_lit(data4_3); - - let (data4_4, rest) = rest.split_at(2); - let data4_4 = hex_lit(data4_4); - - let (data4_5, rest) = rest.split_at(2); - let data4_5 = hex_lit(data4_5); - - let (data4_6, rest) = rest.split_at(2); - let data4_6 = hex_lit(data4_6); - - let (data4_7, data4_8) = rest.split_at(2); - let data4_7 = hex_lit(data4_7); - let data4_8 = hex_lit(data4_8); - Ok(quote! { - ::windows::core::GUID { - data1: #data1, - data2: #data2, - data3: #data3, - data4: [#data4_1, #data4_2, #data4_3, #data4_4, #data4_5, #data4_6, #data4_7, #data4_8] - } - }) - } -} - -impl Parse for Guid { - fn parse(cursor: ParseStream) -> syn::Result { - let string: syn::LitStr = cursor.parse()?; - - Ok(Self(string)) - } -} - /// Parsed interface /// /// ```rust /// #[interface("0000010c-0000-0000-C000-000000000046")] /// unsafe trait IFoo {} -/// //^ this function parses this +/// //^ parses this /// ``` struct Interface { pub visibility: syn::Visibility, @@ -128,6 +60,7 @@ struct Interface { } impl Interface { + /// Generates all the code needed for a COM interface fn gen_tokens(&self, guid: &Guid) -> syn::Result { let vis = &self.visibility; let name = &self.name; @@ -154,6 +87,7 @@ impl Interface { }) } + /// Generates the methods users can call on the COM interface pointer fn gen_implementation(&self) -> proc_macro2::TokenStream { let name = &self.name; let methods = self @@ -166,15 +100,7 @@ impl Interface { if m.args.iter().any(|a| !a.pass_through) { panic!("TODO: handle methods with non-pass through arguments"); } - let args = &m - .args - .iter() - .map(|a| { - let pat = &a.pat; - let ty = &a.ty; - quote! { #pat: #ty } - }) - .collect::>(); + let args = m.gen_args(); let params = &m .args .iter() @@ -197,6 +123,7 @@ impl Interface { } } + /// Generates the vtable for a COM interface fn gen_vtable(&self, vtable_name: &syn::Ident) -> proc_macro2::TokenStream { let vtable_entries = self .methods @@ -206,18 +133,7 @@ impl Interface { if m.args.iter().any(|a| !a.pass_through) { panic!("TODO: handle methods with non-pass through arguments"); } - let args = &m - .args - .iter() - .map(|a| { - let pat = &a.pat; - let typ = &a.ty; - quote! { - #pat: #typ - - } - }) - .collect::>(); + let args = m.gen_args(); quote! { pub #name: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, #(#args),*) -> ::windows::core::HRESULT, } @@ -232,6 +148,7 @@ impl Interface { } } + /// Generates various conversions such as from and to `IUnknown` fn gen_conversions(&self) -> proc_macro2::TokenStream { let name = &self.name; let name_string = format!("{}", name); @@ -311,6 +228,93 @@ impl Parse for Interface { } } +/// Parsed interface guid attribute +/// +/// ```rust +/// #[interface("0000010c-0000-0000-C000-000000000046")] +/// //^ parses this +/// unsafe trait IFoo {} +/// ``` +struct Guid(syn::LitStr); + +impl Guid { + /// The various chunks of a COM interface GUID separated by "-" + fn chunks(&self) -> syn::Result<[String; 5]> { + fn ensure_length<'a>(part: Option<&'a str>, index: usize, length: usize, span: proc_macro2::Span) -> syn::Result { + let part = match part { + Some(p) => p, + None => return Err(syn::Error::new(span, format!("The IID missing part at index {}", index,))), + }; + + if part.len() != length { + return Err(syn::Error::new(span, format!("The IID part at index {} must be {} characters long but was {} characters", index, length, part.len()))); + } + + Ok(part.to_owned()) + } + + let guid_value = self.0.value(); + let mut delimited = guid_value.split('-').fuse(); + let chunks = [ensure_length(delimited.next(), 0, 8, self.0.span())?, ensure_length(delimited.next(), 1, 4, self.0.span())?, ensure_length(delimited.next(), 2, 4, self.0.span())?, ensure_length(delimited.next(), 3, 4, self.0.span())?, ensure_length(delimited.next(), 4, 12, self.0.span())?]; + + Ok(chunks) + } + + fn to_tokens(&self) -> syn::Result { + fn hex_lit(num: &str) -> syn::LitInt { + syn::LitInt::new(&format!("0x{}", num), proc_macro2::Span::call_site()) + } + + let chunks = self.chunks()?; + let data1 = hex_lit(&chunks[0]); + let data2 = hex_lit(&chunks[1]); + let data3 = hex_lit(&chunks[2]); + let (data4_1, data4_2) = chunks[3].split_at(2); + let data4_1 = hex_lit(data4_1); + let data4_2 = hex_lit(data4_2); + let (data4_3, rest) = chunks[4].split_at(2); + let data4_3 = hex_lit(data4_3); + + let (data4_4, rest) = rest.split_at(2); + let data4_4 = hex_lit(data4_4); + + let (data4_5, rest) = rest.split_at(2); + let data4_5 = hex_lit(data4_5); + + let (data4_6, rest) = rest.split_at(2); + let data4_6 = hex_lit(data4_6); + + let (data4_7, data4_8) = rest.split_at(2); + let data4_7 = hex_lit(data4_7); + let data4_8 = hex_lit(data4_8); + Ok(quote! { + ::windows::core::GUID { + data1: #data1, + data2: #data2, + data3: #data3, + data4: [#data4_1, #data4_2, #data4_3, #data4_4, #data4_5, #data4_6, #data4_7, #data4_8] + } + }) + } +} + +impl Parse for Guid { + fn parse(cursor: ParseStream) -> syn::Result { + let string: syn::LitStr = cursor.parse()?; + + Ok(Self(string)) + } +} + +/// A parsed interface method +/// +/// ```rust +/// #[interface("0000010c-0000-0000-C000-000000000046")] +/// unsafe trait IFoo { +/// fn GetValue(&self, value: *mut f64) -> HRESULT; +/// //^ parses this +/// } +/// ``` struct InterfaceMethod { pub name: syn::Ident, pub visibility: syn::Visibility, @@ -319,6 +323,20 @@ struct InterfaceMethod { pub docs: Vec, } +impl InterfaceMethod { + /// Generates arguments (of the form `$pat: $type`) + fn gen_args(&self) -> Vec { + self.args + .iter() + .map(|a| { + let pat = &a.pat; + let ty = &a.ty; + quote! { #pat: #ty } + }) + .collect::>() + } +} + impl syn::parse::Parse for InterfaceMethod { fn parse(input: syn::parse::ParseStream) -> syn::Result { let docs = input.call(syn::Attribute::parse_outer)?; @@ -352,8 +370,12 @@ impl syn::parse::Parse for InterfaceMethod { } } +/// An argument to an interface method struct InterfaceMethodArg { + /// The type of the argument pub ty: Box, + /// The name of the argument pub pat: Box, + /// Whether the argument needs transformation before crossing an FFI boundary pub pass_through: bool, } diff --git a/crates/libs/tokens/src/token_stream.rs b/crates/libs/tokens/src/token_stream.rs index dd2debc8d1..f2c631f0dd 100644 --- a/crates/libs/tokens/src/token_stream.rs +++ b/crates/libs/tokens/src/token_stream.rs @@ -142,7 +142,6 @@ macro_rules! unsuffixed { } impl Literal { - unsuffixed!(u128 => u128_unsuffixed); unsuffixed!(usize => usize_unsuffixed); unsuffixed!(u32 => u32_unsuffixed); unsuffixed!(u16 => u16_unsuffixed); diff --git a/crates/tests/interface/tests/com.rs b/crates/tests/interface/tests/com.rs index 0139ab01f7..fc7151cc9b 100644 --- a/crates/tests/interface/tests/com.rs +++ b/crates/tests/interface/tests/com.rs @@ -6,6 +6,6 @@ unsafe trait IUIAnimationVariable: IUnknown { } #[test] -fn mix() -> Result<()> { +fn com_works() -> Result<()> { Ok(()) } From 215537fe7730d21e27612052cc0647a74538a0af Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 16 Feb 2022 09:05:11 +0100 Subject: [PATCH 05/13] Fix issues in CI --- .github/workflows/build.yml | 2 ++ .github/workflows/test.yml | 2 ++ crates/libs/interface/Cargo.toml | 2 +- crates/tests/interface/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6ff1e27d89..786a39e462 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,6 +72,7 @@ jobs: run: | cargo clippy -p windows-bindgen && cargo clippy -p windows-implement && + cargo clippy -p windows-interface && cargo clippy -p windows-metadata && cargo clippy -p windows-sys && cargo clippy -p windows-tokens && @@ -125,6 +126,7 @@ jobs: cargo clippy -p test_implement_properties && cargo clippy -p test_implement_vector && cargo clippy -p test_implement_winrt && + cargo clippy -p test_interface && cargo clippy -p test_interop && cargo clippy -p test_lib && cargo clippy -p test_matrix3x2 && diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 57adb3a3d5..c8a916b0e5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -60,6 +60,7 @@ jobs: - name: Test stable run: | cargo test --target ${{ matrix.target }} -p windows-bindgen && + cargo test --target ${{ matrix.target }} -p windows-interface && cargo test --target ${{ matrix.target }} -p windows-metadata && cargo test --target ${{ matrix.target }} -p windows-sys && cargo test --target ${{ matrix.target }} -p windows-tokens && @@ -83,6 +84,7 @@ jobs: cargo test --target ${{ matrix.target }} -p test_enums && cargo test --target ${{ matrix.target }} -p test_handles && cargo test --target ${{ matrix.target }} -p test_helpers && + cargo test --target ${{ matrix.target }} -p test_interface && cargo test --target ${{ matrix.target }} -p test_interop && cargo test --target ${{ matrix.target }} -p test_lib && cargo test --target ${{ matrix.target }} -p test_matrix3x2 && diff --git a/crates/libs/interface/Cargo.toml b/crates/libs/interface/Cargo.toml index 33635d51c7..9c9b50a53b 100644 --- a/crates/libs/interface/Cargo.toml +++ b/crates/libs/interface/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "windows-interface" version = "0.32.0" -edition = "2021" +edition = "2018" authors = ["Microsoft"] license = "MIT OR Apache-2.0" description = "The interface macro for the windows crate" diff --git a/crates/tests/interface/Cargo.toml b/crates/tests/interface/Cargo.toml index 74241912a3..0fa79d7128 100644 --- a/crates/tests/interface/Cargo.toml +++ b/crates/tests/interface/Cargo.toml @@ -2,7 +2,7 @@ name = "test_interface" version = "0.0.0" authors = ["Microsoft"] -edition = "2021" +edition = "2018" [dependencies.windows] path = "../../libs/windows" From bb57a8ca4ed656dfbd33be332c915a216edd2d1c Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 16 Feb 2022 11:28:44 +0000 Subject: [PATCH 06/13] More complex example --- crates/libs/interface/src/lib.rs | 42 ++++++++++- crates/tests/interface/Cargo.toml | 4 ++ crates/tests/interface/tests/com.rs | 106 ++++++++++++++++++++++++++-- 3 files changed, 144 insertions(+), 8 deletions(-) diff --git a/crates/libs/interface/src/lib.rs b/crates/libs/interface/src/lib.rs index 87f235d9a8..a57c71504d 100644 --- a/crates/libs/interface/src/lib.rs +++ b/crates/libs/interface/src/lib.rs @@ -69,8 +69,10 @@ impl Interface { let vtable_name = quote::format_ident!("{}_Vtbl", name); let guid = guid.to_tokens()?; let implementation = self.gen_implementation(); + let com_trait = self.get_com_trait(); let vtable = self.gen_vtable(&vtable_name); let conversions = self.gen_conversions(); + Ok(quote! { #[repr(transparent)] #vis struct #name(#parent); @@ -81,8 +83,8 @@ impl Interface { const IID: ::windows::core::GUID = #guid; } + #com_trait #vtable - #conversions }) } @@ -123,8 +125,33 @@ impl Interface { } } + fn get_com_trait(&self) -> proc_macro2::TokenStream { + let name = quote::format_ident!("{}_Impl", self.name); + let vis = &self.visibility; + let methods = self + .methods + .iter() + .map(|m| { + let name = &m.name; + if m.args.iter().any(|a| !a.pass_through) { + panic!("TODO: handle methods with non-pass through arguments"); + } + let args = m.gen_args(); + quote! { + unsafe fn #name(&self, #(#args)*) -> ::windows::core::HRESULT; + } + }) + .collect::>(); + quote! { + #vis trait #name: Sized { + #(#methods)* + } + } + } + /// Generates the vtable for a COM interface fn gen_vtable(&self, vtable_name: &syn::Ident) -> proc_macro2::TokenStream { + let name = &self.name; let vtable_entries = self .methods .iter() @@ -139,12 +166,25 @@ impl Interface { } }) .collect::>(); + let trait_name = quote::format_ident!("{}_Impl", name); quote! { #[repr(C)] #[doc(hidden)] pub struct #vtable_name { + // TODO: handle non-IUnknown parents + pub base: ::windows::core::IUnknownVtbl, #(#vtable_entries)* } + + impl #vtable_name { + pub const fn new() -> Self { + loop {} + } + + pub fn matches(iid: &windows::core::GUID) -> bool { + iid == &<#name as ::windows::core::Interface>::IID + } + } } } diff --git a/crates/tests/interface/Cargo.toml b/crates/tests/interface/Cargo.toml index 0fa79d7128..3ef8f354d1 100644 --- a/crates/tests/interface/Cargo.toml +++ b/crates/tests/interface/Cargo.toml @@ -7,5 +7,9 @@ edition = "2018" [dependencies.windows] path = "../../libs/windows" features = [ + "Win32_Foundation", + "Win32_System_Com", "interface", + "implement", + "alloc" ] diff --git a/crates/tests/interface/tests/com.rs b/crates/tests/interface/tests/com.rs index fc7151cc9b..102cd15b2b 100644 --- a/crates/tests/interface/tests/com.rs +++ b/crates/tests/interface/tests/com.rs @@ -1,11 +1,103 @@ -use windows::core::*; +#![allow(non_snake_case)] +#![feature(const_fn_trait_bound)] -#[interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] -unsafe trait IUIAnimationVariable: IUnknown { - fn GetValue(&self, value: *mut f64) -> HRESULT; +use windows::{ + core::*, + Win32::Foundation::*, + Win32::System::Com::*, +}; + +#[interface("a39ee748-6a27-4817-a6f2-13914bef5890")] +pub unsafe trait ICustomUri : IUnknown { + unsafe fn GetPropertyBSTR(&self) -> HRESULT; + unsafe fn GetPropertyLength(&self) -> HRESULT; + unsafe fn GetPropertyDWORD(&self) -> HRESULT; + unsafe fn HasProperty(&self) -> HRESULT; + unsafe fn GetAbsoluteUri(&self) -> HRESULT; + unsafe fn GetAuthority(&self) -> HRESULT; + unsafe fn GetDisplayUri(&self) -> HRESULT; + unsafe fn GetDomain(&self, value: *mut BSTR) -> HRESULT; + // etc } -#[test] -fn com_works() -> Result<()> { - Ok(()) +#[implement(ICustomUri, IUri)] +struct CustomUri; + +impl ICustomUri_Impl for CustomUri { + unsafe fn GetPropertyBSTR(&self) -> HRESULT{ todo!() } + unsafe fn GetPropertyLength(&self) -> HRESULT{ todo!() } + unsafe fn GetPropertyDWORD(&self) -> HRESULT{ todo!() } + unsafe fn HasProperty(&self) -> HRESULT{ todo!() } + unsafe fn GetAbsoluteUri(&self) -> HRESULT{ todo!() } + unsafe fn GetAuthority(&self) -> HRESULT{ todo!() } + unsafe fn GetDisplayUri(&self) -> HRESULT{ todo!() } + unsafe fn GetDomain(&self, value: *mut BSTR) -> HRESULT { + *value = "kennykerr.ca".into(); + S_OK + } } + +impl IUri_Impl for CustomUri { + fn GetPropertyBSTR(&self, uriprop: Uri_PROPERTY, pbstrproperty: *mut BSTR, dwflags: u32) -> ::windows::core::Result<()> { + todo!() + } + fn GetPropertyLength(&self, uriprop: Uri_PROPERTY, pcchproperty: *mut u32, dwflags: u32) -> ::windows::core::Result<()> { + todo!() + } + fn GetPropertyDWORD(&self, uriprop: Uri_PROPERTY, pdwproperty: *mut u32, dwflags: u32) -> ::windows::core::Result<()> { + todo!() + } + fn HasProperty(&self, uriprop: Uri_PROPERTY) -> ::windows::core::Result { todo!() } + fn GetAbsoluteUri(&self) -> ::windows::core::Result { todo!() } + fn GetAuthority(&self) -> ::windows::core::Result { todo!() } + fn GetDisplayUri(&self) -> ::windows::core::Result { todo!() } + fn GetDomain(&self) -> ::windows::core::Result { + Ok("kennykerr.ca".into()) + } + fn GetExtension(&self) -> ::windows::core::Result { todo!() } + fn GetFragment(&self) -> ::windows::core::Result { todo!() } + fn GetHost(&self) -> ::windows::core::Result { todo!() } + fn GetPassword(&self) -> ::windows::core::Result { todo!() } + fn GetPath(&self) -> ::windows::core::Result { todo!() } + fn GetPathAndQuery(&self) -> ::windows::core::Result { todo!() } + fn GetQuery(&self) -> ::windows::core::Result { todo!() } + fn GetRawUri(&self) -> ::windows::core::Result { todo!() } + fn GetSchemeName(&self) -> ::windows::core::Result { todo!() } + fn GetUserInfo(&self) -> ::windows::core::Result { todo!() } + fn GetUserName(&self) -> ::windows::core::Result { todo!() } + fn GetHostType(&self) -> ::windows::core::Result { todo!() } + fn GetPort(&self) -> ::windows::core::Result { todo!() } + fn GetScheme(&self) -> ::windows::core::Result { todo!() } + fn GetZone(&self) -> ::windows::core::Result { todo!() } + fn GetProperties(&self) -> ::windows::core::Result { todo!() } + fn IsEqual(&self, puri: &::core::option::Option) -> ::windows::core::Result { todo!() } +} + +#[test] +fn test_custom_interface() -> windows::core::Result<()> { + unsafe { + // Use the OS implementation through the OS interface + let a: IUri = CreateUri("http://kennykerr.ca", Default::default(), 0)?; + let domain = a.GetDomain()?; + assert_eq!(domain, "kennykerr.ca"); + + // Call the OS implementation through the custom interface + let b: ICustomUri = a.cast()?; + let mut domain = BSTR::new(); + b.GetDomain(&mut domain).ok()?; + assert_eq!(domain, "kennykerr.ca"); + + // Use the custom implementation through the OS interface + let c: IUri = CustomUri.into(); + let domain = c.GetDomain()?; + assert_eq!(domain, "kennykerr.ca"); + + // Call the custom implementation through the custom interface + let d: ICustomUri = c.cast()?; + let mut domain = BSTR::new(); + d.GetDomain(&mut domain).ok()?; + assert_eq!(domain, "kennykerr.ca"); + + Ok(()) + } +} \ No newline at end of file From 22e20601eee0e004fa89b0414a1a025061275139 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 16 Feb 2022 14:24:41 +0000 Subject: [PATCH 07/13] Finish getting example to compile --- crates/libs/interface/src/lib.rs | 41 +++++++++- crates/tests/interface/tests/com.rs | 123 ++++++++++++++++++++-------- 2 files changed, 127 insertions(+), 37 deletions(-) diff --git a/crates/libs/interface/src/lib.rs b/crates/libs/interface/src/lib.rs index a57c71504d..3f58019980 100644 --- a/crates/libs/interface/src/lib.rs +++ b/crates/libs/interface/src/lib.rs @@ -152,6 +152,8 @@ impl Interface { /// Generates the vtable for a COM interface fn gen_vtable(&self, vtable_name: &syn::Ident) -> proc_macro2::TokenStream { let name = &self.name; + // TODO + let parent_vtable = quote!(::windows::core::IUnknownVtbl); let vtable_entries = self .methods .iter() @@ -167,6 +169,40 @@ impl Interface { }) .collect::>(); let trait_name = quote::format_ident!("{}_Impl", name); + let functions = self + .methods + .iter() + .map(|m| { + let name = &m.name; + let args = m.gen_args(); + let params = &m + .args + .iter() + .map(|a| { + let pat = &a.pat; + quote! { #pat } + }) + .collect::>(); + quote! { + + unsafe extern "system" fn #name(this: *mut ::core::ffi::c_void, #(#args),*) -> ::windows::core::HRESULT { + let this = (this as *mut ::windows::core::RawPtr).offset(OFFSET) as *mut Identity; + let this = (*this).get_impl() as *mut Impl; + (*this).#name(#(#params),*).into() + } + } + }) + .collect::>(); + let entries = self + .methods + .iter() + .map(|m| { + let name = &m.name; + quote! { + #name: #name:: + } + }) + .collect::>(); quote! { #[repr(C)] #[doc(hidden)] @@ -178,9 +214,10 @@ impl Interface { impl #vtable_name { pub const fn new() -> Self { - loop {} + #(#functions)* + Self { base: #parent_vtable::new::(), #(#entries),* } } - + pub fn matches(iid: &windows::core::GUID) -> bool { iid == &<#name as ::windows::core::Interface>::IID } diff --git a/crates/tests/interface/tests/com.rs b/crates/tests/interface/tests/com.rs index 102cd15b2b..22d0a1acb7 100644 --- a/crates/tests/interface/tests/com.rs +++ b/crates/tests/interface/tests/com.rs @@ -1,14 +1,11 @@ #![allow(non_snake_case)] #![feature(const_fn_trait_bound)] +#![feature(const_fn_fn_ptr_basics)] -use windows::{ - core::*, - Win32::Foundation::*, - Win32::System::Com::*, -}; +use windows::{core::*, Win32::Foundation::*, Win32::System::Com::*}; #[interface("a39ee748-6a27-4817-a6f2-13914bef5890")] -pub unsafe trait ICustomUri : IUnknown { +pub unsafe trait ICustomUri: IUnknown { unsafe fn GetPropertyBSTR(&self) -> HRESULT; unsafe fn GetPropertyLength(&self) -> HRESULT; unsafe fn GetPropertyDWORD(&self) -> HRESULT; @@ -24,13 +21,27 @@ pub unsafe trait ICustomUri : IUnknown { struct CustomUri; impl ICustomUri_Impl for CustomUri { - unsafe fn GetPropertyBSTR(&self) -> HRESULT{ todo!() } - unsafe fn GetPropertyLength(&self) -> HRESULT{ todo!() } - unsafe fn GetPropertyDWORD(&self) -> HRESULT{ todo!() } - unsafe fn HasProperty(&self) -> HRESULT{ todo!() } - unsafe fn GetAbsoluteUri(&self) -> HRESULT{ todo!() } - unsafe fn GetAuthority(&self) -> HRESULT{ todo!() } - unsafe fn GetDisplayUri(&self) -> HRESULT{ todo!() } + unsafe fn GetPropertyBSTR(&self) -> HRESULT { + todo!() + } + unsafe fn GetPropertyLength(&self) -> HRESULT { + todo!() + } + unsafe fn GetPropertyDWORD(&self) -> HRESULT { + todo!() + } + unsafe fn HasProperty(&self) -> HRESULT { + todo!() + } + unsafe fn GetAbsoluteUri(&self) -> HRESULT { + todo!() + } + unsafe fn GetAuthority(&self) -> HRESULT { + todo!() + } + unsafe fn GetDisplayUri(&self) -> HRESULT { + todo!() + } unsafe fn GetDomain(&self, value: *mut BSTR) -> HRESULT { *value = "kennykerr.ca".into(); S_OK @@ -47,30 +58,72 @@ impl IUri_Impl for CustomUri { fn GetPropertyDWORD(&self, uriprop: Uri_PROPERTY, pdwproperty: *mut u32, dwflags: u32) -> ::windows::core::Result<()> { todo!() } - fn HasProperty(&self, uriprop: Uri_PROPERTY) -> ::windows::core::Result { todo!() } - fn GetAbsoluteUri(&self) -> ::windows::core::Result { todo!() } - fn GetAuthority(&self) -> ::windows::core::Result { todo!() } - fn GetDisplayUri(&self) -> ::windows::core::Result { todo!() } + fn HasProperty(&self, uriprop: Uri_PROPERTY) -> ::windows::core::Result { + todo!() + } + fn GetAbsoluteUri(&self) -> ::windows::core::Result { + todo!() + } + fn GetAuthority(&self) -> ::windows::core::Result { + todo!() + } + fn GetDisplayUri(&self) -> ::windows::core::Result { + todo!() + } fn GetDomain(&self) -> ::windows::core::Result { Ok("kennykerr.ca".into()) } - fn GetExtension(&self) -> ::windows::core::Result { todo!() } - fn GetFragment(&self) -> ::windows::core::Result { todo!() } - fn GetHost(&self) -> ::windows::core::Result { todo!() } - fn GetPassword(&self) -> ::windows::core::Result { todo!() } - fn GetPath(&self) -> ::windows::core::Result { todo!() } - fn GetPathAndQuery(&self) -> ::windows::core::Result { todo!() } - fn GetQuery(&self) -> ::windows::core::Result { todo!() } - fn GetRawUri(&self) -> ::windows::core::Result { todo!() } - fn GetSchemeName(&self) -> ::windows::core::Result { todo!() } - fn GetUserInfo(&self) -> ::windows::core::Result { todo!() } - fn GetUserName(&self) -> ::windows::core::Result { todo!() } - fn GetHostType(&self) -> ::windows::core::Result { todo!() } - fn GetPort(&self) -> ::windows::core::Result { todo!() } - fn GetScheme(&self) -> ::windows::core::Result { todo!() } - fn GetZone(&self) -> ::windows::core::Result { todo!() } - fn GetProperties(&self) -> ::windows::core::Result { todo!() } - fn IsEqual(&self, puri: &::core::option::Option) -> ::windows::core::Result { todo!() } + fn GetExtension(&self) -> ::windows::core::Result { + todo!() + } + fn GetFragment(&self) -> ::windows::core::Result { + todo!() + } + fn GetHost(&self) -> ::windows::core::Result { + todo!() + } + fn GetPassword(&self) -> ::windows::core::Result { + todo!() + } + fn GetPath(&self) -> ::windows::core::Result { + todo!() + } + fn GetPathAndQuery(&self) -> ::windows::core::Result { + todo!() + } + fn GetQuery(&self) -> ::windows::core::Result { + todo!() + } + fn GetRawUri(&self) -> ::windows::core::Result { + todo!() + } + fn GetSchemeName(&self) -> ::windows::core::Result { + todo!() + } + fn GetUserInfo(&self) -> ::windows::core::Result { + todo!() + } + fn GetUserName(&self) -> ::windows::core::Result { + todo!() + } + fn GetHostType(&self) -> ::windows::core::Result { + todo!() + } + fn GetPort(&self) -> ::windows::core::Result { + todo!() + } + fn GetScheme(&self) -> ::windows::core::Result { + todo!() + } + fn GetZone(&self) -> ::windows::core::Result { + todo!() + } + fn GetProperties(&self) -> ::windows::core::Result { + todo!() + } + fn IsEqual(&self, puri: &::core::option::Option) -> ::windows::core::Result { + todo!() + } } #[test] @@ -100,4 +153,4 @@ fn test_custom_interface() -> windows::core::Result<()> { Ok(()) } -} \ No newline at end of file +} From 9db25430394857d6910df444b22c812acf2747d2 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 16 Feb 2022 14:55:41 +0000 Subject: [PATCH 08/13] Clean up warnings --- crates/libs/interface/src/lib.rs | 23 +++++++++++++---------- crates/tests/interface/tests/com.rs | 10 +++++----- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/crates/libs/interface/src/lib.rs b/crates/libs/interface/src/lib.rs index 3f58019980..dd1e010185 100644 --- a/crates/libs/interface/src/lib.rs +++ b/crates/libs/interface/src/lib.rs @@ -64,8 +64,8 @@ impl Interface { fn gen_tokens(&self, guid: &Guid) -> syn::Result { let vis = &self.visibility; let name = &self.name; - // TODO: support non-IUnknown parents - let parent = quote!(::windows::core::IUnknown); + let docs = &self.docs; + let parent = self.parent.as_ref().map(|p| quote!(#p)).unwrap_or_else(|| quote!(::windows::core::IUnknown)); let vtable_name = quote::format_ident!("{}_Vtbl", name); let guid = guid.to_tokens()?; let implementation = self.gen_implementation(); @@ -75,6 +75,7 @@ impl Interface { Ok(quote! { #[repr(transparent)] + #(#docs)* #vis struct #name(#parent); #implementation @@ -133,16 +134,19 @@ impl Interface { .iter() .map(|m| { let name = &m.name; + let docs = &m.docs; if m.args.iter().any(|a| !a.pass_through) { panic!("TODO: handle methods with non-pass through arguments"); } let args = m.gen_args(); quote! { + #(#docs)* unsafe fn #name(&self, #(#args)*) -> ::windows::core::HRESULT; } }) .collect::>(); quote! { + #[allow(non_camel_case_types)] #vis trait #name: Sized { #(#methods)* } @@ -159,12 +163,13 @@ impl Interface { .iter() .map(|m| { let name = &m.name; + let ret = &m.ret; if m.args.iter().any(|a| !a.pass_through) { panic!("TODO: handle methods with non-pass through arguments"); } let args = m.gen_args(); quote! { - pub #name: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, #(#args),*) -> ::windows::core::HRESULT, + pub #name: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, #(#args),*) #ret, } }) .collect::>(); @@ -184,12 +189,11 @@ impl Interface { }) .collect::>(); quote! { - unsafe extern "system" fn #name(this: *mut ::core::ffi::c_void, #(#args),*) -> ::windows::core::HRESULT { - let this = (this as *mut ::windows::core::RawPtr).offset(OFFSET) as *mut Identity; - let this = (*this).get_impl() as *mut Impl; - (*this).#name(#(#params),*).into() - } + let this = (this as *mut ::windows::core::RawPtr).offset(OFFSET) as *mut Identity; + let this = (*this).get_impl() as *mut Impl; + (*this).#name(#(#params),*).into() + } } }) .collect::>(); @@ -278,7 +282,6 @@ impl Parse for Interface { let mut docs = Vec::new(); for attr in attributes.into_iter() { let path = &attr.path; - let tokens = &attr.tokens; if path.is_ident("doc") { docs.push(attr); } else { @@ -288,7 +291,7 @@ impl Parse for Interface { let visibility = input.parse::()?; let _ = input.parse::()?; - let interface = input.parse::()?; + let _ = input.parse::()?; let name = input.parse::()?; let mut parent = None; if name != "IUnknown" { diff --git a/crates/tests/interface/tests/com.rs b/crates/tests/interface/tests/com.rs index 22d0a1acb7..3a1a1d3341 100644 --- a/crates/tests/interface/tests/com.rs +++ b/crates/tests/interface/tests/com.rs @@ -49,16 +49,16 @@ impl ICustomUri_Impl for CustomUri { } impl IUri_Impl for CustomUri { - fn GetPropertyBSTR(&self, uriprop: Uri_PROPERTY, pbstrproperty: *mut BSTR, dwflags: u32) -> ::windows::core::Result<()> { + fn GetPropertyBSTR(&self, _uriprop: Uri_PROPERTY, _pbstrproperty: *mut BSTR, _dwflags: u32) -> ::windows::core::Result<()> { todo!() } - fn GetPropertyLength(&self, uriprop: Uri_PROPERTY, pcchproperty: *mut u32, dwflags: u32) -> ::windows::core::Result<()> { + fn GetPropertyLength(&self, _uriprop: Uri_PROPERTY, _pcchproperty: *mut u32, _dwflags: u32) -> ::windows::core::Result<()> { todo!() } - fn GetPropertyDWORD(&self, uriprop: Uri_PROPERTY, pdwproperty: *mut u32, dwflags: u32) -> ::windows::core::Result<()> { + fn GetPropertyDWORD(&self, _uriprop: Uri_PROPERTY, _pdwproperty: *mut u32, _dwflags: u32) -> ::windows::core::Result<()> { todo!() } - fn HasProperty(&self, uriprop: Uri_PROPERTY) -> ::windows::core::Result { + fn HasProperty(&self, _uriprop: Uri_PROPERTY) -> ::windows::core::Result { todo!() } fn GetAbsoluteUri(&self) -> ::windows::core::Result { @@ -121,7 +121,7 @@ impl IUri_Impl for CustomUri { fn GetProperties(&self) -> ::windows::core::Result { todo!() } - fn IsEqual(&self, puri: &::core::option::Option) -> ::windows::core::Result { + fn IsEqual(&self, _puri: &::core::option::Option) -> ::windows::core::Result { todo!() } } From a3df0dff49bc0e2d1e7c47e8ec49974ff96b597e Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 16 Feb 2022 15:48:12 +0000 Subject: [PATCH 09/13] Fix doc tests --- crates/libs/interface/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/libs/interface/src/lib.rs b/crates/libs/interface/src/lib.rs index dd1e010185..f2401d7029 100644 --- a/crates/libs/interface/src/lib.rs +++ b/crates/libs/interface/src/lib.rs @@ -6,7 +6,7 @@ use syn::spanned::Spanned; /// /// # Example /// ```rust -/// #[interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] +/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] /// unsafe trait IUIAnimationVariable: IUnknown { /// fn GetValue(&self, value: *mut f64) -> HRESULT; /// } @@ -47,7 +47,7 @@ macro_rules! expected_token { /// Parsed interface /// /// ```rust -/// #[interface("0000010c-0000-0000-C000-000000000046")] +/// #[windows_interface::interface("0000010c-0000-0000-C000-000000000046")] /// unsafe trait IFoo {} /// //^ parses this /// ``` @@ -311,7 +311,7 @@ impl Parse for Interface { /// Parsed interface guid attribute /// /// ```rust -/// #[interface("0000010c-0000-0000-C000-000000000046")] +/// #[windows_interface::interface("0000010c-0000-0000-C000-000000000046")] /// //^ parses this /// unsafe trait IFoo {} /// ``` @@ -389,7 +389,7 @@ impl Parse for Guid { /// A parsed interface method /// /// ```rust -/// #[interface("0000010c-0000-0000-C000-000000000046")] +/// #[windows_interface::interface("0000010c-0000-0000-C000-000000000046")] /// unsafe trait IFoo { /// fn GetValue(&self, value: *mut f64) -> HRESULT; /// //^ parses this From c39bc4f474a25f938bd8105de6c210bc0e7c46e8 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 16 Feb 2022 15:53:01 +0000 Subject: [PATCH 10/13] Fix clippy --- crates/libs/interface/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/libs/interface/src/lib.rs b/crates/libs/interface/src/lib.rs index f2401d7029..6c57328ee9 100644 --- a/crates/libs/interface/src/lib.rs +++ b/crates/libs/interface/src/lib.rs @@ -320,7 +320,7 @@ struct Guid(syn::LitStr); impl Guid { /// The various chunks of a COM interface GUID separated by "-" fn chunks(&self) -> syn::Result<[String; 5]> { - fn ensure_length<'a>(part: Option<&'a str>, index: usize, length: usize, span: proc_macro2::Span) -> syn::Result { + fn ensure_length(part: Option<&str>, index: usize, length: usize, span: proc_macro2::Span) -> syn::Result { let part = match part { Some(p) => p, None => return Err(syn::Error::new(span, format!("The IID missing part at index {}", index,))), From cf05fe269b58ade2c96ab42ca4967694eb5fa235 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 16 Feb 2022 16:05:34 +0000 Subject: [PATCH 11/13] More test fixes --- crates/libs/interface/src/lib.rs | 28 ++-- .../Cargo.toml | 2 +- .../src/lib.rs | 0 crates/tests/implement_interface/tests/com.rs | 81 +++++++++ crates/tests/interface/tests/com.rs | 156 ------------------ 5 files changed, 98 insertions(+), 169 deletions(-) rename crates/tests/{interface => implement_interface}/Cargo.toml (87%) rename crates/tests/{interface => implement_interface}/src/lib.rs (100%) create mode 100644 crates/tests/implement_interface/tests/com.rs delete mode 100644 crates/tests/interface/tests/com.rs diff --git a/crates/libs/interface/src/lib.rs b/crates/libs/interface/src/lib.rs index 6c57328ee9..9c3c6943a0 100644 --- a/crates/libs/interface/src/lib.rs +++ b/crates/libs/interface/src/lib.rs @@ -5,7 +5,7 @@ use syn::spanned::Spanned; /// A COM interface definition /// /// # Example -/// ```rust +/// ```rust,ignore /// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] /// unsafe trait IUIAnimationVariable: IUnknown { /// fn GetValue(&self, value: *mut f64) -> HRESULT; @@ -46,10 +46,12 @@ macro_rules! expected_token { /// Parsed interface /// -/// ```rust -/// #[windows_interface::interface("0000010c-0000-0000-C000-000000000046")] -/// unsafe trait IFoo {} +/// ```rust,ignore +/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] +/// unsafe trait IUIAnimationVariable: IUnknown { /// //^ parses this +/// fn GetValue(&self, value: *mut f64) -> HRESULT; +/// } /// ``` struct Interface { pub visibility: syn::Visibility, @@ -310,10 +312,12 @@ impl Parse for Interface { /// Parsed interface guid attribute /// -/// ```rust -/// #[windows_interface::interface("0000010c-0000-0000-C000-000000000046")] -/// //^ parses this -/// unsafe trait IFoo {} +/// ```rust,ignore +/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] +/// //^ parses this +/// unsafe trait IUIAnimationVariable: IUnknown { +/// fn GetValue(&self, value: *mut f64) -> HRESULT; +/// } /// ``` struct Guid(syn::LitStr); @@ -388,11 +392,11 @@ impl Parse for Guid { /// A parsed interface method /// -/// ```rust -/// #[windows_interface::interface("0000010c-0000-0000-C000-000000000046")] -/// unsafe trait IFoo { +/// ```rust,ignore +/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] +/// unsafe trait IUIAnimationVariable: IUnknown { /// fn GetValue(&self, value: *mut f64) -> HRESULT; -/// //^ parses this +/// //^ parses this /// } /// ``` struct InterfaceMethod { diff --git a/crates/tests/interface/Cargo.toml b/crates/tests/implement_interface/Cargo.toml similarity index 87% rename from crates/tests/interface/Cargo.toml rename to crates/tests/implement_interface/Cargo.toml index 3ef8f354d1..d14214751c 100644 --- a/crates/tests/interface/Cargo.toml +++ b/crates/tests/implement_interface/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "test_interface" +name = "test_implement_interface" version = "0.0.0" authors = ["Microsoft"] edition = "2018" diff --git a/crates/tests/interface/src/lib.rs b/crates/tests/implement_interface/src/lib.rs similarity index 100% rename from crates/tests/interface/src/lib.rs rename to crates/tests/implement_interface/src/lib.rs diff --git a/crates/tests/implement_interface/tests/com.rs b/crates/tests/implement_interface/tests/com.rs new file mode 100644 index 0000000000..c85ea22253 --- /dev/null +++ b/crates/tests/implement_interface/tests/com.rs @@ -0,0 +1,81 @@ +#![allow(non_snake_case)] +#![feature(const_fn_trait_bound)] +#![feature(const_fn_fn_ptr_basics)] + +use windows::{core::*, Win32::Foundation::*, Win32::System::Com::*}; + +/// A custom declaration of implementation of `IUri` +#[interface("a39ee748-6a27-4817-a6f2-13914bef5890")] +pub unsafe trait ICustomUri: IUnknown { + unsafe fn GetPropertyBSTR(&self) -> HRESULT; + unsafe fn GetPropertyLength(&self) -> HRESULT; + unsafe fn GetPropertyDWORD(&self) -> HRESULT; + unsafe fn HasProperty(&self) -> HRESULT; + unsafe fn GetAbsoluteUri(&self) -> HRESULT; + unsafe fn GetAuthority(&self) -> HRESULT; + unsafe fn GetDisplayUri(&self) -> HRESULT; + unsafe fn GetDomain(&self, value: *mut BSTR) -> HRESULT; + // etc +} + +#[implement(ICustomUri)] +struct CustomUri; + +impl ICustomUri_Impl for CustomUri { + unsafe fn GetPropertyBSTR(&self) -> HRESULT { + todo!() + } + unsafe fn GetPropertyLength(&self) -> HRESULT { + todo!() + } + unsafe fn GetPropertyDWORD(&self) -> HRESULT { + todo!() + } + unsafe fn HasProperty(&self) -> HRESULT { + todo!() + } + unsafe fn GetAbsoluteUri(&self) -> HRESULT { + todo!() + } + unsafe fn GetAuthority(&self) -> HRESULT { + todo!() + } + unsafe fn GetDisplayUri(&self) -> HRESULT { + todo!() + } + unsafe fn GetDomain(&self, value: *mut BSTR) -> HRESULT { + *value = "kennykerr.ca".into(); + S_OK + } +} + +#[test] +fn test_custom_interface() -> windows::core::Result<()> { + unsafe { + // Use the OS implementation through the OS interface + let a: IUri = CreateUri("http://kennykerr.ca", Default::default(), 0)?; + let domain = a.GetDomain()?; + assert_eq!(domain, "kennykerr.ca"); + + // Call the OS implementation through the custom interface + let b: ICustomUri = a.cast()?; + let mut domain = BSTR::new(); + b.GetDomain(&mut domain).ok()?; + assert_eq!(domain, "kennykerr.ca"); + + // Use the custom implementation through the OS interface + let c: ICustomUri = CustomUri.into(); + // This works because `ICustomUri` and `IUri` share the same guid + let c: IUri = c.cast()?; + let domain = c.GetDomain()?; + assert_eq!(domain, "kennykerr.ca"); + + // Call the custom implementation through the custom interface + let d: ICustomUri = c.cast()?; + let mut domain = BSTR::new(); + d.GetDomain(&mut domain).ok()?; + assert_eq!(domain, "kennykerr.ca"); + + Ok(()) + } +} diff --git a/crates/tests/interface/tests/com.rs b/crates/tests/interface/tests/com.rs deleted file mode 100644 index 3a1a1d3341..0000000000 --- a/crates/tests/interface/tests/com.rs +++ /dev/null @@ -1,156 +0,0 @@ -#![allow(non_snake_case)] -#![feature(const_fn_trait_bound)] -#![feature(const_fn_fn_ptr_basics)] - -use windows::{core::*, Win32::Foundation::*, Win32::System::Com::*}; - -#[interface("a39ee748-6a27-4817-a6f2-13914bef5890")] -pub unsafe trait ICustomUri: IUnknown { - unsafe fn GetPropertyBSTR(&self) -> HRESULT; - unsafe fn GetPropertyLength(&self) -> HRESULT; - unsafe fn GetPropertyDWORD(&self) -> HRESULT; - unsafe fn HasProperty(&self) -> HRESULT; - unsafe fn GetAbsoluteUri(&self) -> HRESULT; - unsafe fn GetAuthority(&self) -> HRESULT; - unsafe fn GetDisplayUri(&self) -> HRESULT; - unsafe fn GetDomain(&self, value: *mut BSTR) -> HRESULT; - // etc -} - -#[implement(ICustomUri, IUri)] -struct CustomUri; - -impl ICustomUri_Impl for CustomUri { - unsafe fn GetPropertyBSTR(&self) -> HRESULT { - todo!() - } - unsafe fn GetPropertyLength(&self) -> HRESULT { - todo!() - } - unsafe fn GetPropertyDWORD(&self) -> HRESULT { - todo!() - } - unsafe fn HasProperty(&self) -> HRESULT { - todo!() - } - unsafe fn GetAbsoluteUri(&self) -> HRESULT { - todo!() - } - unsafe fn GetAuthority(&self) -> HRESULT { - todo!() - } - unsafe fn GetDisplayUri(&self) -> HRESULT { - todo!() - } - unsafe fn GetDomain(&self, value: *mut BSTR) -> HRESULT { - *value = "kennykerr.ca".into(); - S_OK - } -} - -impl IUri_Impl for CustomUri { - fn GetPropertyBSTR(&self, _uriprop: Uri_PROPERTY, _pbstrproperty: *mut BSTR, _dwflags: u32) -> ::windows::core::Result<()> { - todo!() - } - fn GetPropertyLength(&self, _uriprop: Uri_PROPERTY, _pcchproperty: *mut u32, _dwflags: u32) -> ::windows::core::Result<()> { - todo!() - } - fn GetPropertyDWORD(&self, _uriprop: Uri_PROPERTY, _pdwproperty: *mut u32, _dwflags: u32) -> ::windows::core::Result<()> { - todo!() - } - fn HasProperty(&self, _uriprop: Uri_PROPERTY) -> ::windows::core::Result { - todo!() - } - fn GetAbsoluteUri(&self) -> ::windows::core::Result { - todo!() - } - fn GetAuthority(&self) -> ::windows::core::Result { - todo!() - } - fn GetDisplayUri(&self) -> ::windows::core::Result { - todo!() - } - fn GetDomain(&self) -> ::windows::core::Result { - Ok("kennykerr.ca".into()) - } - fn GetExtension(&self) -> ::windows::core::Result { - todo!() - } - fn GetFragment(&self) -> ::windows::core::Result { - todo!() - } - fn GetHost(&self) -> ::windows::core::Result { - todo!() - } - fn GetPassword(&self) -> ::windows::core::Result { - todo!() - } - fn GetPath(&self) -> ::windows::core::Result { - todo!() - } - fn GetPathAndQuery(&self) -> ::windows::core::Result { - todo!() - } - fn GetQuery(&self) -> ::windows::core::Result { - todo!() - } - fn GetRawUri(&self) -> ::windows::core::Result { - todo!() - } - fn GetSchemeName(&self) -> ::windows::core::Result { - todo!() - } - fn GetUserInfo(&self) -> ::windows::core::Result { - todo!() - } - fn GetUserName(&self) -> ::windows::core::Result { - todo!() - } - fn GetHostType(&self) -> ::windows::core::Result { - todo!() - } - fn GetPort(&self) -> ::windows::core::Result { - todo!() - } - fn GetScheme(&self) -> ::windows::core::Result { - todo!() - } - fn GetZone(&self) -> ::windows::core::Result { - todo!() - } - fn GetProperties(&self) -> ::windows::core::Result { - todo!() - } - fn IsEqual(&self, _puri: &::core::option::Option) -> ::windows::core::Result { - todo!() - } -} - -#[test] -fn test_custom_interface() -> windows::core::Result<()> { - unsafe { - // Use the OS implementation through the OS interface - let a: IUri = CreateUri("http://kennykerr.ca", Default::default(), 0)?; - let domain = a.GetDomain()?; - assert_eq!(domain, "kennykerr.ca"); - - // Call the OS implementation through the custom interface - let b: ICustomUri = a.cast()?; - let mut domain = BSTR::new(); - b.GetDomain(&mut domain).ok()?; - assert_eq!(domain, "kennykerr.ca"); - - // Use the custom implementation through the OS interface - let c: IUri = CustomUri.into(); - let domain = c.GetDomain()?; - assert_eq!(domain, "kennykerr.ca"); - - // Call the custom implementation through the custom interface - let d: ICustomUri = c.cast()?; - let mut domain = BSTR::new(); - d.GetDomain(&mut domain).ok()?; - assert_eq!(domain, "kennykerr.ca"); - - Ok(()) - } -} From 1bfe214b8e4b4439bb96f46f2bb16345547a9320 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 16 Feb 2022 16:15:28 +0000 Subject: [PATCH 12/13] Update yaml --- .github/workflows/build.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 786a39e462..4669d38f98 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -120,13 +120,13 @@ jobs: cargo clippy -p test_implement_data_object && cargo clippy -p test_implement_drop_target && cargo clippy -p test_implement_identity && + cargo clippy -p test_implement_interface && cargo clippy -p test_implement_map && cargo clippy -p test_implement_no_use && cargo clippy -p test_implement_null_result && cargo clippy -p test_implement_properties && cargo clippy -p test_implement_vector && cargo clippy -p test_implement_winrt && - cargo clippy -p test_interface && cargo clippy -p test_interop && cargo clippy -p test_lib && cargo clippy -p test_matrix3x2 && diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c8a916b0e5..4df32084ad 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -84,7 +84,6 @@ jobs: cargo test --target ${{ matrix.target }} -p test_enums && cargo test --target ${{ matrix.target }} -p test_handles && cargo test --target ${{ matrix.target }} -p test_helpers && - cargo test --target ${{ matrix.target }} -p test_interface && cargo test --target ${{ matrix.target }} -p test_interop && cargo test --target ${{ matrix.target }} -p test_lib && cargo test --target ${{ matrix.target }} -p test_matrix3x2 && @@ -134,6 +133,7 @@ jobs: cargo test --target ${{ matrix.target }} -p test_implement_data_object && cargo test --target ${{ matrix.target }} -p test_implement_drop_target && cargo test --target ${{ matrix.target }} -p test_implement_identity && + cargo test --target ${{ matrix.target }} -p test_implement_interface && cargo test --target ${{ matrix.target }} -p test_implement_map && cargo test --target ${{ matrix.target }} -p test_implement_no_use && cargo test --target ${{ matrix.target }} -p test_implement_null_result && From 65512d8abaa7b4bb4ee15d165e81fd049cf9da8d Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 16 Feb 2022 17:58:02 +0100 Subject: [PATCH 13/13] Change windows Cargo tool --- crates/tools/windows/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/tools/windows/src/main.rs b/crates/tools/windows/src/main.rs index 81e34b7f71..2c516cb95c 100644 --- a/crates/tools/windows/src/main.rs +++ b/crates/tools/windows/src/main.rs @@ -69,12 +69,14 @@ windows_x86_64_gnu = { path = "../../targets/x86_64_gnu", version = "0.32.0" } [dependencies] windows-implement = { path = "../implement", version = "0.32.0", optional = true } +windows-interface = { path = "../interface", version = "0.32.0", optional = true } [features] default = [] deprecated = [] alloc = [] implement = ["windows-implement"] +interface = ["windows-interface"] "# .as_bytes(), )