Skip to content

Commit 8da8359

Browse files
authored
try_as casts should not store COM error context; consume method cast checking should use return codes directly (#1450)
Why is this change being made? I have been trying to ingest the HEAD of cppwinrt for some large internal projects and one of them had some test failures with the new version. The failures are because there is a try_as cast in their code that fails and is handled fine, but it leaves a COM error context floating around on that thread. Subsequent code fails to originate an error because it sees a context already active on the thread and NOOP'ed. What this boils down to is that try_as should not have a behavioral change to store context when the cast fails. Briefly summarize what changed To address this problem I am taking a PR suggestion from @oldnewthing to have a new try_as_with_reason method that returns both the cast result as well as the HRESULT. The error context logic was only there to smuggle the HRESULT out of a call without an HRESULT return value, so if it is a direct return we don't need that anymore. The new method directly returns the HRESULT so there is no ambiguity. The previous approach relied on a cast operator to call try_as (or just addref when the type is already a match). Thanks to the recent if constexpr code gen change those cases are now separated out. We have a code block where we know a cast is needed so try_as_with_reason can be called unconditionally. The code path where no cast is needed already circumvents this and is nicely unaffected. This also made check_cast_result equiavelnt to check_hresult so it was deleted in favor of just calling check_hresult. How was this change tested? I did local builds of cppwinrt (both Debug and Release) and ran the tests. I am also compiling some large internal projects with it to ensure that there are no obvious breaks or regressions. I am expecting minimal to no binary size impact from this change.
1 parent d296af6 commit 8da8359

File tree

5 files changed

+47
-34
lines changed

5 files changed

+47
-34
lines changed

cppwinrt/code_writers.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,16 +1130,16 @@ namespace cppwinrt
11301130
{
11311131
// we intentionally ignore errors when unregistering event handlers to be consistent with event_revoker
11321132
//
1133-
// The `noexcept` versions will crash if check_cast_result throws but that is no different than previous
1133+
// The `noexcept` versions will crash if check_hresult throws but that is no different than previous
11341134
// behavior where it would not check the cast result and nullptr crash. At least the exception will terminate
11351135
// immediately while preserving the error code and local variables.
11361136
format = R"( template <typename D%> auto consume_%<D%>::%(%) const noexcept
11371137
{%
11381138
if constexpr (!std::is_same_v<D, %>)
11391139
{
1140-
auto const& castedResult = static_cast<% const&>(static_cast<D const&>(*this));
1140+
auto const [castedResult, code] = impl::try_as_with_reason<%, D const*>(static_cast<D const*>(this));
1141+
check_hresult(code);
11411142
auto const abiType = *(abi_t<%>**)&castedResult;
1142-
check_cast_result(abiType);
11431143
abiType->%(%);
11441144
}
11451145
else
@@ -1156,9 +1156,9 @@ namespace cppwinrt
11561156
{%
11571157
if constexpr (!std::is_same_v<D, %>)
11581158
{
1159-
auto const& castedResult = static_cast<% const&>(static_cast<D const&>(*this));
1159+
auto const [castedResult, code] = impl::try_as_with_reason<%, D const*>(static_cast<D const*>(this));
1160+
check_hresult(code);
11601161
auto const abiType = *(abi_t<%>**)&castedResult;
1161-
check_cast_result(abiType);
11621162
WINRT_VERIFY_(0, abiType->%(%));
11631163
}
11641164
else
@@ -1176,9 +1176,9 @@ namespace cppwinrt
11761176
{%
11771177
if constexpr (!std::is_same_v<D, %>)
11781178
{
1179-
auto const& castedResult = static_cast<% const&>(static_cast<D const&>(*this));
1179+
auto const [castedResult, code] = impl::try_as_with_reason<%, D const*>(static_cast<D const*>(this));
1180+
check_hresult(code);
11801181
auto const abiType = *(abi_t<%>**)&castedResult;
1181-
check_cast_result(abiType);
11821182
check_hresult(abiType->%(%));
11831183
}
11841184
else

strings/base_error.h

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -538,27 +538,6 @@ namespace winrt::impl
538538
}
539539
return result;
540540
}
541-
542-
inline WINRT_IMPL_NOINLINE void check_cast_result(void* from, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current())
543-
{
544-
if (!from)
545-
{
546-
com_ptr<impl::IRestrictedErrorInfo> restrictedError;
547-
if (WINRT_IMPL_GetRestrictedErrorInfo(restrictedError.put_void()) == 0)
548-
{
549-
WINRT_IMPL_SetRestrictedErrorInfo(restrictedError.get());
550-
551-
int32_t code;
552-
impl::bstr_handle description;
553-
impl::bstr_handle restrictedDescription;
554-
impl::bstr_handle capabilitySid;
555-
if (restrictedError->GetErrorDetails(description.put(), &code, restrictedDescription.put(), capabilitySid.put()) == 0)
556-
{
557-
throw hresult_error(code, take_ownership_from_abi, sourceInformation);
558-
}
559-
}
560-
}
561-
}
562541
}
563542

564543
#undef WINRT_IMPL_RETURNADDRESS

strings/base_extern.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ extern "C"
3232
int32_t __stdcall WINRT_IMPL_RoCaptureErrorContext(int32_t error) noexcept WINRT_IMPL_LINK(RoCaptureErrorContext, 4);
3333
void __stdcall WINRT_IMPL_RoFailFastWithErrorContext(int32_t) noexcept WINRT_IMPL_LINK(RoFailFastWithErrorContext, 4);
3434
int32_t __stdcall WINRT_IMPL_RoTransformError(int32_t, int32_t, void*) noexcept WINRT_IMPL_LINK(RoTransformError, 12);
35-
int32_t __stdcall WINRT_IMPL_GetRestrictedErrorInfo(void**) noexcept WINRT_IMPL_LINK(GetRestrictedErrorInfo, 4);
36-
int32_t __stdcall WINRT_IMPL_SetRestrictedErrorInfo(void*) noexcept WINRT_IMPL_LINK(SetRestrictedErrorInfo, 4);
3735

3836
void* __stdcall WINRT_IMPL_LoadLibraryExW(wchar_t const* name, void* unused, uint32_t flags) noexcept WINRT_IMPL_LINK(LoadLibraryExW, 12);
3937
int32_t __stdcall WINRT_IMPL_FreeLibrary(void* library) noexcept WINRT_IMPL_LINK(FreeLibrary, 4);

strings/base_implements.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,9 +792,20 @@ namespace winrt::impl
792792
{
793793
return m_inner.operator bool();
794794
}
795+
796+
template <typename To, typename From>
797+
friend auto winrt::impl::try_as_with_reason(From ptr) noexcept;
798+
795799
protected:
796800
static constexpr bool is_composing = true;
797801
Windows::Foundation::IInspectable m_inner;
802+
803+
private:
804+
template <typename Qi>
805+
auto try_as_with_reason() const noexcept
806+
{
807+
return m_inner.try_as_with_reason<Qi>();
808+
}
798809
};
799810

800811
template <typename D, bool>

strings/base_windows.h

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,31 @@ namespace winrt::impl
128128
}
129129

130130
void* result{};
131-
hresult code = ptr->QueryInterface(guid_of<To>(), &result);
132-
if (code < 0)
131+
ptr->QueryInterface(guid_of<To>(), &result);
132+
return wrap_as_result<To>(result);
133+
}
134+
135+
template <typename To, typename From, std::enable_if_t<is_com_interface_v<To>, int> = 0>
136+
std::pair<com_ref<To>, hresult> try_as_with_reason(From* ptr) noexcept
137+
{
138+
#ifdef WINRT_DIAGNOSTICS
139+
get_diagnostics_info().add_query<To>();
140+
#endif
141+
142+
if (!ptr)
133143
{
134-
WINRT_IMPL_RoCaptureErrorContext(code);
144+
return { nullptr, 0 };
135145
}
136-
return wrap_as_result<To>(result);
146+
147+
void* result{};
148+
hresult code = ptr->QueryInterface(guid_of<To>(), &result);
149+
return { wrap_as_result<To>(result), code };
150+
}
151+
152+
template <typename To, typename From>
153+
auto try_as_with_reason(From ptr) noexcept
154+
{
155+
return ptr->template try_as_with_reason<To>();
137156
}
138157
}
139158

@@ -209,6 +228,12 @@ WINRT_EXPORT namespace winrt::Windows::Foundation
209228
return impl::try_as<To>(m_ptr);
210229
}
211230

231+
template <typename To>
232+
auto try_as_with_reason() const noexcept
233+
{
234+
return impl::try_as_with_reason<To>(m_ptr);
235+
}
236+
212237
template <typename To>
213238
void as(To& to) const
214239
{

0 commit comments

Comments
 (0)