From f493b696cea52bf5e2605b641f9fb5fc1d7b756c Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Fri, 6 Nov 2026 04:28:46 -0400 Subject: [PATCH] fix(metadata): IIDs of generics over classes with a generic default interface A class whose default interface is itself generic, such as DeviceInformationCollection (IVectorView), had no wire signature: ClassDeclaration::default_interface() only returns plain interfaces, so IAsyncOperation got a zero IID. The QueryInterface for it failed, and on napi toPromise(DeviceInformation.FindAllAsync(...)) rejected with "interface property 'Completed' construction failed". The class signature now embeds the default interface's own pinterface(...) signature, as WinRT specifies; a test checks the result against RoGetParameterizedTypeInstanceIID. The metadata locator, which only the tests use, handles such classes with SetRuntimeClassParameterizedDefault instead of unwrapping. --- metadata/src/generic_instance_id_builder.rs | 83 +++++++++++++++++---- 1 file changed, 67 insertions(+), 16 deletions(-) diff --git a/metadata/src/generic_instance_id_builder.rs b/metadata/src/generic_instance_id_builder.rs index 8fa89fe..f095771 100644 --- a/metadata/src/generic_instance_id_builder.rs +++ b/metadata/src/generic_instance_id_builder.rs @@ -51,23 +51,45 @@ impl IRoMetaDataLocator_Impl for IRoMetaDataLocatorImpl { .downcast_ref::() .unwrap(); - let default_interface = class_declaration.default_interface().unwrap(); + let Ok(builder) = metadatadestination.ok() else { + return Ok(()); + }; + let Some(default_interface) = class_declaration.default_interface() else { + // A generic default interface goes by its parsed name elements. + let Some(generic) = class_declaration.default_interface_full_name() + else { + return Ok(()); + }; + let generic = HSTRING::from(generic.replace(", ", ",")); + let mut count = 0u32; + let mut parts: *mut HSTRING = std::ptr::null_mut(); + unsafe { RoParseTypeName(&generic, &mut count, &mut parts) }?; + let parts_slice = + unsafe { std::slice::from_raw_parts(parts, count as usize) }; + let elements: Vec = + parts_slice.iter().map(|p| PCWSTR(p.as_ptr())).collect(); + let result = unsafe { + builder.SetRuntimeClassParameterizedDefault(name, &elements) + }; + for part in parts_slice { + drop(unsafe { std::ptr::read(part) }); + } + unsafe { + windows::Win32::System::Com::CoTaskMemFree(Some(parts as *const _)) + }; + return result; + }; let default_interface_id = default_interface.id(); let full_name = HSTRING::from(default_interface.full_name()); let full_name = PCWSTR::from_raw(full_name.as_ptr()); - - if let Ok(builder) = metadatadestination.ok() { - let result = unsafe { - builder.SetRuntimeClassSimpleDefault( - name, - full_name, - Some(&default_interface_id), - ) - }; - - debug_assert!(result.is_ok()); - } - + let result = unsafe { + builder.SetRuntimeClassSimpleDefault( + name, + full_name, + Some(&default_interface_id), + ) + }; + debug_assert!(result.is_ok()); return Ok(()); } DeclarationKind::Interface => { @@ -335,8 +357,15 @@ fn wire_signature_for_name(name: &str) -> Option { } DeclarationKind::Class => { let class = lock.as_any().downcast_ref::()?; - let default_iface = class.default_interface()?; - Some(format!("rc({};{})", name, format_guid(&default_iface.id()))) + let default_signature = match class.default_interface() { + Some(iface) => format_guid(&iface.id()), + // A generic default interface (`IVectorView`) contributes its own signature. + None => { + let generic = class.default_interface_full_name()?.replace(", ", ","); + wire_signature_for_name(&generic)? + } + }; + Some(format!("rc({};{})", name, default_signature)) } DeclarationKind::Enum => { let enum_decl = lock.as_any().downcast_ref::()?; @@ -467,6 +496,28 @@ fn compute_winrt_piid(signature: &str) -> GUID { mod tests { use super::*; + fn os_iid(names: &[&str]) -> GUID { + let names: Vec = names.iter().map(|n| HSTRING::from(*n)).collect(); + let elements: Vec = names.iter().map(|n| PCWSTR(n.as_ptr())).collect(); + let locator = IRoMetaDataLocatorImpl; + let locator = IRoMetaDataLocator::new(&locator); + let mut iid = GUID::zeroed(); + unsafe { RoGetParameterizedTypeInstanceIID(&elements, &*locator, &mut iid, None).unwrap() }; + iid + } + + #[test] + fn class_with_generic_default_interface_matches_the_os() { + let expected = os_iid(&[ + "Windows.Foundation.IAsyncOperation`1", + "Windows.Devices.Enumeration.DeviceInformationCollection", + ]); + let actual = GenericInstanceIdBuilder::generate_id_from_name( + "Windows.Foundation.IAsyncOperation`1", + ); + assert_eq!(actual, expected); + } + #[test] fn known_piid_iasyncoperationwithprogress_httpresponsemessage_httpprogress() { // From Windows SDK windows.web.http.h