Skip to content

Commit d056690

Browse files
authored
Improve diagnostics when trying to use classic COM without <unknwn.h> (#1022)
1 parent a903a2c commit d056690

File tree

8 files changed

+50
-61
lines changed

8 files changed

+50
-61
lines changed

strings/base_implements.h

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,8 @@ namespace winrt::impl
4444
template <template <typename> typename Condition, typename T>
4545
using tuple_if = typename tuple_if_base<Condition, T>::type;
4646

47-
#ifdef WINRT_IMPL_IUNKNOWN_DEFINED
48-
4947
template <typename T>
50-
struct is_interface : std::disjunction<std::is_base_of<Windows::Foundation::IInspectable, T>, std::conjunction<std::is_base_of<::IUnknown, T>, std::negation<is_implements<T>>>> {};
51-
52-
#else
53-
54-
template <typename T>
55-
struct is_interface : std::is_base_of<Windows::Foundation::IInspectable, T> {};
56-
57-
#endif
48+
struct is_interface : std::disjunction<std::is_base_of<Windows::Foundation::IInspectable, T>, is_classic_com_interface<T>> {};
5849

5950
template <typename T>
6051
struct is_marker : std::disjunction<std::is_base_of<marker, T>, std::is_void<T>> {};
@@ -485,20 +476,19 @@ namespace winrt::impl
485476
}
486477
};
487478

488-
#ifdef WINRT_IMPL_IUNKNOWN_DEFINED
489-
490479
template <typename D, typename I>
491-
struct producer<D, I, std::enable_if_t<std::is_base_of_v< ::IUnknown, I> && !is_implements_v<I>>> : I
480+
struct producer<D, I, std::enable_if_t<is_classic_com_interface<I>::value>> : I
492481
{
482+
#ifndef WINRT_IMPL_IUNKNOWN_DEFINED
483+
static_assert(std::is_void_v<I> /* dependent_false */, "To implement classic COM interfaces, you must #include <unknwn.h> before including C++/WinRT headers.");
484+
#endif
493485
};
494486

495487
template <typename D, typename I>
496-
struct producer_convert<D, I, std::enable_if_t<std::is_base_of_v< ::IUnknown, I> && !is_implements_v<I>>> : producer<D, I>
488+
struct producer_convert<D, I, std::enable_if_t<is_classic_com_interface<I>::value>> : producer<D, I>
497489
{
498490
};
499491

500-
#endif
501-
502492
struct INonDelegatingInspectable : Windows::Foundation::IUnknown
503493
{
504494
INonDelegatingInspectable(std::nullptr_t = nullptr) noexcept {}

strings/base_macros.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,8 @@
5555

5656
#ifdef __IUnknown_INTERFACE_DEFINED__
5757
#define WINRT_IMPL_IUNKNOWN_DEFINED
58+
#else
59+
// Forward declare so we can talk about it.
60+
struct IUnknown;
61+
typedef struct _GUID GUID;
5862
#endif

strings/base_meta.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,14 @@ namespace winrt::impl
118118
};
119119

120120
template <typename T>
121-
#ifdef WINRT_IMPL_IUNKNOWN_DEFINED
122-
#ifdef __clang__
121+
#if defined(__clang__)
122+
#if __has_declspec_attribute(uuid)
123123
inline const guid guid_v{ __uuidof(T) };
124124
#else
125-
inline constexpr guid guid_v{ __uuidof(T) };
125+
inline constexpr guid guid_v{};
126126
#endif
127+
#elif defined(_MSC_VER)
128+
inline constexpr guid guid_v{ __uuidof(T) };
127129
#else
128130
inline constexpr guid guid_v{};
129131
#endif

strings/base_reference_produce.h

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -212,14 +212,12 @@ namespace winrt::impl
212212
using itf = Windows::Foundation::IReference<guid>;
213213
};
214214

215-
#ifdef WINRT_IMPL_IUNKNOWN_DEFINED
216215
template <>
217216
struct reference_traits<GUID>
218217
{
219-
static auto make(GUID const& value) { return Windows::Foundation::PropertyValue::CreateGuid(value); }
218+
static auto make(GUID const& value) { return Windows::Foundation::PropertyValue::CreateGuid(reinterpret_cast<guid const&>(value)); }
220219
using itf = Windows::Foundation::IReference<guid>;
221220
};
222-
#endif
223221

224222
template <>
225223
struct reference_traits<Windows::Foundation::DateTime>
@@ -354,14 +352,12 @@ namespace winrt::impl
354352
using itf = Windows::Foundation::IReferenceArray<guid>;
355353
};
356354

357-
#ifdef WINRT_IMPL_IUNKNOWN_DEFINED
358355
template <>
359356
struct reference_traits<com_array<GUID>>
360357
{
361358
static auto make(array_view<GUID const> const& value) { return Windows::Foundation::PropertyValue::CreateGuidArray(reinterpret_cast<array_view<guid const> const&>(value)); }
362359
using itf = Windows::Foundation::IReferenceArray<guid>;
363360
};
364-
#endif
365361

366362
template <>
367363
struct reference_traits<com_array<Windows::Foundation::DateTime>>
@@ -444,14 +440,12 @@ namespace winrt::impl
444440
return static_cast<T>(value.template as<Windows::Foundation::IReference<std::underlying_type_t<T>>>().Value());
445441
}
446442
}
447-
#ifdef WINRT_IMPL_IUNKNOWN_DEFINED
448443
else if constexpr (std::is_same_v<T, com_array<GUID>>)
449444
{
450445
T result;
451446
reinterpret_cast<com_array<guid>&>(result) = value.template as<typename impl::reference_traits<T>::itf>().Value();
452447
return result;
453448
}
454-
#endif
455449
else
456450
{
457451
return value.template as<typename impl::reference_traits<T>::itf>().Value();
@@ -473,7 +467,6 @@ namespace winrt::impl
473467
return static_cast<T>(temp.Value());
474468
}
475469
}
476-
#ifdef WINRT_IMPL_IUNKNOWN_DEFINED
477470
else if constexpr (std::is_same_v<T, com_array<GUID>>)
478471
{
479472
if (auto temp = value.template try_as<typename impl::reference_traits<T>::itf>())
@@ -483,7 +476,6 @@ namespace winrt::impl
483476
return result;
484477
}
485478
}
486-
#endif
487479
else
488480
{
489481
if (auto temp = value.template try_as<typename impl::reference_traits<T>::itf>())

strings/base_types.h

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -141,24 +141,14 @@ WINRT_EXPORT namespace winrt
141141
{
142142
}
143143

144-
#ifdef WINRT_IMPL_IUNKNOWN_DEFINED
145-
146-
constexpr guid(GUID const& value) noexcept :
147-
Data1(value.Data1),
148-
Data2(value.Data2),
149-
Data3(value.Data3),
150-
Data4{ value.Data4[0], value.Data4[1], value.Data4[2], value.Data4[3], value.Data4[4], value.Data4[5], value.Data4[6], value.Data4[7] }
151-
{
152-
153-
}
144+
template<bool dummy = true>
145+
constexpr guid(GUID const& value) noexcept : guid(convert<dummy>(value)) { }
154146

155147
operator GUID const&() const noexcept
156148
{
157149
return reinterpret_cast<GUID const&>(*this);
158150
}
159151

160-
#endif
161-
162152
constexpr explicit guid(std::string_view const value) :
163153
guid(parse(value))
164154
{
@@ -168,6 +158,15 @@ WINRT_EXPORT namespace winrt
168158
guid(parse(value))
169159
{
170160
}
161+
162+
private:
163+
template<bool, typename T>
164+
constexpr static guid convert(T const& value) noexcept
165+
{
166+
return { value.Data1, value.Data2, value.Data3,
167+
{ value.Data4[0], value.Data4[1], value.Data4[2], value.Data4[3], value.Data4[4], value.Data4[5], value.Data4[6], value.Data4[7] }
168+
};
169+
}
171170
};
172171

173172
inline bool operator==(guid const& left, guid const& right) noexcept

strings/base_windows.h

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,11 @@ namespace winrt::impl
8181
return { result, take_ownership_from_abi };
8282
}
8383

84-
#ifdef WINRT_IMPL_IUNKNOWN_DEFINED
85-
template <typename T>
86-
struct is_com_interface : std::disjunction<std::is_base_of<Windows::Foundation::IUnknown, T>, std::is_base_of<unknown_abi, T>, is_implements<T>, std::is_base_of<::IUnknown, T>> {};
87-
#else
84+
template<typename T>
85+
struct is_classic_com_interface : std::conjunction<std::is_base_of<::IUnknown, T>, std::negation<is_implements<T>>> {};
86+
8887
template <typename T>
89-
struct is_com_interface : std::disjunction<std::is_base_of<Windows::Foundation::IUnknown, T>, std::is_base_of<unknown_abi, T>, is_implements<T>> {};
90-
#endif
88+
struct is_com_interface : std::disjunction<std::is_base_of<Windows::Foundation::IUnknown, T>, std::is_base_of<unknown_abi, T>, is_implements<T>, is_classic_com_interface<T>> {};
9189

9290
template <typename T>
9391
inline constexpr bool is_com_interface_v = is_com_interface<T>::value;
@@ -363,14 +361,10 @@ WINRT_EXPORT namespace winrt
363361
}
364362
}
365363

366-
#ifdef WINRT_IMPL_IUNKNOWN_DEFINED
367-
368364
inline ::IUnknown* get_unknown(Windows::Foundation::IUnknown const& object) noexcept
369365
{
370366
return static_cast<::IUnknown*>(get_abi(object));
371367
}
372-
373-
#endif
374368
}
375369

376370
WINRT_EXPORT namespace winrt::Windows::Foundation

test/test/guid.cpp

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
#include "pch.h"
22

3+
#define STATIC_REQUIRE_GUID_EQUAL(left, right) \
4+
STATIC_REQUIRE(left.Data1 == right.Data1); \
5+
STATIC_REQUIRE(left.Data2 == right.Data2); \
6+
STATIC_REQUIRE(left.Data3 == right.Data3); \
7+
STATIC_REQUIRE(left.Data4[0] == right.Data4[0]); \
8+
STATIC_REQUIRE(left.Data4[1] == right.Data4[1]); \
9+
STATIC_REQUIRE(left.Data4[2] == right.Data4[2]); \
10+
STATIC_REQUIRE(left.Data4[3] == right.Data4[3]); \
11+
STATIC_REQUIRE(left.Data4[4] == right.Data4[4]); \
12+
STATIC_REQUIRE(left.Data4[5] == right.Data4[5]); \
13+
STATIC_REQUIRE(left.Data4[6] == right.Data4[6]); \
14+
STATIC_REQUIRE(left.Data4[7] == right.Data4[7]);
15+
316
TEST_CASE("guid")
417
{
518
constexpr winrt::guid expected{ 0x00112233, 0x4455, 0x6677, { 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff } };
619

7-
STATIC_REQUIRE(winrt::guid("00112233-4455-6677-8899-aabbccddeeff").Data1 == expected.Data1);
8-
STATIC_REQUIRE(winrt::guid("00112233-4455-6677-8899-aabbccddeeff").Data2 == expected.Data2);
9-
STATIC_REQUIRE(winrt::guid("00112233-4455-6677-8899-aabbccddeeff").Data3 == expected.Data3);
10-
STATIC_REQUIRE(winrt::guid("00112233-4455-6677-8899-aabbccddeeff").Data4[0] == expected.Data4[0]);
11-
STATIC_REQUIRE(winrt::guid("00112233-4455-6677-8899-aabbccddeeff").Data4[1] == expected.Data4[1]);
12-
STATIC_REQUIRE(winrt::guid("00112233-4455-6677-8899-aabbccddeeff").Data4[2] == expected.Data4[2]);
13-
STATIC_REQUIRE(winrt::guid("00112233-4455-6677-8899-aabbccddeeff").Data4[3] == expected.Data4[3]);
14-
STATIC_REQUIRE(winrt::guid("00112233-4455-6677-8899-aabbccddeeff").Data4[4] == expected.Data4[4]);
15-
STATIC_REQUIRE(winrt::guid("00112233-4455-6677-8899-aabbccddeeff").Data4[5] == expected.Data4[5]);
16-
STATIC_REQUIRE(winrt::guid("00112233-4455-6677-8899-aabbccddeeff").Data4[6] == expected.Data4[6]);
17-
STATIC_REQUIRE(winrt::guid("00112233-4455-6677-8899-aabbccddeeff").Data4[7] == expected.Data4[7]);
20+
STATIC_REQUIRE_GUID_EQUAL(winrt::guid("00112233-4455-6677-8899-aabbccddeeff"), expected);
1821

1922
REQUIRE(winrt::guid("00112233-4455-6677-8899-aabbccddeeff") == expected);
2023
REQUIRE(winrt::guid({ "{00112233-4455-6677-8899-aabbccddeeff}" + 1, 36 }) == expected);
@@ -26,4 +29,8 @@ TEST_CASE("guid")
2629
REQUIRE_THROWS_AS(winrt::guid("00112233-4455-6677-8899-aabbccddeeff with extra"), std::invalid_argument);
2730
REQUIRE_THROWS_AS(winrt::guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"), std::invalid_argument);
2831
REQUIRE_THROWS_AS(winrt::guid("{00112233-4455-6677-8899-aabbccddeeff}"), std::invalid_argument);
32+
33+
// Verify that you can constexpr-construct a guid from a GUID.
34+
constexpr winrt::guid from_abi_guid = GUID{ 0x00112233, 0x4455, 0x6677, { 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff } };
35+
STATIC_REQUIRE_GUID_EQUAL(from_abi_guid, expected);
2936
}

test/test_component_derived/pch.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
#pragma once
2+
#include <unknwn.h> // Nested.HierarchyD uses classic COM interface IReferenceTrackerExtension

0 commit comments

Comments
 (0)