diff --git a/CMakeLists.txt b/CMakeLists.txt index b0089e7ad..6e1857892 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,7 +184,7 @@ if(EXTERNAL_WINMD_INCLUDE_DIR STREQUAL "") include(ExternalProject) ExternalProject_Add(winmd GIT_REPOSITORY https://github.com/microsoft/winmd.git - GIT_TAG 0f1eae3bfa63fa2ba3c2912cbfe72a01db94cc5a + GIT_TAG 1.0.260529.3 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index ab119ae89..76d076f51 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -206,6 +206,23 @@ namespace cppwinrt return { w, write_close_namespace }; } + [[nodiscard]] static finish_with wrap_extern_cpp_impl_namespace(writer& w) + { + w.write(R"( +extern "C++" +{ +namespace winrt::impl +{ +)"); + + return { w, [](writer& w) { + w.write(R"( +} // winrt::impl +} // extern "C++" +)"); + }}; + } + [[nodiscard]] static finish_with wrap_std_namespace(writer& w) { w.write(R"(namespace std @@ -537,6 +554,58 @@ namespace cppwinrt } } + static std::string make_pinterface_guard(std::string_view const& cpp_name) + { + std::string guard = "WINRT_IMPL_PINTERFACE_GUID_"; + guard.reserve(guard.size() + cpp_name.size()); + for (char c : cpp_name) + { + if (c == ':' || c == '<' || c == '>' || c == ',' || c == ' ') + guard += '_'; + else + guard += c; + } + return guard; + } + + static void write_generic_inst_specializations(writer& w, std::map const& instantiations) + { + if (instantiations.empty()) + { + return; + } + + // This block is self-contained (written outside any open namespace). + // extern "C++" attaches specializations to the global module so identical + // specializations across modules merge rather than collide. + // #ifndef guards prevent redefinition within a single TU (SCC-consolidated modules). + auto wrap_ns = wrap_extern_cpp_impl_namespace(w); + + for (auto&& [cpp_name, info] : instantiations) + { + auto guard = make_pinterface_guard(cpp_name); + { + auto wrap_guard = wrap_ifndef(w, guard); + w.write("#define %\n", guard); + w.write(" template <> struct pinterface_guid<%>\n", cpp_name); + w.write(" {\n"); + w.write(" static constexpr bool precomputed = true;\n"); + w.write(" static constexpr guid value{ "); + auto& g = info.guid; + w.write_printf("0x%08X,0x%04X,0x%04X,{ 0x%02X,0x%02X,0x%02X,0x%02X,0x%02X,0x%02X,0x%02X,0x%02X }", + g.Data1, g.Data2, g.Data3, + g.Data4[0], g.Data4[1], g.Data4[2], g.Data4[3], + g.Data4[4], g.Data4[5], g.Data4[6], g.Data4[7]); + w.write(" }; // "); + w.write_printf("%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X\n", + g.Data1, g.Data2, g.Data3, + g.Data4[0], g.Data4[1], g.Data4[2], g.Data4[3], + g.Data4[4], g.Data4[5], g.Data4[6], g.Data4[7]); + w.write(" };\n"); + } + } + } + static void write_struct_category(writer& w, TypeDef const& type) { auto format = R"( template <> struct category<%>{ using type = struct_category<%>; }; diff --git a/cppwinrt/cppwinrt.vcxproj b/cppwinrt/cppwinrt.vcxproj index b8beed890..3efa7a64c 100644 --- a/cppwinrt/cppwinrt.vcxproj +++ b/cppwinrt/cppwinrt.vcxproj @@ -1,6 +1,6 @@ - + Debug @@ -309,6 +309,6 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + \ No newline at end of file diff --git a/cppwinrt/file_writers.h b/cppwinrt/file_writers.h index fd9833cb1..0f42bde46 100644 --- a/cppwinrt/file_writers.h +++ b/cppwinrt/file_writers.h @@ -116,12 +116,32 @@ namespace cppwinrt w.write_each(members.interfaces); w.write_each(members.delegates); w.write_each(members.classes); + w.write_each(members.interfaces); w.write_each(members.delegates); w.write_each(members.interfaces); w.write_each(members.structs); } + // Emit pinterface_guid specializations AFTER the wrap_impl_namespace block + // so they don't break the WINRT_EXPORT attribute on subsequent declarations. + // The function writes a self-contained extern "C++" block. + { + std::map instantiations; + collect_generic_instantiations(w, members, instantiations); + + // For Windows.Foundation, also inject well-known IReference/IReferenceArray + // specializations for standard primitive and struct types. These ensure that + // consumers who only include Windows.Foundation.h get precomputed GUIDs for + // boxing types, without depending on other namespace headers. + if (ns == "Windows.Foundation") + { + add_well_known_ireference_instantiations(w, members, instantiations); + } + + write_generic_inst_specializations(w, instantiations); + } + write_close_file_guard(w); w.swap(); write_preamble(w); diff --git a/cppwinrt/helpers.h b/cppwinrt/helpers.h index 2dc152a74..aeed45279 100644 --- a/cppwinrt/helpers.h +++ b/cppwinrt/helpers.h @@ -1,7 +1,16 @@ #pragma once +#include "winmd_signature.h" + namespace cppwinrt { + using winmd_signature::guid_value; + using winmd_signature::extract_guid; + using winmd_signature::format_guid_signature; + using winmd_signature::compute_guid_from_signature; + using winmd_signature::type_arg_stack; + using winmd_signature::signature_builder; + static auto get_start_time() { return std::chrono::high_resolution_clock::now(); @@ -1145,4 +1154,315 @@ namespace cppwinrt return settings.component_filter.includes(class_name); } + + struct generic_inst_info + { + guid_value guid; + }; + + // ---- Recursive collection of concrete generic instantiations ---- + // Walks InterfaceImpl chains and method signatures, carrying concrete TypeSig args to resolve GenericTypeIndex. + + static void collect_generic_inst_recursive( + writer& w, + GenericTypeInstSig const& type, + type_arg_stack const& outer_resolve, + std::map& instantiations); + + // Helper: if a TypeSig contains a GenericTypeInstSig (directly or via TypeSpec), collect it. + static void collect_from_type_sig( + writer& w, + TypeSig const& sig, + type_arg_stack const& resolve, + std::map& instantiations) + { + call(sig.Type(), + [](ElementType) {}, + [&](GenericTypeIndex idx) + { + // Resolve and recurse if the resolved type is itself a generic instantiation + if (!resolve.empty() && idx.index < resolve.back().size()) + { + type_arg_stack parent_resolve(resolve.begin(), resolve.end() - 1); + collect_from_type_sig(w, resolve.back()[idx.index], parent_resolve, instantiations); + } + }, + [](GenericMethodTypeIndex) {}, + [&](coded_index const& t) + { + if (t.type() == TypeDefOrRef::TypeSpec) + { + collect_generic_inst_recursive(w, t.TypeSpec().Signature().GenericTypeInst(), resolve, instantiations); + } + }, + [&](GenericTypeInstSig const& t) + { + collect_generic_inst_recursive(w, t, resolve, instantiations); + }); + } + + static void collect_generic_inst_recursive( + writer& w, + GenericTypeInstSig const& type, + type_arg_stack const& outer_resolve, + std::map& instantiations) + { + // Build the resolution context for this instantiation: + // The args of 'type' may themselves contain GenericTypeIndex references + // that need resolving through outer_resolve. + // Collect the concrete TypeSig args after resolution. + std::vector concrete_args; + for (auto&& arg : type.GenericArgs()) + { + if (auto* idx = std::get_if(&arg.Type())) + { + // Resolve through the outer stack + if (outer_resolve.empty() || idx->index >= outer_resolve.back().size()) + { + return; // Can't resolve — open generic, skip + } + concrete_args.push_back(outer_resolve.back()[idx->index]); + } + else + { + concrete_args.push_back(arg); + } + } + + // Use cpp_name as the dedup key. Only do expensive work if this is a new entry. + auto cpp_name = w.write_temp("%", type); + auto [it, inserted] = instantiations.try_emplace(std::move(cpp_name)); + if (!inserted) + { + return; + } + + it->second.guid = compute_guid_from_signature(signature_builder::get_signature(type, outer_resolve)); + + // Recurse into generic args that are themselves generic instantiations + for (auto&& arg : concrete_args) + { + if (auto* spec = std::get_if>(&arg.Type())) + { + if (spec->type() == TypeDefOrRef::TypeSpec) + { + collect_generic_inst_recursive(w, spec->TypeSpec().Signature().GenericTypeInst(), outer_resolve, instantiations); + } + } + else if (auto* inst = std::get_if(&arg.Type())) + { + collect_generic_inst_recursive(w, *inst, outer_resolve, instantiations); + } + } + + // Build a resolution stack with our concrete args appended + type_arg_stack resolve = outer_resolve; + resolve.push_back(concrete_args); + + // Push the writer's generic_param_stack so write_temp resolves GenericTypeIndex in nested calls + auto writer_guard = w.push_generic_params(type); + + // Recurse into the generic type's required interfaces (e.g., IMap : IIterable, etc.) + auto base_type = find_required(type.GenericType()); + + for (auto&& impl : base_type.InterfaceImpl()) + { + auto iface = impl.Interface(); + if (iface.type() == TypeDefOrRef::TypeSpec) + { + // This TypeSpec may have GenericTypeIndex references to base_type's params. + // 'resolve' has our concrete args at the back, so signature_builder can resolve them. + collect_generic_inst_recursive(w, iface.TypeSpec().Signature().GenericTypeInst(), resolve, instantiations); + } + } + + // Walk method signatures to find generic types in return types and parameters. + // E.g., IIterable.First() returns IIterator, so IIterable> + // produces IIterator> which needs name_v/guid_v. + for (auto&& method : base_type.MethodList()) + { + auto sig = method.Signature(); + if (sig.ReturnType()) + { + collect_from_type_sig(w, sig.ReturnType().Type(), resolve, instantiations); + } + for (auto&& param : sig.Params()) + { + collect_from_type_sig(w, param.Type(), resolve, instantiations); + } + } + } + + // Entry point: collect all concrete generic instantiations from a namespace's members. + static void collect_generic_instantiations( + writer& w, + cache::namespace_members const& members, + std::map& instantiations) + { + auto collect_from_type = [&](TypeDef const& type) + { + for (auto&& impl : type.InterfaceImpl()) + { + auto iface = impl.Interface(); + if (iface.type() == TypeDefOrRef::TypeSpec) + { + collect_generic_inst_recursive(w, iface.TypeSpec().Signature().GenericTypeInst(), {}, instantiations); + } + } + + // Also walk the type's own methods for concrete generic return/param types. + for (auto&& method : type.MethodList()) + { + auto sig = method.Signature(); + if (sig.ReturnType()) + { + collect_from_type_sig(w, sig.ReturnType().Type(), {}, instantiations); + } + for (auto&& param : sig.Params()) + { + collect_from_type_sig(w, param.Type(), {}, instantiations); + } + } + }; + + for (auto&& type : members.classes) + { + collect_from_type(type); + for (auto&& base : get_bases(type)) + { + collect_from_type(base); + } + } + + for (auto&& type : members.interfaces) + { + if (empty(type.GenericParam())) + { + collect_from_type(type); + } + } + } + + // Add well-known IReference and IReferenceArray instantiations for standard primitive + // and Windows.Foundation struct types. These are emitted into Windows.Foundation.0.h so that + // consumers who only include Windows.Foundation.h (e.g. for boxing) still get precomputed GUIDs + // without requiring a header from a namespace that happens to use IReference etc. + // + // Uses the same code path as metadata-discovered instantiations by constructing + // GenericTypeInstSig objects and feeding them through collect_generic_inst_recursive. + // This ensures cpp_name, guard names, and signatures are always consistent. + static void add_well_known_ireference_instantiations( + writer& w, + cache::namespace_members const& members, + std::map& instantiations) + { + auto find_type = [&](std::string_view name) -> TypeDef + { + auto it = members.types.find(name); + return it != members.types.end() ? it->second : TypeDef{}; + }; + + auto iref = find_type("IReference`1"); + auto iref_arr = find_type("IReferenceArray`1"); + if (!iref || !iref_arr) return; + + // Find the System.Guid TypeRef via IPropertyValue.GetGuid's return type. + // There is no TypeDef for System.Guid, only scattered TypeRefs. + // IPropertyValue is in Windows.Foundation, so it's available in members. + // The GetGuid method is at a stable ABI vtable position (index 14, 0-based + // within MethodList), but we fall back to a name search if that doesn't match. + coded_index guid_type_ref{}; + auto ipv = find_type("IPropertyValue"); + if (ipv) + { + // Find the GetGuid method — try index 14 first (stable ABI vtable position), + // then fall back to linear search. + auto methods = ipv.MethodList(); + MethodDef get_guid_method{}; + + if (size(methods) > 14 && methods.first[14].Name() == "GetGuid") + { + get_guid_method = methods.first[14]; + } + else + { + for (auto&& m : methods) + { + if (m.Name() == "GetGuid") + { + get_guid_method = m; + break; + } + } + } + + if (get_guid_method) + { + auto method_sig = get_guid_method.Signature(); + auto const& ret_type = method_sig.ReturnType().Type().Type(); + if (std::holds_alternative>(ret_type)) + { + guid_type_ref = std::get>(ret_type); + } + } + } + + // Collect TypeSig args for all well-known type arguments. + // Primitives use ElementType directly; WinRT structs use their TypeDef; + // Guid uses the TypeRef found above; Object uses ElementType::Object. + std::vector type_args; + type_args.reserve(20); + + // Fundamental types + type_args.emplace_back(ElementType::U1); + type_args.emplace_back(ElementType::I2); + type_args.emplace_back(ElementType::U2); + type_args.emplace_back(ElementType::I4); + type_args.emplace_back(ElementType::U4); + type_args.emplace_back(ElementType::I8); + type_args.emplace_back(ElementType::U8); + type_args.emplace_back(ElementType::R4); + type_args.emplace_back(ElementType::R8); + type_args.emplace_back(ElementType::Char); + type_args.emplace_back(ElementType::Boolean); + type_args.emplace_back(ElementType::String); + + // Guid (only if we found the TypeRef) + if (guid_type_ref) + { + type_args.emplace_back(guid_type_ref); + } + + // Windows.Foundation struct types + static constexpr std::string_view struct_names[] = { "DateTime", "TimeSpan", "Point", "Size", "Rect" }; + for (auto name : struct_names) + { + auto td = find_type(name); + if (td) + { + type_args.emplace_back(td.coded_index()); + } + } + + auto iref_coded = iref.coded_index(); + auto iref_arr_coded = iref_arr.coded_index(); + + for (auto const& type_arg : type_args) + { + // IReference + auto ireference_inst = GenericTypeInstSig{ iref_coded, std::vector{ type_arg } }; + collect_generic_inst_recursive(w, ireference_inst, {}, instantiations); + + // IReferenceArray + auto ireference_array_inst = GenericTypeInstSig{ iref_arr_coded, std::vector{ type_arg } }; + collect_generic_inst_recursive(w, ireference_array_inst, {}, instantiations); + } + + // IReferenceArray is valid (for passing arrays of inspectable objects), + // but IReference is not (IReference boxes value types; Object is a + // reference type). So Object is not in the shared type_args list above. + auto object_arg = TypeSig{ ElementType::Object }; + auto ireference_array_object_inst = GenericTypeInstSig{ iref_arr_coded, std::vector{ object_arg } }; + collect_generic_inst_recursive(w, ireference_array_object_inst, {}, instantiations); + } } diff --git a/cppwinrt/packages.config b/cppwinrt/packages.config index 9b3264e46..7b2ee1f28 100644 --- a/cppwinrt/packages.config +++ b/cppwinrt/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/cppwinrt/winmd_signature.h b/cppwinrt/winmd_signature.h new file mode 100644 index 000000000..681df4178 --- /dev/null +++ b/cppwinrt/winmd_signature.h @@ -0,0 +1,383 @@ +#pragma once + +// Shared utilities for computing WinRT parameterized interface GUIDs from metadata. +// Used by both cppwinrt.exe (code generator) and the natvis visualizer. +// Only depends on winmd_reader.h — no dependency on the cppwinrt writer or natvis types. + +namespace winmd_signature +{ + using namespace winmd::reader; + + // ---- visit helper (works in both cppwinrt.exe and natvis contexts) ---- + template struct overloaded : T... { using T::operator()...; }; + template overloaded(T...) -> overloaded; + + template + auto call(V&& variant, C&&...c) + { + return std::visit(overloaded{ std::forward(c)... }, std::forward(variant)); + } + + // ---- GUID value type (plain struct, no dependency on winrt::guid) ---- + struct guid_value + { + std::uint32_t Data1; + std::uint16_t Data2; + std::uint16_t Data3; + std::uint8_t Data4[8]; + }; + + // ---- Extract a GUID from a TypeDef's GuidAttribute ---- + inline guid_value extract_guid(TypeDef const& type) + { + auto attribute = get_attribute(type, "Windows.Foundation.Metadata", "GuidAttribute"); + if (!attribute) + { + winmd::impl::throw_invalid("'Windows.Foundation.Metadata.GuidAttribute' attribute for type '", + type.TypeNamespace(), ".", type.TypeName(), "' not found"); + } + auto args = attribute.Value().FixedArgs(); + return + { + std::get(std::get(args[0].value).value), + std::get(std::get(args[1].value).value), + std::get(std::get(args[2].value).value), + { + std::get(std::get(args[3].value).value), + std::get(std::get(args[4].value).value), + std::get(std::get(args[5].value).value), + std::get(std::get(args[6].value).value), + std::get(std::get(args[7].value).value), + std::get(std::get(args[8].value).value), + std::get(std::get(args[9].value).value), + std::get(std::get(args[10].value).value) + } + }; + } + + // ---- Format a GUID as a lowercase signature string: {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} ---- + namespace impl + { + inline void write_hex(char*& p, std::uint8_t value) + { + static constexpr char digits[] = "0123456789abcdef"; + *p++ = digits[value >> 4]; + *p++ = digits[value & 0xF]; + } + + inline void write_hex(char*& p, std::uint16_t value) + { + write_hex(p, static_cast(value >> 8)); + write_hex(p, static_cast(value & 0xFF)); + } + + inline void write_hex(char*& p, std::uint32_t value) + { + write_hex(p, static_cast(value >> 16)); + write_hex(p, static_cast(value & 0xFFFF)); + } + } + + inline std::string format_guid_signature(guid_value const& g) + { + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} = 38 chars + null + char buf[39]; + char* p = buf; + *p++ = '{'; + impl::write_hex(p, g.Data1); *p++ = '-'; + impl::write_hex(p, g.Data2); *p++ = '-'; + impl::write_hex(p, g.Data3); *p++ = '-'; + impl::write_hex(p, g.Data4[0]); + impl::write_hex(p, g.Data4[1]); *p++ = '-'; + impl::write_hex(p, g.Data4[2]); + impl::write_hex(p, g.Data4[3]); + impl::write_hex(p, g.Data4[4]); + impl::write_hex(p, g.Data4[5]); + impl::write_hex(p, g.Data4[6]); + impl::write_hex(p, g.Data4[7]); + *p++ = '}'; + return { buf, static_cast(p - buf) }; + } + + // ---- SHA1 computation ---- + struct sha1_context + { + static constexpr std::uint32_t K[4] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 }; + + static std::uint32_t rotl(std::uint8_t bits, std::uint32_t word) + { + return (word << bits) | (word >> (32 - bits)); + } + + static std::array process_block(std::uint8_t const* input, std::size_t start, std::array hash) + { + std::array W{}; + for (std::size_t t = 0; t < 16; ++t) + { + W[t] = static_cast(input[start + t * 4]) << 24 | + static_cast(input[start + t * 4 + 1]) << 16 | + static_cast(input[start + t * 4 + 2]) << 8 | + static_cast(input[start + t * 4 + 3]); + } + for (std::size_t t = 16; t < 80; ++t) + { + W[t] = rotl(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]); + } + + auto A = hash[0], B = hash[1], C = hash[2], D = hash[3], E = hash[4]; + std::uint32_t temp; + for (std::size_t t = 0; t < 20; ++t) + { + temp = rotl(5, A) + ((B & C) ^ ((~B) & D)) + E + W[t] + K[0]; + E = D; D = C; C = rotl(30, B); B = A; A = temp; + } + for (std::size_t t = 20; t < 40; ++t) + { + temp = rotl(5, A) + (B ^ C ^ D) + E + W[t] + K[1]; + E = D; D = C; C = rotl(30, B); B = A; A = temp; + } + for (std::size_t t = 40; t < 60; ++t) + { + temp = rotl(5, A) + ((B & C) ^ (B & D) ^ (C & D)) + E + W[t] + K[2]; + E = D; D = C; C = rotl(30, B); B = A; A = temp; + } + for (std::size_t t = 60; t < 80; ++t) + { + temp = rotl(5, A) + (B ^ C ^ D) + E + W[t] + K[3]; + E = D; D = C; C = rotl(30, B); B = A; A = temp; + } + + return { hash[0] + A, hash[1] + B, hash[2] + C, hash[3] + D, hash[4] + E }; + } + + static std::array compute(std::vector const& input) + { + std::array hash{ 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; + std::size_t i = 0; + + while (i + 64 <= input.size()) + { + hash = process_block(input.data(), i, hash); + i += 64; + } + + auto bit_length = static_cast(input.size()) * 8; + auto remainder_size = input.size() % 64; + + std::array final_block{}; + std::copy(input.begin() + i, input.end(), final_block.begin()); + final_block[remainder_size] = 0x80; + + std::size_t block_count; + if (remainder_size + 1 + 8 <= 64) + { + block_count = 64; + } + else + { + block_count = 128; + } + + for (int b = 7; b >= 0; --b) + { + final_block[block_count - 8 + (7 - b)] = static_cast((bit_length >> (b * 8)) & 0xFF); + } + + hash = process_block(final_block.data(), 0, hash); + if (block_count == 128) + { + hash = process_block(final_block.data(), 64, hash); + } + + std::array result{}; + for (std::size_t idx = 0; idx < 20; ++idx) + { + result[idx] = static_cast(hash[idx >> 2] >> (8 * (3 - (idx & 0x03)))); + } + return result; + } + }; + + // ---- Compute a UUID v5 GUID from a WinRT type signature string ---- + inline guid_value compute_guid_from_signature(std::string const& sig) + { + constexpr std::uint8_t namespace_bytes[] = + { + // {11f47ad5-7b73-42c0-abae-878b1e16adee} in big-endian byte order (RFC 4122) + 0x11, 0xf4, 0x7a, 0xd5, + 0x7b, 0x73, + 0x42, 0xc0, + 0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16, 0xad, 0xee + }; + + std::vector buffer(std::begin(namespace_bytes), std::end(namespace_bytes)); + buffer.insert(buffer.end(), sig.begin(), sig.end()); + + auto hash = sha1_context::compute(buffer); + + guid_value result; + result.Data1 = static_cast(hash[0]) << 24 | static_cast(hash[1]) << 16 | + static_cast(hash[2]) << 8 | static_cast(hash[3]); + result.Data2 = static_cast(hash[4]) << 8 | hash[5]; + result.Data3 = static_cast(hash[6]) << 8 | hash[7]; + std::copy(hash.begin() + 8, hash.begin() + 16, result.Data4); + + // Set version=5 and variant + result.Data3 = static_cast((result.Data3 & 0x0FFF) | (5 << 12)); + result.Data4[0] = static_cast((result.Data4[0] & 0x3F) | 0x80); + + return result; + } + + // ---- Resolution context for substituting GenericTypeIndex with concrete TypeSig values ---- + using type_arg_stack = std::vector>; + + // ---- signature_builder: computes WinRT type signature strings from metadata ---- + // Accepts an optional resolution stack to substitute GenericTypeIndex. + // Operates entirely on metadata types (TypeDef, TypeRef, TypeSpec, TypeSig). + struct signature_builder + { + static std::string get_signature(coded_index const& type, type_arg_stack const& resolve = {}) + { + switch (type.type()) + { + case TypeDefOrRef::TypeDef: + return get_signature(type.TypeDef()); + case TypeDefOrRef::TypeRef: + return get_signature(type.TypeRef()); + default: + return get_signature(type.TypeSpec().Signature().GenericTypeInst(), resolve); + } + } + + static std::string get_signature(GenericTypeInstSig const& type, type_arg_stack const& resolve = {}) + { + std::string sig = "pinterface(" + get_guid_string(type.GenericType()); + for (auto&& arg : type.GenericArgs()) + { + sig += ";"; + sig += get_signature(arg, resolve); + } + sig += ")"; + return sig; + } + + private: + static std::string get_full_name(TypeDef const& type) + { + return std::string(type.TypeNamespace()) + "." + std::string(type.TypeName()); + } + + static std::string get_default_interface_signature(TypeDef const& type) + { + for (auto&& impl : type.InterfaceImpl()) + { + if (get_attribute(impl, "Windows.Foundation.Metadata", "DefaultAttribute")) + { + return "rc(" + get_full_name(type) + ";" + get_signature(impl.Interface()) + ")"; + } + } + return {}; + } + + static std::string get_enum_signature(TypeDef const& type) + { + bool is_flags = static_cast(get_attribute(type, "System", "FlagsAttribute")); + return "enum(" + get_full_name(type) + ";" + (is_flags ? "u4" : "i4") + ")"; + } + + static std::string get_struct_signature(TypeDef const& type) + { + std::string sig = "struct(" + get_full_name(type); + for (auto& field : type.FieldList()) + { + sig += ";"; + sig += get_signature(field.Signature().Type()); + } + sig += ")"; + return sig; + } + + static std::string get_guid_string(TypeDef const& type) + { + return format_guid_signature(extract_guid(type)); + } + + static std::string get_guid_string(coded_index const& type) + { + switch (type.type()) + { + case TypeDefOrRef::TypeDef: + return get_guid_string(type.TypeDef()); + case TypeDefOrRef::TypeRef: + return get_guid_string(find_required(type.TypeRef())); + default: + return get_signature(type.TypeSpec().Signature().GenericTypeInst()); + } + } + + static std::string get_element_signature(ElementType t) + { + switch (t) + { + case ElementType::Boolean: return "b1"; + case ElementType::Char: return "c2"; + case ElementType::I1: return "i1"; + case ElementType::U1: return "u1"; + case ElementType::I2: return "i2"; + case ElementType::U2: return "u2"; + case ElementType::I4: return "i4"; + case ElementType::U4: return "u4"; + case ElementType::I8: return "i8"; + case ElementType::U8: return "u8"; + case ElementType::R4: return "f4"; + case ElementType::R8: return "f8"; + case ElementType::String: return "string"; + case ElementType::Object: return "cinterface(IInspectable)"; + default: return {}; + } + } + + static std::string get_signature(TypeSig::value_type const& type, type_arg_stack const& resolve = {}) + { + return call(type, + [&](ElementType t) -> std::string { return get_element_signature(t); }, + [&](GenericTypeIndex idx) -> std::string + { + if (!resolve.empty() && idx.index < resolve.back().size()) + { + type_arg_stack parent_resolve(resolve.begin(), resolve.end() - 1); + return get_signature(resolve.back()[idx.index], parent_resolve); + } + return {}; + }, + [](GenericMethodTypeIndex) -> std::string { return {}; }, + [&](auto&& t) -> std::string { return get_signature(t, resolve); }); + } + + static std::string get_signature(TypeDef const& type) + { + switch (get_category(type)) + { + case category::interface_type: return get_guid_string(type); + case category::class_type: return get_default_interface_signature(type); + case category::enum_type: return get_enum_signature(type); + case category::struct_type: return get_struct_signature(type); + case category::delegate_type: return "delegate(" + get_guid_string(type) + ")"; + default: return {}; + } + } + + static std::string get_signature(TypeRef const& type) + { + if (type.TypeNamespace() == "System" && type.TypeName() == "Guid") + return "g16"; + return get_signature(find_required(type)); + } + + static std::string get_signature(TypeSig const& sig, type_arg_stack const& resolve = {}) + { + return get_signature(sig.Type(), resolve); + } + }; +} diff --git a/natvis/cppwinrtvisualizer.vcxproj b/natvis/cppwinrtvisualizer.vcxproj index af3fbcf1f..4232a41ea 100644 --- a/natvis/cppwinrtvisualizer.vcxproj +++ b/natvis/cppwinrtvisualizer.vcxproj @@ -1,6 +1,6 @@ - + Debug @@ -321,6 +321,6 @@ - + \ No newline at end of file diff --git a/natvis/packages.config b/natvis/packages.config index 7907cba44..2176891c4 100644 --- a/natvis/packages.config +++ b/natvis/packages.config @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/natvis/type_resolver.cpp b/natvis/type_resolver.cpp index de8af71f4..c7c9a891c 100644 --- a/natvis/type_resolver.cpp +++ b/natvis/type_resolver.cpp @@ -1,4 +1,5 @@ #include "pch.h" +#include "winmd_signature.h" using namespace winrt; using namespace winmd::reader; @@ -7,47 +8,14 @@ using namespace Microsoft::VisualStudio::Debugger; static std::map, std::pair> _cache; -template -static bool has_attribute(T const& row, std::string_view const& type_namespace, std::string_view const& type_name) noexcept +static guid to_winrt_guid(winmd_signature::guid_value const& g) { - return static_cast(get_attribute(row, type_namespace, type_name)); -} - -template -static auto call(V&& variant, C&& ...call) -{ - return std::visit(overloaded{ std::forward(call)... }, std::forward(variant)); -} - -static std::string get_full_name(TypeDef const& type) -{ - return std::string(type.TypeNamespace()) + "." + std::string(type.TypeName()); + return { g.Data1, g.Data2, g.Data3, { g.Data4[0], g.Data4[1], g.Data4[2], g.Data4[3], g.Data4[4], g.Data4[5], g.Data4[6], g.Data4[7] } }; } static guid get_guid(TypeDef const& type) { - auto attribute = get_attribute(type, "Windows.Foundation.Metadata", "GuidAttribute"); - if (!attribute) - { - throw_invalid("'Windows.Foundation.Metadata.GuidAttribute' attribute for type '", get_full_name(type), "' not found"); - } - auto args = attribute.Value().FixedArgs(); - return - { - std::get(std::get(args[0].value).value), - std::get(std::get(args[1].value).value), - std::get(std::get(args[2].value).value), - { - std::get(std::get(args[3].value).value), - std::get(std::get(args[4].value).value), - std::get(std::get(args[5].value).value), - std::get(std::get(args[6].value).value), - std::get(std::get(args[7].value).value), - std::get(std::get(args[8].value).value), - std::get(std::get(args[9].value).value), - std::get(std::get(args[10].value).value) - } - }; + return to_winrt_guid(winmd_signature::extract_guid(type)); } static std::wstring format_guid(guid guid) @@ -70,212 +38,10 @@ static std::wstring format_guid(guid guid) return guid_str; } -static auto get_default_interface(TypeDef const& type) -{ - auto impls = type.InterfaceImpl(); - for (auto&& impl : impls) - { - if (has_attribute(impl, "Windows.Foundation.Metadata"sv, "DefaultAttribute"sv)) - { - return impl.Interface(); - } - } - throw_invalid("Type '", get_full_name(type), "' does not have a default interface"); -} - -struct signature_generator -{ - static std::string get_signature(coded_index const& type) - { - switch (type.type()) - { - case TypeDefOrRef::TypeDef: - return get_signature(type.TypeDef()); - case TypeDefOrRef::TypeRef: - return get_signature(type.TypeRef()); - default: //case TypeDefOrRef::TypeSpec: - return get_signature(type.TypeSpec().Signature().GenericTypeInst()); - } - } - - static std::string get_signature(GenericTypeInstSig const& type) - { - std::string sig = "pinterface(" + get_guid_signature(type.GenericType()); - for (auto&& arg : type.GenericArgs()) - { - sig += ";"; - sig += get_signature(arg); - } - sig += ")"; - return sig; - } - -private: - static std::string get_class_signature(TypeDef const& type) - { - return std::string("rc(") + get_full_name(type) + ";" + get_signature(get_default_interface(type)) + ")"; - } - - static auto get_enum_signature(TypeDef const& type) - { - bool is_flags = has_attribute(type, "System"sv, "FlagsAttribute"sv); - return "enum(" + get_full_name(type) + ";" + (is_flags ? "u4" : "i4") + ")"; - } - - static std::string get_struct_signature(TypeDef const& type) - { - std::string sig = "struct(" + get_full_name(type); - for (auto& field : type.FieldList()) - { - sig += ";"; - sig += get_signature(field.Signature().Type()); - } - sig += ")"; - return sig; - } - - static std::string get_guid_signature(TypeDef const& type) - { - auto guid = get_guid(type); - std::string guid_str(70, '?'); - int count = sprintf_s(guid_str.data(), guid_str.size() + 1, - "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", - guid.Data1, - guid.Data2, - guid.Data3, - guid.Data4[0], - guid.Data4[1], - guid.Data4[2], - guid.Data4[3], - guid.Data4[4], - guid.Data4[5], - guid.Data4[6], - guid.Data4[7]); - guid_str.resize(count); - return guid_str; - } - - static std::string get_guid_signature(coded_index const& type) - { - switch (type.type()) - { - case TypeDefOrRef::TypeDef: - return get_guid_signature(type.TypeDef()); - case TypeDefOrRef::TypeRef: - return get_guid_signature(find_required(type.TypeRef())); - default: //case TypeDefOrRef::TypeSpec: - return get_signature(type.TypeSpec().Signature().GenericTypeInst()); - } - } - - static std::string get_signature(TypeSig::value_type const& type) - { - return call(type, [&](ElementType type) -> std::string - { - switch (type) - { - case ElementType::Boolean: return "b1"; - case ElementType::Char: return "c2"; - case ElementType::I1: return "i1"; - case ElementType::U1: return "u1"; - case ElementType::I2: return "i2"; - case ElementType::U2: return "u2"; - case ElementType::I4: return "i4"; - case ElementType::U4: return "u4"; - case ElementType::I8: return "i8"; - case ElementType::U8: return "u8"; - case ElementType::R4: return "f4"; - case ElementType::R8: return "f8"; - case ElementType::String: return "string"; - case ElementType::Object: return "cinterface(IInspectable)"; - default: assert(false); return ""; - } - }, - [&](auto&& type) - { - return get_signature(type); - }); - } - - static std::string get_signature(TypeDef const& type) - { - switch (get_category(type)) - { - case category::interface_type: - return get_guid_signature(type); - case category::class_type: - return get_class_signature(type); - case category::enum_type: - return get_enum_signature(type); - case category::struct_type: - return get_struct_signature(type); - default: //case category::delegate_type: - return "delegate(" + get_guid_signature(type) + ")"; - } - } - - static std::string get_signature(TypeRef const& type) - { - if (type.TypeNamespace() == "System" && type.TypeName() == "Guid") - { - return "g16"; - } - return get_signature(find_required(type)); - } - - static std::string get_signature(TypeSig const& signature) - { - return get_signature(signature.Type()); - } -}; - -using namespace winrt::impl; - -static auto calculate_sha1(std::vector const& input) -{ - auto input_size = input.size(); - - std::array intermediate_hash{ 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; - uint32_t i = 0; - while (i + 64 <= input_size) - { - intermediate_hash = process_msg_block(input.data(), i, intermediate_hash); - i += 64; - } - - auto length = size_to_bytes(input_size * 8); - auto remainder_size = (input_size % 64) + 1; - if (remainder_size + 8 <= 64) - { - std::array remainder{}; - std::copy(input.begin() + i, input.end(), remainder.begin()); - remainder[remainder_size - 1] = 0x80; - std::copy(length.begin(), length.end(), remainder.end() - 8); - intermediate_hash = process_msg_block(remainder.data(), 0, intermediate_hash); - } - else - { - std::array remainder{}; - std::copy(input.begin() + i, input.end(), remainder.begin()); - remainder[remainder_size - 1] = 0x80; - std::copy(length.begin(), length.end(), remainder.end() - 8); - intermediate_hash = process_msg_block(remainder.data(), 0, intermediate_hash); - intermediate_hash = process_msg_block(remainder.data(), 64, intermediate_hash); - } - - return get_result(intermediate_hash); -} - static guid generate_guid(GenericTypeInstSig const& type) { - constexpr guid namespace_guid = { 0xd57af411, 0x737b, 0xc042,{ 0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16, 0xad, 0xee } }; - constexpr auto namespace_bytes = winrt::impl::to_array(namespace_guid); - - std::vector buffer{ namespace_bytes.begin(), namespace_bytes.end() }; - auto sig = signature_generator::get_signature(type); - buffer.insert(buffer.end(), sig.begin(), sig.end()); - - return set_named_guid_fields(endian_swap(to_guid(calculate_sha1(buffer)))); + auto sig = winmd_signature::signature_builder::get_signature(type); + return to_winrt_guid(winmd_signature::compute_guid_from_signature(sig)); } std::pair ResolveTypeInterface(DkmProcess* process, winmd::reader::TypeSig const& typeSig) diff --git a/strings/base_identity.h b/strings/base_identity.h index 4a5bedbb1..77cc3b0f0 100644 --- a/strings/base_identity.h +++ b/strings/base_identity.h @@ -453,6 +453,7 @@ WINRT_EXPORT namespace winrt::impl template struct pinterface_guid { + static constexpr bool precomputed = false; #ifdef _MSC_VER #pragma warning(suppress: 4307) #endif diff --git a/test/test/pinterface_guid_precomputed.cpp b/test/test/pinterface_guid_precomputed.cpp new file mode 100644 index 000000000..8ec754b4a --- /dev/null +++ b/test/test/pinterface_guid_precomputed.cpp @@ -0,0 +1,134 @@ +#include "pch.h" + +// Validate that pre-computed pinterface_guid specializations: +// 1. Have the precomputed flag set to true +// 2. Produce GUID values matching the algorithmic SHA-1 computation +// +// Tests cover both code-generated IReference/IReferenceArray +// specializations injected via add_well_known_ireference_instantiations into +// the generated Windows.Foundation.0.h output and other code-generated +// specializations discovered from Windows SDK metadata (IMap, IIterable, +// TypedEventHandler, etc.). + +using namespace winrt; +using namespace winrt::impl; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Collections; + +namespace +{ + constexpr bool equal(guid const& left, guid const& right) noexcept + { + return left.Data1 == right.Data1 && + left.Data2 == right.Data2 && + left.Data3 == right.Data3 && + left.Data4[0] == right.Data4[0] && + left.Data4[1] == right.Data4[1] && + left.Data4[2] == right.Data4[2] && + left.Data4[3] == right.Data4[3] && + left.Data4[4] == right.Data4[4] && + left.Data4[5] == right.Data4[5] && + left.Data4[6] == right.Data4[6] && + left.Data4[7] == right.Data4[7]; + } +} + +// Verify the specialization is marked precomputed and its value matches the +// SHA-1 computation (generate_guid + signature::data), which is the +// algorithm the primary template would use. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4307) // integral constant overflow from SHA-1 +#endif + +#define REQUIRE_PRECOMPUTED(...) \ + STATIC_REQUIRE(pinterface_guid<__VA_ARGS__>::precomputed); \ + STATIC_REQUIRE(equal(pinterface_guid<__VA_ARGS__>::value, generate_guid(signature<__VA_ARGS__>::data))) + +TEST_CASE("pinterface_guid_precomputed_ireference") +{ + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); + REQUIRE_PRECOMPUTED(IReference); +} + +TEST_CASE("pinterface_guid_precomputed_ireferencearray") +{ + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); + REQUIRE_PRECOMPUTED(IReferenceArray); +} + +TEST_CASE("pinterface_guid_precomputed_from_metadata") +{ + // These specializations are discovered from metadata: closed generic types + // that appear as method parameters, return types, or implemented interfaces + // in the Windows SDK. The IPropertyValue family uses IMap/IIterable/IKeyValuePair + // with and . + + // IMap / IMapView chains (used by PropertySet, ValueSet, etc.) + REQUIRE_PRECOMPUTED(IMap); + REQUIRE_PRECOMPUTED(IMap); + REQUIRE_PRECOMPUTED(IMapView); + REQUIRE_PRECOMPUTED(IMapView); + REQUIRE_PRECOMPUTED(IKeyValuePair); + REQUIRE_PRECOMPUTED(IKeyValuePair); + + // IIterable / IIterator over key-value pairs + REQUIRE_PRECOMPUTED(IIterable>); + REQUIRE_PRECOMPUTED(IIterable>); + REQUIRE_PRECOMPUTED(IIterator>); + REQUIRE_PRECOMPUTED(IIterator>); + + // IObservableMap and change handlers + REQUIRE_PRECOMPUTED(IObservableMap); + REQUIRE_PRECOMPUTED(IObservableMap); + REQUIRE_PRECOMPUTED(IMapChangedEventArgs); + REQUIRE_PRECOMPUTED(MapChangedEventHandler); + REQUIRE_PRECOMPUTED(MapChangedEventHandler); + + // TypedEventHandler from Windows.Foundation (e.g. IMemoryBufferReference.Closed) + REQUIRE_PRECOMPUTED(TypedEventHandler); + + // WwwFormUrlDecoder collections + REQUIRE_PRECOMPUTED(IIterable); + REQUIRE_PRECOMPUTED(IIterator); + REQUIRE_PRECOMPUTED(IVectorView); +} + +#undef REQUIRE_PRECOMPUTED + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/test/test/test.vcxproj b/test/test/test.vcxproj index 43928dab2..b28d3eddf 100644 --- a/test/test/test.vcxproj +++ b/test/test/test.vcxproj @@ -296,6 +296,7 @@ NotUsing NotUsing + diff --git a/test/test_cpp20_module/pinterface_guid_precomputed.cpp b/test/test_cpp20_module/pinterface_guid_precomputed.cpp new file mode 100644 index 000000000..02ca02c90 --- /dev/null +++ b/test/test_cpp20_module/pinterface_guid_precomputed.cpp @@ -0,0 +1,120 @@ +#include "pch.h" + +import std; +import winrt.Windows.Foundation; +import winrt.Windows.Foundation.Collections; + +// Validate that pre-computed pinterface_guid specializations: +// 1. Have the precomputed flag set to true +// 2. Produce correct GUID values across module boundaries +// +// Tests cover both well-known IReference/IReferenceArray specializations +// injected by add_well_known_ireference_instantiations into the generated +// Windows.Foundation.0.h output and code-generated specializations discovered +// from Windows SDK metadata (IMap, IIterable, TypedEventHandler, etc.). + +using namespace winrt; +using namespace winrt::impl; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Collections; + +namespace +{ + constexpr bool equal(guid const& left, guid const& right) noexcept + { + return left.Data1 == right.Data1 && + left.Data2 == right.Data2 && + left.Data3 == right.Data3 && + left.Data4[0] == right.Data4[0] && + left.Data4[1] == right.Data4[1] && + left.Data4[2] == right.Data4[2] && + left.Data4[3] == right.Data4[3] && + left.Data4[4] == right.Data4[4] && + left.Data4[5] == right.Data4[5] && + left.Data4[6] == right.Data4[6] && + left.Data4[7] == right.Data4[7]; + } +} + +#define REQUIRE_PRECOMPUTED_GUID(expected_guid, ...) \ + STATIC_REQUIRE(pinterface_guid<__VA_ARGS__>::precomputed); \ + STATIC_REQUIRE(equal(guid(expected_guid), guid_of<__VA_ARGS__>())) + +TEST_CASE("module_pinterface_guid_ireference") +{ + REQUIRE_PRECOMPUTED_GUID("E5198CC8-2873-55F5-B0A1-84FF9E4AAD62", IReference); + REQUIRE_PRECOMPUTED_GUID("6EC9E41B-6709-5647-9918-A1270110FC4E", IReference); + REQUIRE_PRECOMPUTED_GUID("5AB7D2C3-6B62-5E71-A4B6-2D49C4F238FD", IReference); + REQUIRE_PRECOMPUTED_GUID("548CEFBD-BC8A-5FA0-8DF2-957440FC8BF4", IReference); + REQUIRE_PRECOMPUTED_GUID("513EF3AF-E784-5325-A91E-97C2B8111CF3", IReference); + REQUIRE_PRECOMPUTED_GUID("4DDA9E24-E69F-5C6A-A0A6-93427365AF2A", IReference); + REQUIRE_PRECOMPUTED_GUID("6755E376-53BB-568B-A11D-17239868309E", IReference); + REQUIRE_PRECOMPUTED_GUID("719CC2BA-3E76-5DEF-9F1A-38D85A145EA8", IReference); + REQUIRE_PRECOMPUTED_GUID("2F2D6C29-5473-5F3E-92E7-96572BB990E2", IReference); + REQUIRE_PRECOMPUTED_GUID("FB393EF3-BBAC-5BD5-9144-84F23576F415", IReference); + REQUIRE_PRECOMPUTED_GUID("3C00FD60-2950-5939-A21A-2D12C5A01B8A", IReference); + REQUIRE_PRECOMPUTED_GUID("FD416DFB-2A07-52EB-AAE3-DFCE14116C05", IReference); + REQUIRE_PRECOMPUTED_GUID("7D50F649-632C-51F9-849A-EE49428933EA", IReference); + REQUIRE_PRECOMPUTED_GUID("5541D8A7-497C-5AA4-86FC-7713ADBF2A2C", IReference); + REQUIRE_PRECOMPUTED_GUID("604D0C4C-91DE-5C2A-935F-362F13EAF800", IReference); + REQUIRE_PRECOMPUTED_GUID("84F14C22-A00A-5272-8D3D-82112E66DF00", IReference); + REQUIRE_PRECOMPUTED_GUID("61723086-8E53-5276-9F36-2A4BB93E2B75", IReference); + REQUIRE_PRECOMPUTED_GUID("80423F11-054F-5EAC-AFD3-63B6CE15E77B", IReference); +} + +TEST_CASE("module_pinterface_guid_ireferencearray") +{ + REQUIRE_PRECOMPUTED_GUID("2AF22683-3734-56D0-A60E-688CC85D1619", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("912F8FD7-ADC0-5D60-A896-7ED76089CC5B", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("6624A2DD-83F7-519C-9D55-BB1F6560456B", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("A6D080A5-B087-5BC2-9A9F-5CD687B4D1F7", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("97374B68-EB87-56CC-B18E-27EF0F9CFC0C", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("6E333271-2E2A-5955-8790-836C76EE53B6", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("38B60434-D67C-523E-9D0E-24D643411073", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("6AB1EA83-CB41-5F99-92CC-23BD4336A1FB", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("D301F253-E0A3-5D2B-9A41-A4D62BEC4623", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("A4095AAB-EB7D-5782-8FAD-1609DEA249AD", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("E8E72666-48CC-593F-BA85-2663496956E3", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("0385688E-E3C7-5C5E-A389-5524EDE349F1", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("9CD7A84F-0C80-59C5-B44E-977841BB43D9", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("EECF9838-C1C2-5B4A-976F-CEC261AE1D55", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("1B8E9594-588E-5A07-9E65-0731A4C9A2DB", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("AD73197D-2CFA-57A6-8993-9FAC40FEB791", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("39313214-5C7D-599D-AE5A-17D9D6492258", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("3B40E9D4-E0C3-56F6-B88B-E505EB73757B", IReferenceArray); + REQUIRE_PRECOMPUTED_GUID("8A444256-D661-5E9A-A72B-D8F1D7962D0C", IReferenceArray); +} + +TEST_CASE("module_pinterface_guid_from_metadata") +{ + // IMap / IMapView chains (used by PropertySet, ValueSet, etc.) + REQUIRE_PRECOMPUTED_GUID("1B0D3570-0877-5EC2-8A2C-3B9539506ACA", IMap); + REQUIRE_PRECOMPUTED_GUID("F6D1F700-49C2-52AE-8154-826F9908773C", IMap); + REQUIRE_PRECOMPUTED_GUID("BB78502A-F79D-54FA-92C9-90C5039FDF7E", IMapView); + REQUIRE_PRECOMPUTED_GUID("AC7F26F2-FEB7-5B2A-8AC4-345BC62CAEDE", IMapView); + REQUIRE_PRECOMPUTED_GUID("09335560-6C6B-5A26-9348-97B781132B20", IKeyValuePair); + REQUIRE_PRECOMPUTED_GUID("60310303-49C5-52E6-ABC6-A9B36ECCC716", IKeyValuePair); + + // IIterable / IIterator over key-value pairs + REQUIRE_PRECOMPUTED_GUID("FE2F3D47-5D47-5499-8374-430C7CDA0204", IIterable>); + REQUIRE_PRECOMPUTED_GUID("E9BDAAF0-CBF6-5C72-BE90-29CBF3A1319B", IIterable>); + REQUIRE_PRECOMPUTED_GUID("5DB5FA32-707C-5849-A06B-91C8EB9D10E8", IIterator>); + REQUIRE_PRECOMPUTED_GUID("05EB86F1-7140-5517-B88D-CBAEBE57E6B1", IIterator>); + + // IObservableMap and change handlers + REQUIRE_PRECOMPUTED_GUID("236AAC9D-FB12-5C4D-A41C-9E445FB4D7EC", IObservableMap); + REQUIRE_PRECOMPUTED_GUID("1E036276-2F60-55F6-B7F3-F86079E6900B", IObservableMap); + REQUIRE_PRECOMPUTED_GUID("60141EFB-F2F9-5377-96FD-F8C60D9558B5", IMapChangedEventArgs); + REQUIRE_PRECOMPUTED_GUID("24F981E5-DDCA-538D-AADA-A59906084CF1", MapChangedEventHandler); + REQUIRE_PRECOMPUTED_GUID("E2663F37-2E1B-500C-AD68-C3ED7A8F74C8", MapChangedEventHandler); + + // TypedEventHandler from Windows.Foundation (e.g. IMemoryBufferReference.Closed) + REQUIRE_PRECOMPUTED_GUID("F4637D4A-0760-5431-BFC0-24EB1D4F6C4F", TypedEventHandler); + + // WwwFormUrlDecoder collections + REQUIRE_PRECOMPUTED_GUID("876BE83B-7218-5BFB-A169-83152EF7E146", IIterable); + REQUIRE_PRECOMPUTED_GUID("32E54295-373C-50CB-80A1-468A990CA780", IIterator); + REQUIRE_PRECOMPUTED_GUID("B1F00D3B-1F06-5117-93EA-2A0D79116701", IVectorView); +} + +#undef REQUIRE_PRECOMPUTED_GUID diff --git a/test/test_cpp20_module/test_cpp20_module.vcxproj b/test/test_cpp20_module/test_cpp20_module.vcxproj index 1e76c487b..cf29456cc 100644 --- a/test/test_cpp20_module/test_cpp20_module.vcxproj +++ b/test/test_cpp20_module/test_cpp20_module.vcxproj @@ -107,6 +107,7 @@ + \ No newline at end of file