{"id":108078,"date":"2023-04-21T07:00:00","date_gmt":"2023-04-21T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108078"},"modified":"2023-04-21T07:24:26","modified_gmt":"2023-04-21T14:24:26","slug":"20230421-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230421-00\/?p=108078","title":{"rendered":"Protecting a broker from a failing delegate"},"content":{"rendered":"<p>Last time, we saw how we can <a title=\"Protecting a broker from a failing event handler\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230420-00\/?p=108075\"> protect a broker from a failed event handler<\/a>. But what if the thing you need to protect against is a delegate?<\/p>\n<p>You can just replicate the event logic for detecting broken delegates, which we saw <a title=\"The case of the unhandled exception in a brokered Windows Runtime component\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230419-00\/?p=108070\"> at the start of this mini-series<\/a>.<\/p>\n<pre>\/\/ C++\/CX\r\npublic delegate String^ SomeClassConversionHandler(Object^ o);\r\n\r\npublic ref class SomeClass\r\n{\r\npublic:\r\n  SomeClass(SomeClassConversionHandler^ converter) :\r\n    m_converter(converter) {}\r\n\r\nprivate:\r\n  SomeClassConversionHandler^ m_converter;\r\n};\r\n\r\n\/\/ C++\/WinRT\r\n\/\/ .idl\r\nnamespace Contoso\r\n{\r\n  delegate String SomeClassConversionHandler(Object o);\r\n\r\n  runtimeclass SomeClass\r\n  {\r\n    SomeClass(SomeClassConversionHandler converter);\r\n  }\r\n}\r\n\r\n\/\/ .cpp\r\nstruct SomeClass : SomeClassT&lt;SomeClass&gt;\r\n{\r\n  SomeClass(Contoso::SomeClassConversionHandler const&amp; converter)\r\n    : m_converter(converter) {}\r\n\r\n  Contoso::SomeClassConversionHandler m_converter;\r\n};\r\n<\/pre>\n<p>Suppose the <code>Some\u00adClass<\/code> object does some work, but occasionally needs help converting an object to a string, and that assistance comes from a <code>converter<\/code> provided at construction. Suppose the original code looks like this:<\/p>\n<pre>void SomeClass::DoWork()\r\n{\r\n  if (need_conversion) {\r\n    auto converted = m_converter(o);\r\n    \/* ... more code ... *\/\r\n  }\r\n}\r\n<\/pre>\n<p>And we need to protect the call to <code>m_converter<\/code> from a failed delegate.<\/p>\n<p>Well, first of all, we have to decide what we want to happen if the delegate fails.<\/p>\n<p>One possibility is that we want to treat any kind of failure to mean that the conversion to a string produces an empty string.<\/p>\n<pre>\/\/ C++\/CX\r\nvoid SomeClass::DoWork()\r\n{\r\n  if (need_conversion) {\r\n    String^ converted;\r\n    <span style=\"color: #08f;\">try {<\/span>\r\n      converted = m_converter(o);\r\n    <span style=\"color: #08f;\">} catch (...) {\r\n      \/\/ Treat all conversion failures as\r\n      \/\/ converting to empty string.\r\n      converted = L\"\";\r\n    }<\/span>\r\n    \/* ... more code ... *\/\r\n  }\r\n}\r\n\r\n\/\/ C++\/WinRT\r\nvoid SomeClass::DoWork()\r\n{\r\n  if (need_conversion) {\r\n    winrt::hstring converted;\r\n    <span style=\"color: #08f;\">try {<\/span>\r\n      converted = m_converter(o);\r\n    <span style=\"color: #08f;\">} catch (...) {\r\n      \/\/ Treat all conversion failures as\r\n      \/\/ converting to empty string.\r\n      converted = L\"\";\r\n    }<\/span>\r\n    \/* ... more code ... *\/\r\n  }\r\n}\r\n<\/pre>\n<p>Now, this does mean that if the failure is due to a disconnected delegate, every conversion attempt will raise a new <code>Disconnected\u00adException<\/code> that is caught and ignored. This is rather inefficient, because disconnection is a permanent state, and it clutters the error logs, so you may want to detect disconnected converters as a special case.<\/p>\n<pre>bool IsDisconnectedHResult(int hr)\r\n{\r\n  return hr == HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) ||\r\n         hr == RPC_E_DISCONNECTED ||\r\n         hr == JSCRIPT_E_CANTEXECUTE;\r\n}\r\n\r\n\/\/ C++\/CX\r\nvoid SomeClass::DoWork()\r\n{\r\n  if (need_conversion) {\r\n    String^ converted;\r\n    <span style=\"color: #08f;\">if (m_converted == nullptr) {\r\n      \/\/ If no converter, then just use empty string.\r\n      converted = L\"\";\r\n    } else {<\/span>\r\n      try {\r\n        converted = m_converter(o);\r\n      } catch (Exception^ ex) {\r\n        if (IsDisconnectedHResult(ex-&gt;HResult)) {\r\n          <span style=\"color: #08f;\">\/\/ Don't use this disconnected converter any more.\r\n          m_converter = nullptr;<\/span>\r\n        }\r\n        \/\/ Treat all conversion failures as\r\n        \/\/ converting to empty string.\r\n        converted = L\"\";\r\n      }\r\n    }\r\n    \/* ... more code ... *\/\r\n  }\r\n}\r\n\r\n\/\/ C++\/WinRT\r\nvoid SomeClass::DoWork()\r\n{\r\n  if (need_conversion) {\r\n    winrt::hstring converted;\r\n    <span style=\"color: #08f;\">if (m_converted == nullptr) {\r\n      \/\/ If no converter, then just use empty string.\r\n      converted = L\"\";\r\n    } else {<\/span>\r\n      try {\r\n        converted = m_converter(o);\r\n      } catch (...) {\r\n        if (IsDisconnectedHResult(winrt::to_hresult())) {\r\n          <span style=\"color: #08f;\">\/\/ Don't use this disconnected converter any more.\r\n          m_converter = nullptr;<\/span>\r\n        }\r\n        \/\/ Treat all conversion failures as\r\n        \/\/ converting to empty string.\r\n        converted = L\"\";\r\n      }\r\n    }\r\n    \/* ... more code ... *\/\r\n  }\r\n}\r\n<\/pre>\n<p>Here, we overload <code>nullptr<\/code> as a special converter value to mean &#8220;Don&#8217;t even try converting. Just go straight to an empty string.&#8221;<\/p>\n<p>If you already have assigned a special meaning to <code>nullptr<\/code> (say, because that means &#8220;Perform a default conversion&#8221;), then you have some other options.<\/p>\n<p>You could add another member variable to remember whether the converter is any good.<\/p>\n<pre>\/\/ C++\/CX\r\npublic ref class SomeClass\r\n{\r\npublic:\r\n  SomeClass(SomeClassConversionHandler^ converter) :\r\n    m_converter(converter) {}\r\n\r\nprivate:\r\n  SomeClassConversionHandler^ m_converter;\r\n  <span style=\"color: #08f;\">bool m_isConverterDisconnected = false;<\/span>\r\n};\r\n\r\nvoid SomeClass::DoWork()\r\n{\r\n  if (need_conversion) {\r\n    String^ converted;\r\n    <span style=\"color: #08f;\">if (m_isConverterDisconnected) {\r\n      \/\/ Disconnected converter is treated as converting\r\n      \/\/ to empty string.\r\n      converted = L\"\";\r\n    } else<\/span> if (m_converter == nullptr) {\r\n      converted = DefaultConversion(o);\r\n    } else {\r\n      try {\r\n        converted = m_converter(o);\r\n      } catch (Exception^ ex) {\r\n        if (IsDisconnectedHResult(ex-&gt;HResult)) {\r\n          \/\/ Don't use this disconnected converter any more.\r\n          <span style=\"color: #08f;\">m_isConverterDisconnected = true;<\/span>\r\n        } else {\r\n          \/\/ Treat all conversion failures as\r\n          \/\/ converting to empty string.\r\n          converted = L\"\";\r\n        }\r\n      }\r\n    }\r\n    \/* ... more code ... *\/\r\n  }\r\n}\r\n\r\n\/\/ C++\/WinRT\r\nstruct SomeClass : SomeClassT&lt;SomeClass&gt;\r\n{\r\n  SomeClass(Contoso::SomeClassConversionHandler const&amp; converter)\r\n    : m_converter(converter) {}\r\n\r\n  Contoso::SomeClassConversionHandler m_converter;\r\n  <span style=\"color: #08f;\">bool m_isConverterDisconnected = false;<\/span>\r\n};\r\n\r\nvoid SomeClass::DoWork()\r\n{\r\n  if (need_conversion) {\r\n    winrt::hstring converted;\r\n    <span style=\"color: #08f;\">if (m_isConverterDisconnected) {\r\n      \/\/ Disconnected converter is treated as converting\r\n      \/\/ to empty string.\r\n      converted = L\"\";\r\n    } else<\/span> if (m_converter == nullptr) {\r\n      converted = DefaultConversion(o);\r\n    } else {\r\n      try {\r\n        converted = m_converter(o);\r\n      } catch (...) {\r\n        if (IsDisconnectedHResult(winrt::to_hresult())) {\r\n          \/\/ Don't use this disconnected converter any more.\r\n          <span style=\"color: #08f;\">m_isConverterDisconnected = true;<\/span>\r\n        } else {\r\n          \/\/ Treat all conversion failures as\r\n          \/\/ converting to empty string.\r\n          converted = L\"\";\r\n        }\r\n      }\r\n    }\r\n    \/* ... more code ... *\/\r\n  }\r\n}\r\n\r\n<\/pre>\n<p>Or you could replace the disconnected delegate with a working one that performs the &#8220;What to do if the delegate is disconnected&#8221; action.<\/p>\n<pre>\/\/ C++\/CX\r\nvoid SomeClass::DoWork()\r\n{\r\n  if (need_conversion) {\r\n    String^ converted;\r\n    if (m_converter == nullptr) {\r\n      converted = DefaultConversion(o);\r\n    } else {\r\n      try {\r\n        converted = m_converter(o);\r\n      } catch (Exception^ ex) {\r\n        if (IsDisconnectedHResult(ex-&gt;HResult)) {\r\n          <span style=\"color: #08f;\">\/\/ Replace this disconnected delegate with a dummy one.\r\n          m_converter = ref new SomeClassConversionHandler(\r\n            [](Object^) -&gt; String^ { return L\"\"; });<\/span>\r\n        }\r\n        \/\/ Treat all conversion failures as\r\n        \/\/ converting to empty string.\r\n        converted = L\"\";\r\n      }\r\n    }\r\n    \/* ... more code ... *\/\r\n  }\r\n}\r\n\r\n\/\/ C++\/WinRT\r\nvoid SomeClass::DoWork()\r\n{\r\n  if (need_conversion) {\r\n    winrt::hstring converted;\r\n    if (m_converter == nullptr) {\r\n      converted = DefaultConversion(o);\r\n    } else {\r\n      try {\r\n        converted = m_converter(o);\r\n      } catch (...) {\r\n        if (IsDisconnectedHResult(winrt::to_hresult())) {\r\n          <span style=\"color: #08f;\">\/\/ Replace this disconnected delegate with a dummy one.\r\n          m_converter = [](auto&amp;&amp;) { return winrt::hstring(); };<\/span>\r\n        }\r\n        \/\/ Treat all conversion failures as\r\n        \/\/ converting to empty string.\r\n        converted = L\"\";\r\n      }\r\n    }\r\n    \/* ... more code ... *\/\r\n  }\r\n}\r\n<\/pre>\n<p>Now, maybe you want to deal with disconnected delegates differently from actively broken ones. For example, the delegate might throw an <code>E_INVALIDARG<\/code> if the object is not convertible at all, and that&#8217;s different from &#8220;converts to nothing&#8221;.<\/p>\n<pre>\/\/ C++\/CX\r\nvoid SomeClass::DoWork()\r\n{\r\n  if (need_conversion) {\r\n    String^ converted;\r\n    try {\r\n      if (m_converter == nullptr) {\r\n        converted = DefaultConversion(o);\r\n      } else {\r\n        converted = m_converter(o);\r\n      }\r\n    } catch (Exception^ ex) {\r\n      if (IsDisconnectedHResult(ex-&gt;HResult)) {\r\n        \/\/ Don't use this disconnected converter any more.\r\n        \/\/ Use default conversions from now on.\r\n        m_converter = nullptr;\r\n        converted = DefaultConversion(o);\r\n      } else {\r\n        <span style=\"color: #08f;\">throw;<\/span>\r\n      }\r\n    }\r\n    \/* ... more code ... *\/\r\n  }\r\n}\r\n\r\n\/\/ C++\/WinRT\r\nvoid SomeClass::DoWork()\r\n{\r\n  if (need_conversion) {\r\n    winrt::hstring converted;\r\n    try {\r\n      if (m_converter == nullptr) {\r\n        converted = DefaultConversion(o);\r\n      } else {\r\n        converted = m_converter(o);\r\n      }\r\n    } catch (...) {\r\n      if (IsDisconnectedHResult(winrt::to_hresult())) {\r\n        \/\/ Don't use this disconnected converter any more.\r\n        \/\/ Use default conversions from now on.\r\n        m_converter = nullptr;\r\n        converted = DefaultConversion(o);\r\n      } else {\r\n        <span style=\"color: #08f;\">throw;<\/span>\r\n      }\r\n    }\r\n    \/* ... more code ... *\/\r\n  }\r\n}\r\n<\/pre>\n<p>Here, we rethrow any exception from the converter that isn&#8217;t a disconnection. The exception then propagates back to the caller, to tell it that the conversion failed. The caller can then take whatever remedial action it deems appropriate.<\/p>\n<p>Dealing with a broken delegate is more work than dealing with a broken event handler because you have to reimplement the disconnection logic yourself. One way to sidestep this problem is to to leverage all the work that went into events: Just use an event!<\/p>\n<pre>\/\/ C++\/CX\r\npublic ref class SomeClassConversionRequestedEventArgs\r\n{\r\n    property Object^ Value { Object^ get(); };\r\n    property String^ Converted\r\n    { String^ get(); void set(String^); };\r\n};\r\n\r\npublic ref class SomeClass\r\n{\r\npublic:\r\n  SomeClass() {}\r\n\r\n  event TypedEventHandler&lt;SomeClass^, SomeClassConversionRequestedEventArgs^&gt;;^\r\n    ConversionRequested;\r\n};\r\n\r\nvoid SomeClass::DoWork()\r\n{\r\n  if (need_conversion) {\r\n    auto args = ref new SomeClassConversionRequestedEventArgs();\r\n    args-&gt;Value = o;\r\n    ConversionRequested(this, args);\r\n    auto converted = args-&gt;Converted;\r\n    \/* ... more code ... *\/\r\n  }\r\n}\r\n\r\n\r\n\/\/ C++\/WinRT\r\n\/\/ .idl\r\nnamespace Contoso\r\n{\r\n  runtimeclass SomeClassConversionRequestedEventArgs\r\n  {\r\n    Object Value{ get };\r\n    String Converted;\r\n  }\r\n\r\n  runtimeclass SomeClass\r\n  {\r\n    SomeClass();\r\n    event TypedEventHandler&lt;SomeClass, SomeClassConversionRequestedEventArgs&gt;\r\n      ConversionRequested;\r\n  }\r\n}\r\n\r\n\/\/ .cpp\r\nstruct SomeClass : SomeClassT&lt;SomeClass&gt;\r\n{\r\n  SomeClass() {}\r\n\r\n  \/* save some typing *\/\r\n  using ConversionHandler = winrt::TypedEventHandler&lt;\r\n      Contoso::SomeClass, Contoso::SomeClassConversionRequestedEventArgs&gt;;\r\n\r\n  auto ConversionRequested(ConversionHandler const&amp; handler) {\r\n    return m_conversionRequestedEvent.add(handler);\r\n  }\r\n  void ConversionRequested(winrt::event_token const&amp; token) {\r\n    return m_conversionRequestedEvent.remove(token);\r\n  }\r\n\r\n  winrt::event&lt;\r\n    winrt::TypedEventHandler&lt;\r\n      Contoso::SomeClass, Contoso::SomeClassConversionRequestedEventArgs&gt;&gt;\r\n    m_conversionRequestedEvent;\r\n};\r\n\r\nvoid SomeClass::DoWork()\r\n{\r\n  if (need_conversion) {\r\n    Contoso::SomeClassConversionRequestedEventArgs args;\r\n    args.Value(o);\r\n    m_conversionRequestedEvent(*this, args);\r\n    auto converted = args.Converted();\r\n    \/* ... more code ... *\/\r\n  }\r\n}\r\n<\/pre>\n<p>The caller registers an event handler for the <code>Conversion\u00adRequested<\/code> event, and now the event infrastructure now does the work of dealing with the disconnection exception. You can then apply the other techniques for <!-- Protecting a broker from a failing event handler --> protecting a broker from a failed event handler if you want to customize how errors are handled.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Catching them in a different way.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-108078","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Catching them in a different way.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108078","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=108078"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108078\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=108078"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108078"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108078"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}