From 4484d4b0ed231b0fb0c9647c61c06aa98292ff9e Mon Sep 17 00:00:00 2001 From: Duncan Horn Date: Wed, 22 Jul 2026 16:35:19 -0700 Subject: [PATCH 1/3] Using robots to fix/update docs part 6: cppwinrt* headers --- include/wil/cppwinrt.h | 99 ++++++++++-- include/wil/cppwinrt_authoring.h | 149 ++++++++++++++++-- include/wil/cppwinrt_helpers.h | 53 ++++--- include/wil/cppwinrt_notifiable_server_lock.h | 41 +++++ include/wil/cppwinrt_wrl.h | 35 +++- 5 files changed, 332 insertions(+), 45 deletions(-) diff --git a/include/wil/cppwinrt.h b/include/wil/cppwinrt.h index d2b8521ae..0a3895855 100644 --- a/include/wil/cppwinrt.h +++ b/include/wil/cppwinrt.h @@ -224,6 +224,7 @@ inline HRESULT __stdcall ResultFromCaughtException_CppWinRt( namespace wil { +/// @cond inline std::int32_t __stdcall winrt_to_hresult(void* returnAddress) noexcept { // C++/WinRT only gives us the return address (caller), so pass along an empty 'DiagnosticsInfo' since we don't @@ -255,6 +256,7 @@ inline void WilInitialize_CppWinRT() } } } +/// @endcond /// @cond namespace details @@ -271,52 +273,97 @@ namespace details } // namespace details /// @endcond -// Provides an overload of verify_hresult so that the WIL macros can recognize winrt::hresult as a valid "hresult" type. +//! Lets WIL's error-handling macros accept a `winrt::hresult` where they expect an `HRESULT`. +//! This overload is found by the `RETURN_IF_FAILED` / `THROW_IF_FAILED` family (and friends) so a `winrt::hresult` result can be +//! checked directly, without first converting it to an `HRESULT`. +//! @param hr The `winrt::hresult` to validate. +//! @return `hr`, as an `HRESULT`-compatible value. inline long verify_hresult(winrt::hresult hr) noexcept { return hr; } -// Provides versions of get_abi and put_abi for genericity that directly use HSTRING for convenience. +//! Returns the raw ABI representation of a C++/WinRT projected object, for passing across the ABI boundary in generic code. +//! This overload set exists mainly because `winrt::get_abi` no longer references any Windows SDK types: for a `winrt::hstring` it +//! returns a bare `void*` rather than an `HSTRING`, so WIL provides these as an alternative that at least knows about `HSTRING` +//! (the `winrt::hstring` overload returns the concrete `HSTRING`). For every other type it simply forwards to `winrt::get_abi`. +//! For a projected interface or runtime class `winrt::impl::abi_t` resolves to a `void*` ABI interface pointer; for a +//! projected value type it is instead the `winrt::impl::abi_t` ABI struct, by value (layout-compatible with, but distinct +//! from, the classic/SDK `ABI::` type). Paired with @ref put_abi. +//! @tparam T A C++/WinRT projected type. +//! @param object The object whose ABI representation to return. +//! @return For an interface or runtime class, a borrowed (not AddRef'd) `void*` interface pointer; for a value type, the +//! `winrt::impl::abi_t` ABI struct by value. template auto get_abi(T const& object) noexcept { return winrt::get_abi(object); } +//! Returns the raw `HSTRING` that backs a `winrt::hstring`. +//! @param object The string whose `HSTRING` to return. +//! @return The `HSTRING` backing `object` (may be null for an empty string). inline auto get_abi(winrt::hstring const& object) noexcept { return static_cast(winrt::get_abi(object)); } +//! Returns a null-terminated wide-character pointer to a `winrt::hstring`'s buffer. +//! This is the `winrt::hstring` overload of WIL's generic `str_raw_ptr` set, which normalizes the string types WIL supports (raw +//! `PCWSTR`, `std::wstring`, `HSTRING`, and so on) to a `PCWSTR` for use by WIL's generic string helpers. +//! @param str The string to read from. +//! @return A `PCWSTR` to `str`'s contents, valid for as long as `str` is alive and unmodified. inline auto str_raw_ptr(const winrt::hstring& str) noexcept { return str.c_str(); } +//! Returns the address of a C++/WinRT object's ABI pointer, for receiving it as an out-parameter in generic code. +//! Like @ref get_abi, this exists mainly because `winrt::put_abi` no longer references any Windows SDK types: for a +//! `winrt::hstring` it yields a bare `void**` rather than an `HSTRING*`, so the `winrt::hstring` overload here yields the +//! concrete `HSTRING*`. For every other type it forwards to `winrt::put_abi`. For a projected interface or runtime class the +//! result is `void**` (the address of the object's `void*` ABI interface pointer), and the held interface is released first, so +//! the object need not already be empty; for a projected value type it is `winrt::impl::abi_t*`. Paired with @ref get_abi. +//! @tparam T A C++/WinRT projected type. +//! @param object The object that receives the ABI value; for an interface or runtime class, any held interface is released first. +//! @return For an interface or runtime class, a `void**`; for a value type, a `winrt::impl::abi_t*`. template auto put_abi(T& object) noexcept { return winrt::put_abi(object); } +//! Returns the address of a `winrt::hstring`'s backing `HSTRING`, as an `HSTRING*`. +//! @param object The (empty) string that receives the `HSTRING`. +//! @return The address of `object`'s backing `HSTRING`, suitable as an out-parameter. inline auto put_abi(winrt::hstring& object) noexcept { return reinterpret_cast(winrt::put_abi(object)); } +//! Returns the raw `IUnknown` ABI pointer for a C++/WinRT object, without taking a reference. +//! @param ptr The object whose ABI pointer to return. +//! @return The `IUnknown*` backing `ptr` (borrowed, not AddRef'd; may be null). inline ::IUnknown* com_raw_ptr(const winrt::Windows::Foundation::IUnknown& ptr) noexcept { return static_cast<::IUnknown*>(winrt::get_abi(ptr)); } -// Needed to power wil::cx_object_from_abi that requires IInspectable +//! Returns the raw `IInspectable` ABI pointer for a C++/WinRT object, without taking a reference. +//! This overload supplies the `IInspectable` that `wil::cx_object_from_abi` requires. +//! @param ptr The object whose ABI pointer to return. +//! @return The `IInspectable*` backing `ptr` (borrowed, not AddRef'd; may be null). inline ::IInspectable* com_raw_ptr(const winrt::Windows::Foundation::IInspectable& ptr) noexcept { return static_cast<::IInspectable*>(winrt::get_abi(ptr)); } -// Taken from the docs.microsoft.com article +//! Constructs a C++/WinRT projected object from a raw ABI interface pointer by querying for the projected type. +//! Calls `QueryInterface` for `T`'s default interface and returns the resulting projection. +//! @tparam T The C++/WinRT projected type to construct. +//! @param from The interface pointer to convert from; must not be null. +//! @return A `T` referring to the same object as `from`. +//! @note Throws a `winrt::hresult_error` if the `QueryInterface` call fails. template T convert_from_abi(::IUnknown* from) { @@ -325,10 +372,23 @@ T convert_from_abi(::IUnknown* from) return result; } -// For obtaining an object from an interop method on the factory. Example: -// winrt::InputPane inputPane = wil::capture_interop(&IInputPaneInterop::GetForWindow, hwnd); -// If the method produces something different from the factory type: -// winrt::IAsyncAction action = wil::capture_interop(&IAccountsSettingsPaneInterop::ShowAddAccountForWindow, hwnd); +/** Calls an interop method on a C++/WinRT type's activation factory and returns the object it produces. +Retrieves the activation factory for `WinRTFactory`, queries it for `Interface`, invokes `method` with `args`, and wraps the +result as a `WinRTResult`. Use this with factory interop interfaces such as `IInputPaneInterop`. +~~~ +// Produce an object of the factory's own type: +winrt::InputPane inputPane = wil::capture_interop(&IInputPaneInterop::GetForWindow, hwnd); + +// Produce a different type from the one whose factory exposes the interop method: +winrt::IAsyncAction action = wil::capture_interop( + &IAccountsSettingsPaneInterop::ShowAddAccountForWindow, hwnd); +~~~ +@tparam WinRTResult The C++/WinRT type to return. +@tparam WinRTFactory The C++/WinRT type whose activation factory exposes `method`; defaults to `WinRTResult`. +@param method Pointer to the interop interface method to call. +@param args Arguments to forward to `method`, ahead of the capture out-parameters it fills in. +@return The `WinRTResult` produced by `method`. +@note Throws a `winrt::hresult_error` if the factory lookup or the interop call fails. */ template auto capture_interop(HRESULT (__stdcall Interface::*method)(InterfaceArgs...), Args&&... args) { @@ -336,8 +396,19 @@ auto capture_interop(HRESULT (__stdcall Interface::*method)(InterfaceArgs...), A return winrt::capture(interop, method, std::forward(args)...); } -// For obtaining an object from an interop method on an instance. Example: -// winrt::UserActivitySession session = wil::capture_interop(activity, &IUserActivityInterop::CreateSessionForWindow, hwnd); +/** Calls an interop method on an existing C++/WinRT object and returns the object it produces. +Queries `obj` for `Interface`, invokes `method` with `args`, and wraps the result as a `WinRTResult`. Use this with per-instance +interop interfaces such as `IUserActivityInterop`. +~~~ +winrt::UserActivitySession session = wil::capture_interop( + activity, &IUserActivityInterop::CreateSessionForWindow, hwnd); +~~~ +@tparam WinRTResult The C++/WinRT type to return. +@param obj The object to query for `Interface` and call the interop method on. +@param method Pointer to the interop interface method to call. +@param args Arguments to forward to `method`, ahead of the capture out-parameters it fills in. +@return The `WinRTResult` produced by `method`. +@note Throws a `winrt::hresult_error` if the query or the interop call fails. */ template auto capture_interop(winrt::Windows::Foundation::IUnknown const& obj, HRESULT (__stdcall Interface::*method)(InterfaceArgs...), Args&&... args) { @@ -403,15 +474,18 @@ winrt::fire_and_forget ContinueBackgroundWork() */ struct [[nodiscard]] winrt_module_reference { + //! Takes a reference on the host C++/WinRT module, incrementing its lock count. winrt_module_reference() { ++winrt::get_module_lock(); } + //! Takes an additional reference on the host module; every live copy holds its own lock. winrt_module_reference(winrt_module_reference const&) : winrt_module_reference() { } + //! Releases this reference, decrementing the host module's lock count. ~winrt_module_reference() { --winrt::get_module_lock(); @@ -462,6 +536,11 @@ struct winrt_conditionally_implements : Implements { using Implements::Implements; + //! Overrides C++/WinRT's interface lookup so conditionally-implemented interfaces are hidden when disabled. + //! For each `Version`/`Interface` pair, a `QueryInterface` for that interface succeeds only when the paired + //! `Version::IsEnabled()` returns `true`; interfaces not listed as conditional resolve normally. + //! @param iid The interface being queried for. + //! @return A pointer to the requested interface, or null if it is conditionally disabled or unsupported. void* find_interface(winrt::guid const& iid) const noexcept override { static_assert(sizeof...(Rest) % 2 == 0, "Extra template parameters should come in groups of two"); diff --git a/include/wil/cppwinrt_authoring.h b/include/wil/cppwinrt_authoring.h index 2ee4ad9c2..28ed7aee0 100644 --- a/include/wil/cppwinrt_authoring.h +++ b/include/wil/cppwinrt_authoring.h @@ -44,36 +44,55 @@ namespace details } // namespace details /// @endcond +/** A read-only C++/WinRT property backed by single-threaded (non-atomic) storage. +Wraps a value of type `T` and exposes it as a read-only property on a C++/WinRT runtime class: `operator()` returns the value (the +getter C++/WinRT calls), and no public call-style setter is exposed. Set the value from within your implementation with +`operator=`. For a property that callers can also set, use @ref single_threaded_rw_property instead. +~~~ +struct MyClass : MyClassT +{ + wil::single_threaded_property Answer{42}; // a read-only Answer property + + void Recompute() + { + Answer = 43; // set internally with operator=; Answer(43) intentionally does not compile + } +}; +~~~ +@tparam T The property's value type. */ template struct single_threaded_property : std::conditional_t || std::is_final_v, wil::details::single_threaded_property_storage, T> { single_threaded_property() = default; + //! Constructs the property, forwarding the arguments to initialize the underlying value. + //! @tparam TArgs Constructor argument types for `T` (or its storage). + //! @param value Arguments used to initialize the property's value. template single_threaded_property(TArgs&&... value) : base_type(std::forward(value)...) { } + /// @cond using base_type = std::conditional_t || std::is_final_v, wil::details::single_threaded_property_storage, T>; + /// @endcond + //! Returns the current property value. + //! This is the getter C++/WinRT invokes for the projected property. + //! @return A copy of the current value. T operator()() const { return *this; } - // This is the only setter exposed. We don't expose `operator()(Q&& q)`, - // since that is what C++/WinRT uses to implement public setters. Since - // single_threaded_property is intended for readonly properties, we - // don't want to expose that. - // - // To set the value of this property *internally* (within your - // implementation), use this `operator=`: - // - // MyProperty = 42; - // // MyProperty(42); // won't work - // - // For settable properties, use single_threaded_rw_property instead. + //! Sets the property value from within your implementation. + //! This is the only exposed setter; there is deliberately no call-style `operator()(Q&&)` setter, because C++/WinRT uses that + //! form to expose *public* setters and this type models a read-only property. Write `MyProperty = 42;` (not + //! `MyProperty(42)`). For a publicly settable property, use @ref single_threaded_rw_property. + //! @tparam Q The assigned value type. + //! @param q The new value to store. + //! @return A reference to this property. template auto& operator=(Q&& q) { @@ -82,10 +101,26 @@ struct single_threaded_property } }; +/** A read-write C++/WinRT property backed by single-threaded (non-atomic) storage. +Wraps a value of type `T` and exposes it as a read-write property on a C++/WinRT runtime class: `operator()` with no argument gets +the value, `operator()(value)` sets it (the public setter C++/WinRT calls), and `operator=` sets it from within your +implementation. For a read-only property, use @ref single_threaded_property instead. +~~~ +struct MyClass : MyClassT +{ + wil::single_threaded_rw_property Title{L"Untitled"}; // a read-write Title property +}; +~~~ +@tparam T The property's value type. */ template struct single_threaded_rw_property : single_threaded_property { + /// @cond using base_type = single_threaded_property; + /// @endcond + //! Constructs the property, forwarding the arguments to initialize the underlying value. + //! @tparam TArgs Constructor argument types for `T`. + //! @param value Arguments used to initialize the property's value. template single_threaded_rw_property(TArgs&&... value) : base_type(std::forward(value)...) { @@ -94,6 +129,10 @@ struct single_threaded_rw_property : single_threaded_property using base_type::operator(); // needed in lieu of deducing-this + //! Sets the property value; this is the public setter C++/WinRT invokes when a caller assigns the property. + //! @tparam Q The assigned value type. + //! @param q The new value to store. + //! @return A reference to this property. template auto& operator()(Q&& q) { @@ -101,6 +140,10 @@ struct single_threaded_rw_property : single_threaded_property } // needed in lieu of deducing-this + //! Sets the property value from within your implementation (for example `Title = L"Home";`). + //! @tparam Q The assigned value type. + //! @param q The new value to store. + //! @return A reference to this property. template auto& operator=(Q&& q) { @@ -117,9 +160,25 @@ struct single_threaded_rw_property : single_threaded_property #define __WIL_CPPWINRT_AUTHORING_INCLUDED_ICLASSFACTORY /// @endcond +/** A COM class factory (`IClassFactory`) that creates instances of a C++/WinRT (`winrt::implements`) class. +Creates instances of `T` with `winrt::make_self`, and implements `LockServer` against the C++/WinRT module lock so the host +module stays loaded while the factory is locked. Hand it to whatever needs an `IClassFactory` (for example +`CoRegisterClassObject`, or a `DllGetClassObject` implementation); @ref register_com_server wraps the `CoRegisterClassObject` case +for you. +@tparam T The C++/WinRT class the factory creates. +@tparam Rest Additional C++/WinRT `implements` markers for the factory (not interfaces to implement), appended to its + `winrt::implements` base. For example @ref register_com_server passes `winrt::no_module_lock` so the factory object + itself holds no module lock. */ template struct class_factory : winrt::implements, IClassFactory, winrt::no_weak_ref, Rest...> { + //! Creates an instance of `T` and returns the requested interface. + //! Implements `IClassFactory::CreateInstance`. Aggregation is not supported, so a non-null `outer` fails with + //! `CLASS_E_NOAGGREGATION`. + //! @param outer The aggregating object's controlling `IUnknown`; must be null, since aggregation is unsupported. + //! @param iid Interface to return in `result`. + //! @param result Receives the requested interface on success, or null on failure. + //! @return `S_OK` on success; `CLASS_E_NOAGGREGATION` if `outer` is non-null; otherwise a failure `HRESULT`. HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept final try { @@ -136,6 +195,10 @@ struct class_factory : winrt::implements, IClassFactor } CATCH_RETURN() + //! Increments or decrements the C++/WinRT module lock to keep the host module loaded. + //! Implements `IClassFactory::LockServer`. + //! @param lock `TRUE` to take a module lock, `FALSE` to release one. + //! @return `S_OK` - this function cannot fail. HRESULT __stdcall LockServer(BOOL lock) noexcept final try { @@ -161,6 +224,15 @@ struct class_factory : winrt::implements, IClassFactor #define __WIL_CPPWINRT_AUTHORING_INCLUDED_COM_SERVER /// @endcond +/** Registers a C++/WinRT class as a COM class object for an out-of-process server, returning an RAII un-registration cookie. +Creates a @ref class_factory for `T` and registers it with `CoRegisterClassObject`. The returned cookie unregisters the class +object when it is destroyed, so keep it alive for as long as the class should remain creatable. +@tparam T The C++/WinRT class to register. +@param guid The CLSID to register the class under. +@param context The `CLSCTX` execution context; defaults to `CLSCTX_LOCAL_SERVER`. +@param flags The `REGCLS` registration flags; defaults to `REGCLS_MULTIPLEUSE`. +@return A `wil::unique_com_class_object_cookie` that unregisters the class object when destroyed. +@note Throws a `winrt::hresult_error` if registration fails. */ template WI_NODISCARD_REASON("The class is unregistered when the returned value is destructed") unique_com_class_object_cookie @@ -172,6 +244,15 @@ unique_com_class_object_cookie return registration; } +/** Registers several C++/WinRT classes as COM class objects at once, returning a `std::vector` of RAII un-registration cookies. +Registers each class in `Ts...` under the matching CLSID in the `guids` array. Keep the returned cookies alive for as long as the +classes should remain creatable. +@tparam Ts The C++/WinRT classes to register, in the same order as `guids`. +@param guids The CLSIDs to register the classes under, one per class in `Ts`. +@param context The `CLSCTX` execution context; defaults to `CLSCTX_LOCAL_SERVER`. +@param flags The `REGCLS` registration flags; defaults to `REGCLS_MULTIPLEUSE`. +@return A `std::vector` of `wil::unique_com_class_object_cookie`, one per registered class. +@note Throws a `winrt::hresult_error` if any registration fails. */ template WI_NODISCARD_REASON("The classes are unregistered when the returned value is destructed") std::vector register_com_server( @@ -287,21 +368,31 @@ namespace details template struct notify_property_changed_base { + /// @cond using Type = T; + /// @endcond + + //! Registers a handler for the `INotifyPropertyChanged.PropertyChanged` event. + //! @param value The handler to add. + //! @return An `event_token` identifying the registration, for later removal with the token overload. auto PropertyChanged(Xaml_Data_PropertyChangedEventHandler const& value) { return m_propertyChanged.add(value); } + //! Unregisters a previously registered `PropertyChanged` handler. + //! @param token The token returned when the handler was registered. void PropertyChanged(winrt::event_token const& token) { m_propertyChanged.remove(token); } + /// @cond Type& self() { return *static_cast(this); } + /// @endcond /** * @brief Raises a property change notification event @@ -325,8 +416,10 @@ struct notify_property_changed_base return m_propertyChanged(self(), Xaml_Data_PropertyChangedEventArgs{name}); } + /// @cond protected: winrt::event m_propertyChanged; + /// @endcond }; /** @@ -338,16 +431,27 @@ struct notify_property_changed_base template struct single_threaded_notifying_property : single_threaded_rw_property { + /// @cond using Type = T; using base_type = single_threaded_rw_property; + /// @endcond using base_type::operator(); + //! Sets the property value and raises a change notification if the value changed. + //! This is the public C++/WinRT setter. + //! @tparam Q The assigned value type. + //! @param q The new value to store. + //! @return A reference to this property. template auto& operator()(Q&& q) { return *this = std::forward(q); } + //! Sets the property value and raises a change notification if the value changed. + //! @tparam Q The assigned value type. + //! @param q The new value to store. + //! @return A reference to this property. template auto& operator=(Q&& q) { @@ -362,6 +466,23 @@ struct single_threaded_notifying_property : single_threaded_rw_property return *this; } + //! Constructs a notifying property bound to its owner's `PropertyChanged` event. + //! Usually invoked for you by the `INIT_NOTIFYING_PROPERTY` macro rather than directly. + //! ~~~ + //! struct MyPage : MyPageT, wil::notify_property_changed_base + //! { + //! wil::single_threaded_notifying_property Value; + //! + //! // INIT_NOTIFYING_PROPERTY binds Value to this type's PropertyChanged event and names it "Value", + //! // expanding to Value(&m_propertyChanged, *this, L"Value", 42): + //! MyPage() : INIT_NOTIFYING_PROPERTY(Value, 42) { } + //! }; + //! ~~~ + //! @tparam TArgs Argument types used to initialize the property's value. + //! @param npc The owner's `PropertyChanged` event to raise when the value changes. + //! @param sender The object reported as the sender of the change notification (typically the owning control). + //! @param name The property name reported in the change notification. + //! @param args Arguments used to initialize the property's value. template single_threaded_notifying_property( winrt::event* npc, @@ -372,8 +493,12 @@ struct single_threaded_notifying_property : single_threaded_rw_property { } + //! Copy-constructs the property, including its value and change-notification binding. single_threaded_notifying_property(const single_threaded_notifying_property&) = default; + //! Move-constructs the property from another instance. single_threaded_notifying_property(single_threaded_notifying_property&&) = default; + //! Returns the property name reported in change notifications. + //! @return The property name, as a `std::wstring_view` valid for the lifetime of this property. std::wstring_view Name() const noexcept { return m_name; diff --git a/include/wil/cppwinrt_helpers.h b/include/wil/cppwinrt_helpers.h index d3c09341a..31a5483ec 100644 --- a/include/wil/cppwinrt_helpers.h +++ b/include/wil/cppwinrt_helpers.h @@ -104,14 +104,17 @@ struct dispatcher_handler namespace wil { -//! Resumes coroutine execution on the thread associated with the dispatcher, or throws -//! an exception (from an arbitrary thread) if unable. Supported dispatchers are -//! Windows.System.DispatcherQueue, Microsoft.System.DispatcherQueue, -//! Microsoft.UI.Dispatching.DispatcherQueue, and Windows.UI.Core.CoreDispatcher, -//! but you must include the corresponding header before including -//! wil/cppwinrt_helpers.h. It is okay to include wil/cppwinrt_helpers.h multiple times: -//! support will be enabled for any winrt/Namespace.h headers that were included since -//! the previous inclusion of wil/cppwinrt_headers.h. +//! Returns an awaitable that resumes the current coroutine on the thread associated with `dispatcher`. +//! Awaiting the result suspends the coroutine and reschedules it onto `dispatcher`; if the work item can't be scheduled or is +//! dropped, the `co_await` throws (resuming on an arbitrary thread). Supported dispatchers are `Windows.System.DispatcherQueue`, +//! `Microsoft.System.DispatcherQueue`, `Microsoft.UI.Dispatching.DispatcherQueue`, and `Windows.UI.Core.CoreDispatcher`. +//! @tparam Dispatcher The dispatcher type; support for it is enabled by including the matching `winrt/*.h` header (see note). +//! @param dispatcher The dispatcher whose thread the coroutine should resume on. +//! @param priority The scheduling priority for the resumption; defaults to the dispatcher's `Normal` priority. +//! @return An awaitable that resumes the coroutine on `dispatcher`'s thread. +//! @note Include the corresponding `` header before `` to enable support for a given +//! dispatcher. You may include `` multiple times; support is enabled for any `winrt/Namespace.h` +//! headers included since the previous inclusion. template [[nodiscard]] auto resume_foreground( Dispatcher const& dispatcher, @@ -291,9 +294,11 @@ namespace details } // namespace details /// @endcond -/** Converts C++ / WinRT vectors, iterators, and iterables to std::vector by requesting the -collection's data in bulk. This can be more efficient in terms of IPC cost than iteratively -processing the collection. +/** Converts a C++/WinRT vector, iterator, or iterable to a `std::vector` by requesting the collection's data in bulk. +Fetching the data in bulk can be more efficient in terms of IPC cost than iteratively processing the collection. Works with +`IVector`, `IVectorView`, `IIterable`, `IIterator`, and any type or interface that C++/WinRT projects those onto +(`PropertySet`, `IMap`, and so on). Iterable-only types are fetched in chunks of 64. Given an iterator, the returned vector +holds the iterator's current element and everything after it. @code winrt::IVector collection = GetCollection(); std::vector allData = wil::to_vector(collection); // read all data from collection @@ -302,11 +307,10 @@ for (winrt::hstring const& item : allData) // use item } @endcode -Can be used for IVector, IVectorView, IIterable, IIterator, and any type or -interface that C++/WinRT projects those interfaces for (PropertySet, IMap, etc.) -Iterable-only types fetch content in units of 64. When used with an iterator, the returned -vector contains the iterator's current position and any others after it. -*/ +@tparam TSrc The C++/WinRT collection or iterator type to read from. +@param src The collection or iterator to copy elements out of. +@return A `std::vector` of the collection's element type containing the requested elements. +@note Throws a `winrt::hresult_error` (for example `hresult_changed_state`) if the collection changes during the bulk read. */ template auto to_vector(TSrc const& src) { @@ -365,12 +369,12 @@ auto to_vector(TSrc const& src) namespace wil { -#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU) -//! The following methods require that you include both -//! before including wil/cppwinrt_helpers.h, and that NTDDI_VERSION -//! is at least NTDDI_WIN10_CU. It is okay to include wil/cppwinrt_helpers.h multiple times: -//! support will be enabled for any headers that were included since the previous inclusion -//! of wil/cppwinrt_headers.h. +#if (defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU)) || defined(WIL_DOXYGEN) +//! Returns the `Windows.UI.WindowId` for a Win32 HWND. +//! @param hwnd The window handle to convert. +//! @return The `winrt::Windows::UI::WindowId` identifying `hwnd`. +//! @note Requires including `` and `` before ``, with +//! `NTDDI_VERSION` at least `NTDDI_WIN10_CU`. Throws a `winrt::hresult_error` on failure. inline winrt::Windows::UI::WindowId GetWindowIdFromWindow(HWND hwnd) { ABI::Windows::UI::WindowId abiWindowId; @@ -378,6 +382,11 @@ inline winrt::Windows::UI::WindowId GetWindowIdFromWindow(HWND hwnd) return winrt::Windows::UI::WindowId{abiWindowId.Value}; } +//! Returns the Win32 HWND for a `Windows.UI.WindowId`. +//! @param windowId The window id to convert. +//! @return The `HWND` identified by `windowId`. +//! @note Requires including `` and `` before ``, with +//! `NTDDI_VERSION` at least `NTDDI_WIN10_CU`. Throws a `winrt::hresult_error` on failure. inline HWND GetWindowFromWindowId(winrt::Windows::UI::WindowId windowId) { HWND hwnd; diff --git a/include/wil/cppwinrt_notifiable_server_lock.h b/include/wil/cppwinrt_notifiable_server_lock.h index 0ca6d4c7d..3f2fba27a 100644 --- a/include/wil/cppwinrt_notifiable_server_lock.h +++ b/include/wil/cppwinrt_notifiable_server_lock.h @@ -28,13 +28,40 @@ namespace wil { +/** A C++/WinRT module lock that invokes a callback when the server's last object is released. +Replaces C++/WinRT's default module lock (via `WINRT_CUSTOM_MODULE_LOCK`) with one backed by `CoAddRefServerProcess` / +`CoReleaseServerProcess`, so out-of-process COM servers can be told when their final object goes away (typically to begin shutting +the server down). Register a callback with @ref set_notifier; it runs when the reference count drops to zero. + +This header must be included before any C++/WinRT header, and the project must define `WINRT_CUSTOM_MODULE_LOCK`; C++/WinRT then +routes its module lock through `winrt::get_module_lock()`, which returns the @ref instance() singleton. +~~~ +// With WINRT_CUSTOM_MODULE_LOCK defined project-wide (e.g. pass -DWINRT_CUSTOM_MODULE_LOCK to the compiler), include +// this header before any C++/WinRT header: +#include +#include + +// During server startup, ask to be notified when the last COM object is released: +wil::notifiable_server_lock::instance().set_notifier([] { + // The server now has no live objects; begin shutting down + // (for example, signal the event that main() is blocked on). +}); +~~~ +@note Access the singleton returned by @ref instance(); you cannot create instances of this type yourself. */ struct notifiable_server_lock { + //! Increments the server process reference count and returns the new count. + //! Called by C++/WinRT when an object is created or a module reference is taken. Wraps `CoAddRefServerProcess`. + //! @return The incremented server process reference count. uint32_t operator++() noexcept { return CoAddRefServerProcess(); } + //! Decrements the server process reference count and, when it reaches zero, invokes the registered notifier. + //! Called by C++/WinRT when an object or module reference is released. Wraps `CoReleaseServerProcess`; if that returns zero + //! and a notifier has been set with @ref set_notifier, the notifier runs before this returns. + //! @return The decremented server process reference count; zero means the last reference was released. uint32_t operator--() { const auto ref = CoReleaseServerProcess(); @@ -46,17 +73,28 @@ struct notifiable_server_lock return ref; } + //! Sets the callback to run when the server process reference count reaches zero. + //! The callback (invoked from `operator--()`) typically starts tearing the server down, for example by exiting the process or + //! signaling a shutdown event. + //! @tparam Func A callable invocable as `void()`. + //! @param func The callback to invoke when the last reference is released. template void set_notifier(Func&& func) { notifier = std::forward(func); } + //! Clears any registered notifier, so releasing the last reference no longer runs a callback. + //! Pass `nullptr` to this overload to remove any previously registered callback. void set_notifier(std::nullptr_t) noexcept { notifier = nullptr; } + //! Returns the process-wide singleton lock. + //! This is the instance C++/WinRT uses through `winrt::get_module_lock()`; register your notification callback on it with + //! @ref set_notifier. + //! @return A reference to the singleton `notifiable_server_lock`. static notifiable_server_lock& instance() noexcept { static notifiable_server_lock lock; @@ -72,6 +110,9 @@ struct notifiable_server_lock namespace winrt { +//! Returns the module lock C++/WinRT uses for this server: the @ref wil::notifiable_server_lock singleton. +//! C++/WinRT calls this (because the project defines `WINRT_CUSTOM_MODULE_LOCK`) to obtain the object that tracks live objects; +//! register a shutdown callback on it with @ref wil::notifiable_server_lock::set_notifier. inline auto& get_module_lock() noexcept { return wil::notifiable_server_lock::instance(); diff --git a/include/wil/cppwinrt_wrl.h b/include/wil/cppwinrt_wrl.h index 14e67393c..8a8cd77d1 100644 --- a/include/wil/cppwinrt_wrl.h +++ b/include/wil/cppwinrt_wrl.h @@ -19,7 +19,7 @@ #include "result_macros.h" #include -// wil::wrl_factory_for_winrt_com_class provides interopability between a +// wil::wrl_factory_for_winrt_com_class provides interoperability between a // C++/WinRT class and the WRL Module system, allowing the winrt class to be // CoCreatable. // @@ -58,10 +58,25 @@ namespace details } // namespace details /// @endcond +/** A WRL class factory that makes a C++/WinRT runtime class creatable through the WRL module system. +Wrapping a C++/WinRT class in this factory lets it participate in WRL's `Module` object-count and class-registration machinery, so +the class becomes CoCreatable (for example via `CoCreateInstance`). Each instance this factory creates is wrapped so that, for its +lifetime, it holds a reference on the WRL module and keeps that module from unloading. + +Don't name this factory directly; register the class with the @ref CoCreatableCppWinRtClass macro instead. +@tparam TCppWinRTClass The C++/WinRT class implementation to expose as a CoCreatable COM class. */ template class wrl_factory_for_winrt_com_class : public ::Microsoft::WRL::ClassFactory<> { public: + //! Creates an instance of the wrapped C++/WinRT class and returns the requested interface. + //! Implements `IClassFactory::CreateInstance`. Aggregation is not supported, so a non-null `unknownOuter` fails with + //! `CLASS_E_NOAGGREGATION`. The created object holds a reference on the WRL module for its lifetime. + //! @param unknownOuter The aggregating object's controlling `IUnknown`; must be null, since aggregation is unsupported. + //! @param riid Identifies the interface to retrieve. + //! @param object Receives the requested interface on success, or null on failure. + //! @return `S_OK` on success; `CLASS_E_NOAGGREGATION` if `unknownOuter` is non-null; otherwise a failure `HRESULT` from + //! creating the object or querying for `riid`. IFACEMETHODIMP CreateInstance(_In_opt_ ::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void** object) noexcept try { @@ -74,6 +89,24 @@ class wrl_factory_for_winrt_com_class : public ::Microsoft::WRL::ClassFactory<> }; } // namespace wil +//! Registers a C++/WinRT class with WRL's creator map so it can be created through @ref wil::wrl_factory_for_winrt_com_class. +//! Place this in the `.cpp` that defines `className`, and pair it with `CoCreatableClassWrlCreatorMapInclude(className)` in the +//! module's `dll.cpp` (or equivalent). Expands to WRL's `CoCreatableClassWithFactory` using this header's factory. +//! ~~~ +//! // Define MyClass as an ordinary C++/WinRT class; note that its definition never names wrl_factory_for_winrt_com_class: +//! struct MyClass : winrt::implements +//! { +//! // ... IMyThing implementation ... +//! }; +//! +//! // In the .cpp that defines the MyClass class (this macro applies the factory for you): +//! CoCreatableCppWinRtClass(MyClass) +//! +//! // In the module's dll.cpp (or equivalent): +//! CoCreatableClassWrlCreatorMapInclude(MyClass) +//! ~~~ +//! @param className The C++/WinRT class to make CoCreatable. +//! @note `className` must not be `final`; the factory creates each instance through a wrapper that derives from it. #define CoCreatableCppWinRtClass(className) \ CoCreatableClassWithFactory(className, ::wil::wrl_factory_for_winrt_com_class) From 9c552a41d72053bfc3534f1bc52ec644f2dfe4ba Mon Sep 17 00:00:00 2001 From: Duncan Horn Date: Mon, 27 Jul 2026 11:16:49 -0700 Subject: [PATCH 2/3] Address comments --- include/wil/cppwinrt.h | 82 +++++++++----------------------- include/wil/cppwinrt_authoring.h | 15 ++---- 2 files changed, 27 insertions(+), 70 deletions(-) diff --git a/include/wil/cppwinrt.h b/include/wil/cppwinrt.h index 0a3895855..27ad61248 100644 --- a/include/wil/cppwinrt.h +++ b/include/wil/cppwinrt.h @@ -273,97 +273,65 @@ namespace details } // namespace details /// @endcond -//! Lets WIL's error-handling macros accept a `winrt::hresult` where they expect an `HRESULT`. -//! This overload is found by the `RETURN_IF_FAILED` / `THROW_IF_FAILED` family (and friends) so a `winrt::hresult` result can be -//! checked directly, without first converting it to an `HRESULT`. -//! @param hr The `winrt::hresult` to validate. -//! @return `hr`, as an `HRESULT`-compatible value. +//! Adds `winrt::hresult` support to @ref wil::verify_hresult(T) "verify_hresult". +//! This allows macros like `RETURN_IF_FAILED` to be used with `winrt::hresult`. inline long verify_hresult(winrt::hresult hr) noexcept { return hr; } -//! Returns the raw ABI representation of a C++/WinRT projected object, for passing across the ABI boundary in generic code. -//! This overload set exists mainly because `winrt::get_abi` no longer references any Windows SDK types: for a `winrt::hstring` it -//! returns a bare `void*` rather than an `HSTRING`, so WIL provides these as an alternative that at least knows about `HSTRING` -//! (the `winrt::hstring` overload returns the concrete `HSTRING`). For every other type it simply forwards to `winrt::get_abi`. -//! For a projected interface or runtime class `winrt::impl::abi_t` resolves to a `void*` ABI interface pointer; for a -//! projected value type it is instead the `winrt::impl::abi_t` ABI struct, by value (layout-compatible with, but distinct -//! from, the classic/SDK `ABI::` type). Paired with @ref put_abi. -//! @tparam T A C++/WinRT projected type. -//! @param object The object whose ABI representation to return. -//! @return For an interface or runtime class, a borrowed (not AddRef'd) `void*` interface pointer; for a value type, the -//! `winrt::impl::abi_t` ABI struct by value. +//! Returns the raw ABI representation of a C++/WinRT projected object. +//! This is an improvement over `winrt::get_abi` because it returns `HSTRING` rather than `void*` when passed a `winrt::hstring`. template auto get_abi(T const& object) noexcept { return winrt::get_abi(object); } -//! Returns the raw `HSTRING` that backs a `winrt::hstring`. -//! @param object The string whose `HSTRING` to return. -//! @return The `HSTRING` backing `object` (may be null for an empty string). +//! Overload of `wil::get_abi` for `winrt::hstring` which returns the underlying ABI `HSTRING`. inline auto get_abi(winrt::hstring const& object) noexcept { return static_cast(winrt::get_abi(object)); } -//! Returns a null-terminated wide-character pointer to a `winrt::hstring`'s buffer. -//! This is the `winrt::hstring` overload of WIL's generic `str_raw_ptr` set, which normalizes the string types WIL supports (raw -//! `PCWSTR`, `std::wstring`, `HSTRING`, and so on) to a `PCWSTR` for use by WIL's generic string helpers. -//! @param str The string to read from. -//! @return A `PCWSTR` to `str`'s contents, valid for as long as `str` is alive and unmodified. +//! Adds `winrt::hstring` support to @ref wil::str_raw_ptr(PCWSTR) "str_raw_ptr"; see that function for details. inline auto str_raw_ptr(const winrt::hstring& str) noexcept { return str.c_str(); } -//! Returns the address of a C++/WinRT object's ABI pointer, for receiving it as an out-parameter in generic code. -//! Like @ref get_abi, this exists mainly because `winrt::put_abi` no longer references any Windows SDK types: for a -//! `winrt::hstring` it yields a bare `void**` rather than an `HSTRING*`, so the `winrt::hstring` overload here yields the -//! concrete `HSTRING*`. For every other type it forwards to `winrt::put_abi`. For a projected interface or runtime class the -//! result is `void**` (the address of the object's `void*` ABI interface pointer), and the held interface is released first, so -//! the object need not already be empty; for a projected value type it is `winrt::impl::abi_t*`. Paired with @ref get_abi. -//! @tparam T A C++/WinRT projected type. -//! @param object The object that receives the ABI value; for an interface or runtime class, any held interface is released first. -//! @return For an interface or runtime class, a `void**`; for a value type, a `winrt::impl::abi_t*`. +//! Returns the address of a C++/WinRT object's ABI pointer, for receiving it as an out-parameter. +//! This is an improvement over `winrt::put_abi` because it returns a pointer to `HSTRING` rather than a pointer to `void*` when +//! passed a `winrt::hstring`. template auto put_abi(T& object) noexcept { return winrt::put_abi(object); } -//! Returns the address of a `winrt::hstring`'s backing `HSTRING`, as an `HSTRING*`. -//! @param object The (empty) string that receives the `HSTRING`. -//! @return The address of `object`'s backing `HSTRING`, suitable as an out-parameter. +//! Overload of `wil::put_abi` for `winrt::hstring` which returns a pointer to the underlying ABI `HSTRING`. inline auto put_abi(winrt::hstring& object) noexcept { return reinterpret_cast(winrt::put_abi(object)); } -//! Returns the raw `IUnknown` ABI pointer for a C++/WinRT object, without taking a reference. -//! @param ptr The object whose ABI pointer to return. -//! @return The `IUnknown*` backing `ptr` (borrowed, not AddRef'd; may be null). +//! Adds C++/WinRT support to @ref wil::com_raw_ptr(T*) "com_raw_ptr"; see that function for details. inline ::IUnknown* com_raw_ptr(const winrt::Windows::Foundation::IUnknown& ptr) noexcept { return static_cast<::IUnknown*>(winrt::get_abi(ptr)); } -//! Returns the raw `IInspectable` ABI pointer for a C++/WinRT object, without taking a reference. -//! This overload supplies the `IInspectable` that `wil::cx_object_from_abi` requires. -//! @param ptr The object whose ABI pointer to return. -//! @return The `IInspectable*` backing `ptr` (borrowed, not AddRef'd; may be null). +//! Adds C++/WinRT support to @ref wil::com_raw_ptr(T*) "com_raw_ptr"; see that function for details. inline ::IInspectable* com_raw_ptr(const winrt::Windows::Foundation::IInspectable& ptr) noexcept { return static_cast<::IInspectable*>(winrt::get_abi(ptr)); } -//! Constructs a C++/WinRT projected object from a raw ABI interface pointer by querying for the projected type. -//! Calls `QueryInterface` for `T`'s default interface and returns the resulting projection. -//! @tparam T The C++/WinRT projected type to construct. -//! @param from The interface pointer to convert from; must not be null. -//! @return A `T` referring to the same object as `from`. -//! @note Throws a `winrt::hresult_error` if the `QueryInterface` call fails. +//! Converts a raw ABI interface pointer to a C++/WinRT projected object. +//! @tparam T The C++/WinRT projected type to convert to. +//! @param from The interface pointer to convert from. Must not be null. The function does not take ownership of this pointer. +//! @return A `T` that represents the same object as `from`. +//! @note Throws a `winrt::hresult_error` if the conversion fails. template T convert_from_abi(::IUnknown* from) { @@ -373,8 +341,7 @@ T convert_from_abi(::IUnknown* from) } /** Calls an interop method on a C++/WinRT type's activation factory and returns the object it produces. -Retrieves the activation factory for `WinRTFactory`, queries it for `Interface`, invokes `method` with `args`, and wraps the -result as a `WinRTResult`. Use this with factory interop interfaces such as `IInputPaneInterop`. +Use this with factory interop interfaces such as `IInputPaneInterop`. ~~~ // Produce an object of the factory's own type: winrt::InputPane inputPane = wil::capture_interop(&IInputPaneInterop::GetForWindow, hwnd); @@ -397,8 +364,7 @@ auto capture_interop(HRESULT (__stdcall Interface::*method)(InterfaceArgs...), A } /** Calls an interop method on an existing C++/WinRT object and returns the object it produces. -Queries `obj` for `Interface`, invokes `method` with `args`, and wraps the result as a `WinRTResult`. Use this with per-instance -interop interfaces such as `IUserActivityInterop`. +Use this with per-instance interop interfaces such as `IUserActivityInterop`. ~~~ winrt::UserActivitySession session = wil::capture_interop( activity, &IUserActivityInterop::CreateSessionForWindow, hwnd); @@ -480,7 +446,7 @@ struct [[nodiscard]] winrt_module_reference ++winrt::get_module_lock(); } - //! Takes an additional reference on the host module; every live copy holds its own lock. + //! Takes a reference on the host C++/WinRT module, incrementing its lock count. winrt_module_reference(winrt_module_reference const&) : winrt_module_reference() { } @@ -536,11 +502,8 @@ struct winrt_conditionally_implements : Implements { using Implements::Implements; - //! Overrides C++/WinRT's interface lookup so conditionally-implemented interfaces are hidden when disabled. - //! For each `Version`/`Interface` pair, a `QueryInterface` for that interface succeeds only when the paired - //! `Version::IsEnabled()` returns `true`; interfaces not listed as conditional resolve normally. - //! @param iid The interface being queried for. - //! @return A pointer to the requested interface, or null if it is conditionally disabled or unsupported. + /// @cond + // Internal override of C++/WinRT's interface lookup; invoked by the framework, not called directly by clients. void* find_interface(winrt::guid const& iid) const noexcept override { static_assert(sizeof...(Rest) % 2 == 0, "Extra template parameters should come in groups of two"); @@ -550,6 +513,7 @@ struct winrt_conditionally_implements : Implements } return nullptr; } + /// @endcond private: template diff --git a/include/wil/cppwinrt_authoring.h b/include/wil/cppwinrt_authoring.h index 28ed7aee0..e95bb1b90 100644 --- a/include/wil/cppwinrt_authoring.h +++ b/include/wil/cppwinrt_authoring.h @@ -172,13 +172,8 @@ for you. template struct class_factory : winrt::implements, IClassFactory, winrt::no_weak_ref, Rest...> { - //! Creates an instance of `T` and returns the requested interface. - //! Implements `IClassFactory::CreateInstance`. Aggregation is not supported, so a non-null `outer` fails with - //! `CLASS_E_NOAGGREGATION`. - //! @param outer The aggregating object's controlling `IUnknown`; must be null, since aggregation is unsupported. - //! @param iid Interface to return in `result`. - //! @param result Receives the requested interface on success, or null on failure. - //! @return `S_OK` on success; `CLASS_E_NOAGGREGATION` if `outer` is non-null; otherwise a failure `HRESULT`. + /// @cond + // IClassFactory::CreateInstance; invoked by COM, not called directly by clients. HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept final try { @@ -195,10 +190,7 @@ struct class_factory : winrt::implements, IClassFactor } CATCH_RETURN() - //! Increments or decrements the C++/WinRT module lock to keep the host module loaded. - //! Implements `IClassFactory::LockServer`. - //! @param lock `TRUE` to take a module lock, `FALSE` to release one. - //! @return `S_OK` - this function cannot fail. + // IClassFactory::LockServer; invoked by COM, not called directly by clients. HRESULT __stdcall LockServer(BOOL lock) noexcept final try { @@ -214,6 +206,7 @@ struct class_factory : winrt::implements, IClassFactor return S_OK; } CATCH_RETURN() + /// @endcond }; #endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_ICLASSFACTORY) && defined(__IClassFactory_INTERFACE_DEFINED__) From 27907190d8c74e708ab0d4d37218c6a4075b7c83 Mon Sep 17 00:00:00 2001 From: Duncan Horn Date: Mon, 27 Jul 2026 12:07:47 -0700 Subject: [PATCH 3/3] Missed a comment --- include/wil/cppwinrt_authoring.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/wil/cppwinrt_authoring.h b/include/wil/cppwinrt_authoring.h index e95bb1b90..ba6ca054e 100644 --- a/include/wil/cppwinrt_authoring.h +++ b/include/wil/cppwinrt_authoring.h @@ -166,9 +166,9 @@ module stays loaded while the factory is locked. Hand it to whatever needs an `I `CoRegisterClassObject`, or a `DllGetClassObject` implementation); @ref register_com_server wraps the `CoRegisterClassObject` case for you. @tparam T The C++/WinRT class the factory creates. -@tparam Rest Additional C++/WinRT `implements` markers for the factory (not interfaces to implement), appended to its - `winrt::implements` base. For example @ref register_com_server passes `winrt::no_module_lock` so the factory object - itself holds no module lock. */ +@tparam Rest Additional C++/WinRT `implements` markers for the factory, appended to its `winrt::implements` base, typically +markers rather than interfaces to implement. For example @ref register_com_server passes `winrt::no_module_lock` so the factory +object itself holds no module lock. */ template struct class_factory : winrt::implements, IClassFactory, winrt::no_weak_ref, Rest...> {