Skip to content

Port crash-resistant event handler as wil::winrt_event<T, Traits>#619

Draft
Copilot wants to merge 4 commits intomasterfrom
copilot/port-crash-resistant-event-handler
Draft

Port crash-resistant event handler as wil::winrt_event<T, Traits>#619
Copilot wants to merge 4 commits intomasterfrom
copilot/port-crash-resistant-event-handler

Conversation

Copy link

Copilot AI commented Feb 22, 2026

Ports ResilientEvent from WidgetDeveloperPlatform into cppwinrt_authoring.h as wil::winrt_event<T, Traits>, where exception-handling behavior is configurable via traits. Includes support for automatic removal of broken (disconnected) handlers.

New types in wil/cppwinrt_authoring.h

Traits (control what happens when a handler throws; on_handler_exception returns booltrue removes the handler from future invocations, false keeps it):

  • swallow_event_errors_traits — logs via LOG_CAUGHT_EXCEPTION() and swallows; all handlers are called regardless. Default.
  • propagate_event_errors_traits — rethrows the exception, stopping remaining handlers (same as winrt::event)
  • failfast_event_errors_traits — calls FAIL_FAST_CAUGHT_EXCEPTION() on any handler exception
  • rpc_resilient_event_errors_traits — for RPC/IPC disconnection errors (RPC_E_DISCONNECTED, RPC_S_SERVER_UNAVAILABLE, RPC_E_SERVER_DIED, etc.) logs and removes the broken handler; for all other errors logs and keeps the handler

Event type:

  • winrt_event<T, Traits = swallow_event_errors_traits> — wraps each handler in a try/catch at registration time. When Traits::on_handler_exception returns true, the handler is automatically removed from future invocations. Thread safety is inherited from winrt::event<T>. Internally uses shared_ptr<winrt::event<T>> so lambdas can safely self-remove via weak_ptr.

Usage

// Crash-resistant (default): exceptions are logged and swallowed, all handlers invoked
wil::winrt_event<winrt::Windows::Foundation::TypedEventHandler<IFoo, IBar>> MyEvent;

// RPC-resilient: disconnected remote handlers are automatically removed
wil::winrt_event<winrt::Windows::Foundation::TypedEventHandler<IFoo, IBar>,
                 wil::rpc_resilient_event_errors_traits> MyRpcEvent;

// Same behavior as winrt::event — first exception propagates, remaining handlers skipped
wil::winrt_event<winrt::Windows::Foundation::TypedEventHandler<IFoo, IBar>,
                 wil::propagate_event_errors_traits> MyStrictEvent;

// Custom traits for telemetry, selective removal, etc.
struct my_telemetry_traits {
    static bool on_handler_exception(std::exception_ptr ep) noexcept { /* log it */ return false; }
};
wil::winrt_event<winrt::Windows::Foundation::EventHandler<int>, my_telemetry_traits> MyEvent;

The interface mirrors untyped_event/typed_event: operator()(handler) to subscribe, operator()(token) to unsubscribe, invoke(args...) to fire.

Original prompt

This section details on the original issue you should resolve

<issue_title>Port "crash resistant event handler" from Widgets</issue_title>
<issue_description>See https://github.com/microsoft/WidgetDeveloperPlatform/pull/848 - pull that ResilientEvent into cppwinrt_authoring.h as wil::winrt_event<T, Traits> where "traits" is configurable for how the event should behave.</issue_description>

<agent_instructions>Be sure to add tests, and pull it into the wil headers using wil style, naming, etc.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits February 22, 2026 02:29
Port the ResilientEvent from WidgetDeveloperPlatform as wil::winrt_event<T, Traits>
in cppwinrt_authoring.h. Adds three traits classes (swallow, propagate, failfast)
and the winrt_event template that wraps handlers in try/catch at registration time
to support configurable exception handling. Default behavior swallows exceptions
so all handlers are invoked even if some throw. Tests added to
CppWinRTAuthoringTests.cpp.

Co-authored-by: jonwis <18537118+jonwis@users.noreply.github.com>
Co-authored-by: jonwis <18537118+jonwis@users.noreply.github.com>
Copilot AI changed the title [WIP] Port crash resistant event handler from Widgets Port crash-resistant event handler as wil::winrt_event<T, Traits> Feb 22, 2026
Copilot AI requested a review from jonwis February 22, 2026 02:30
Copy link
Member

@jonwis jonwis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot I was hoping you'd port the change basically as-is from the source pull request, including the support for removing broken handlers along the way, etc. This is a nice start, please continue.

{
handler(std::forward<decltype(args)>(args)...);
}
catch (...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot update this so the traits by-default use "wil::LogCaughtException" or whatever it's called, but that can be disabled with a flag.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 808b460. swallow_event_errors_traits::on_handler_exception now calls LOG_CAUGHT_EXCEPTION() before returning false (keeping the handler). If you want to suppress logging, implement custom traits with an empty on_handler_exception — the doc comment notes this.


int value = 0;
auto token = test.MyEvent([&](const winrt::Windows::Foundation::IInspectable&, int args) {
value = args;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot This should have a test that verifies the "caught, not leaked" behavior. Be sure to verify the "no filter" and "some filter" and "all filter" behavior from above.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comprehensive tests in 808b460:

  • WinrtEventSwallowExceptions: verifies exception is caught, not leaked (logged via TestFailureCache) and handlers are kept (no filter — all handlers invoked)
  • WinrtEventRpcResilienceRemovesBrokenHandler: verifies RPC errors remove the broken handler on next invoke (some filter — RPC errors removed)
  • WinrtEventRpcResilienceKeepsHandlerOnNonRpcError: verifies non-RPC errors keep the handler (some filter — only RPC filtered out)
  • WinrtEventCustomTraits: verifies custom traits can implement selective removal logic (custom filter with E_ABORT → remove, all others → swallow)

…vent_errors_traits, handler removal support, better tests

Co-authored-by: jonwis <18537118+jonwis@users.noreply.github.com>
Copy link
Author

Copilot AI commented Feb 22, 2026

@copilot I was hoping you'd port the change basically as-is from the source pull request, including the support for removing broken handlers along the way, etc. This is a nice start, please continue.

Extended in 808b460 to include broken handler removal from the original PR concept: winrt_event now stores its winrt::event<T> via shared_ptr so each registered lambda can hold a weak_ptr back to the event and call remove() on itself when on_handler_exception returns true. The rpc_resilient_event_errors_traits uses this to automatically remove handlers that throw RPC disconnection errors.

Copilot AI requested a review from jonwis February 22, 2026 04:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Port "crash resistant event handler" from Widgets

2 participants