diff --git a/include/wil/cppwinrt.h b/include/wil/cppwinrt.h index d2b8521a..27ad6124 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,65 @@ 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. +//! 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; } -// 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. +//! 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); } +//! 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)); } +//! 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. +//! 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); } +//! 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)); } +//! 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)); } -// Needed to power wil::cx_object_from_abi that requires IInspectable +//! 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)); } -// Taken from the docs.microsoft.com article +//! 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) { @@ -325,10 +340,22 @@ 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. +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 +363,18 @@ 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. +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 +440,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 a reference on the host C++/WinRT module, incrementing its lock count. 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 +502,8 @@ struct winrt_conditionally_implements : Implements { using Implements::Implements; + /// @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"); @@ -471,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 2ee4ad9c..ba6ca054 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,20 @@ 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, 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...> { + /// @cond + // IClassFactory::CreateInstance; invoked by COM, not called directly by clients. HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept final try { @@ -136,6 +190,7 @@ struct class_factory : winrt::implements, IClassFactor } CATCH_RETURN() + // IClassFactory::LockServer; invoked by COM, not called directly by clients. HRESULT __stdcall LockServer(BOOL lock) noexcept final try { @@ -151,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__) @@ -161,6 +217,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 +237,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 +361,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 +409,10 @@ struct notify_property_changed_base return m_propertyChanged(self(), Xaml_Data_PropertyChangedEventArgs{name}); } + /// @cond protected: winrt::event m_propertyChanged; + /// @endcond }; /** @@ -338,16 +424,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 +459,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 +486,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 d3c09341..31a5483e 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 0ca6d4c7..3f2fba27 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 14e67393..8a8cd77d 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)