{"id":108070,"date":"2023-04-19T07:00:00","date_gmt":"2023-04-19T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108070"},"modified":"2023-04-26T19:33:44","modified_gmt":"2023-04-27T02:33:44","slug":"20230419-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230419-00\/?p=108070","title":{"rendered":"The case of the unhandled exception in a brokered Windows Runtime component"},"content":{"rendered":"<p>An enterprise customer was encountering a crash in their <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/uwp\/winrt-components\/brokered-windows-runtime-components-for-side-loaded-windows-store-apps\"> brokered Windows Runtime component<\/a>. The idea here is that you have a UWP app running in the UWP low-privilege app container. Once in a while, it needs to do something that is not permitted by the low-privilege app container, so it calls out to medium-privilege helper process, known as a <i>broker<\/i>, and the broker does the special thing that requires medium privilege.<\/p>\n<p>Here&#8217;s the crash in the broker process. I&#8217;ve removed some cluttering namespaces like <code>Platform::<\/code> and <code>Windows::<wbr \/>Foundation::<wbr \/>Collections::<\/code>.<\/p>\n<pre>0:005&gt; kn\r\n # Child-SP          Call Site\r\n00 0000001b`81efbcf0 ucrtbase!abort+0x4e\r\n01 0000001b`81efbd20 ucrtbase!terminate+0x29\r\n02 0000001b`81efbd50 ucrtbase!__crt_state_management::\r\n        wrapped_invoke&lt;void (__cdecl*)(void) noexcept,void&gt;+0x1a\r\n03 0000001b`81efbd80 contoso!__scrt_unhandled_exception_filter+0x5a\r\n04 0000001b`81efbdb0 KERNELBASE!UnhandledExceptionFilter+0x1ec\r\n05 (Inline Function) ntdll!RtlpThreadExceptionFilter+0x9b\r\n06 0000001b`81efbed0 ntdll!RtlUserThreadStart$filt$0+0xac\r\n07 0000001b`81efbf10 ntdll!__C_specific_handler+0x97\r\n08 0000001b`81efbf80 ntdll!RtlpExecuteHandlerForException+0xf\r\n09 0000001b`81efbfb0 ntdll!RtlDispatchException+0x29f\r\n0a 0000001b`81efc710 ntdll!RtlRaiseException+0x195\r\n0b 0000001b`81efce70 KERNELBASE!RaiseException+0x6c\r\n0c 0000001b`81efcf50 msvcrt!_CxxThrowException+0xb7\r\n0d 0000001b`81efcfc0 contoso!`EventSource::DoInvokeVoid&lt;...&gt;::`1'::catch$4+0x49\r\n0e 0000001b`81efcff0 msvcrt!_CallSettingFrame_LookupContinuationIndex+0x20\r\n0f 0000001b`81efd020 msvcrt!__FrameHandler4::CxxCallCatchBlock+0x16e\r\n10 0000001b`81efd110 ntdll!RcFrameConsolidation+0x6\r\n11 (Inline Function) contoso!__abi_ThrowIfFailed+0xb5\r\n12 (Inline Function) contoso!EventSource::InvokeVoid::__l2::&lt;lambda_...&gt;::operator()+0x11c\r\n13 0000001b`81eff300 contoso!EventSource::DoInvokeVoid&lt;...&gt;+0x1e6\r\n14 0000001b`81eff3c0 contoso!EventSource::InvokeVoid&lt;...&gt;+0xd8\r\n15 (Inline Function) contoso!Vector&lt;Widget ^&gt;::m_wfc_event::raise+0x16\r\n16 0000001b`81eff440 contoso!Vector&lt;Widget ^&gt;::Notify+0xa3\r\n17 (Inline Function) contoso!Vector&lt;Widget ^&gt;::NotifyInserted+0x10\r\n18 0000001b`81eff470 contoso!Vector&lt;Widget ^&gt;::[IVector&lt;Widget ^&gt;]::InsertAt+0x9f\r\n19 0000001b`81eff500 contoso!WidgetManager::[WidgetManager]::OnWidgetAdded+0x706\r\n1a 0000001b`81eff610 contoso!&lt;lambda_...&gt;::operator()+0x204\r\n1b (Inline Function) SHCore!WorkThreadManager::CThread::RunCurrentTaskUnderLock+0xc8\r\n1c 0000001b`81eff6f0 SHCore!WorkThreadManager::CThread::ThreadProc+0x31a\r\n1d 0000001b`81eff960 SHCore!WorkThreadManager::CThread::s_ExecuteThreadProc+0x22\r\n1e (Inline Function) SHCore!&lt;lambda_...&gt;::operator()+0xd\r\n1f 0000001b`81eff9a0 SHCore!&lt;lambda_...&gt;::&lt;lambda_invoker_cdecl&gt;+0x11\r\n20 0000001b`81eff9d0 kernel32!BaseThreadInitThunk+0x1d\r\n21 0000001b`81effa00 ntdll!RtlUserThreadStart+0x28\r\n<\/pre>\n<p>Reading upward from the bottom, we see that the widget manager&#8217;s <code>OnWidgetAdded<\/code> method was called (frame <code>19<\/code>) presumably to notify it that a widget got added.<\/p>\n<p>In response to this, the widget manager inserts the newly-added widget into what is presumably a collection of active widgets (frame <code>18<\/code>).<\/p>\n<p>This is apparently an observable vector, so we raise the <code>VectorChanged<\/code> event (frame <code>17<\/code> to <code>15<\/code>).<\/p>\n<p>While calling all of the event listeners (frame <code>14<\/code> to <code>12<\/code>), an exception occurs, which is thrown (frame <code>11<\/code>) and goes unhandled, so the process terminates with an unhandled exception.<\/p>\n<p>To diagnose this further, we&#8217;d really like to know where that exception came from. Fortunately, there is a debugger extension that <a href=\"https:\/\/docs.microsoft.com\/en-us\/shows\/inside\/c000027b\"> helps decode throw Windows Runtime exceptions<\/a>. If only we could find them.<\/p>\n<p>To find the thrown object, we want an exception record. We can pull this out of <code>RtlDispatchException<\/code>, since that function is going to call each handler with the exception record and context record. So I&#8217;ll dump the stack between frames <code>08<\/code> and <code>09<\/code>, and hopefully we&#8217;ll find the exception record in there.<\/p>\n<pre>0:005&gt; dpp 0000001b`81efbf80\r\n0000001b`81efbf80  00000000`00000000\r\n0000001b`81efbf88  0000001b`81efc510 00000000`00000000\r\n0000001b`81efbf90  0000001b`81efce90 00000081`<span style=\"border: solid 1px black;\">e06d7363<\/span>\r\n0000001b`81efbf98  00007fff`fc5d0000 00000003`00905a4d\r\n0000001b`81efbfa0  0000001b`81efc570 00007fff`fc637558 ntdll!RtlUserThreadStart+0x28\r\n0000001b`81efbfa8  00007fff`fc60d0af 2d850fed`854dd08b\r\n0000001b`81efbfb0  00000000`00000000\r\n0000001b`81efbfb8  0000001b`81efce90 00000081`e06d7363\r\n0000001b`81efbfc0  00007fff`fc637558 c88b49d1`8b481feb\r\n0000001b`81efbfc8  00007fff`fc76257c 0006757f`00067530\r\n0000001b`81efbfd0  0000001b`81efc010 0065006c`00690061\r\n0000001b`81efbfd8  00000000`00000000\r\n0000001b`81efbfe0  0000001b`81efc530 00007fff`fc736a7c ntdll!_xmm+0x84bc\r\n0000001b`81efbfe8  0000001b`81efc520 0000001b`81effa00\r\n0000001b`81efbff0  0000001b`81efc528 00007fff`fc661840 ntdll!__C_specific_handler\r\n<\/pre>\n<p>Aha, we found a C++ exception record on the stack. We can tell because it has the special exception code <code>e06d7363<\/code>.<\/p>\n<pre>0:005&gt; .exr 0000001b`81efce90\r\nExceptionAddress: 00007ffff9ad5c2c (KERNELBASE!RaiseException+0x000000000000006c)\r\n   ExceptionCode: e06d7363 (C++ EH exception)\r\n  ExceptionFlags: 00000081\r\nNumberParameters: 4\r\n   Parameter[0]: 0000000019930520\r\n   Parameter[1]: 0000001b81eff338\r\n   Parameter[2]: 00007fffebcc9158\r\n   Parameter[3]: 00007fffebc70000\r\n  pExceptionObject: 0000001b81eff338\r\n  _s_ThrowInfo    : 00007fffebcc9158\r\n<\/pre>\n<p>Since we know this was a C++\/CX exception, the thrown object will be some kind of <code>Platform::Exception^<\/code> hat pointer, so the <code>pExceptionObject<\/code> will itself be another pointer. Therefore, we will need to <code>dpp<\/code> it to chase through the pointer.<\/p>\n<pre>0:005&gt; dpp 0000001b81eff338 L1\r\n0000001b`81eff338 000001b4`c1138ec0 00007fff`ebccffa0 wincorlib!\r\n                                          Platform::COMException::`vftable'\r\n<\/pre>\n<p>Now that we&#8217;ve confirmed that it&#8217;s a <code>Platform::<wbr \/>Exception<\/code> (indeed, a <code>Platform::<wbr \/>COMException<\/code>), we can dump its contents.<\/p>\n<pre>0:005&gt; ?? ((contoso!Platform::Exception*)0x000001b4`c1138ec0)\r\nclass Platform::Exception * 0x000001b4`c1138ec0\r\n   +0x000 __VFN_table : 0x00007fff`ebccffa0\r\n   +0x008 __VFN_table : 0x00007fff`ebccff58\r\n   +0x010 __VFN_table : 0x00007fff`ebccff00\r\n   +0x018 __VFN_table : 0x00007fff`ebccfeb8\r\n   +0x020 __description    : 0x000001b4`c11641a8 Void\r\n   +0x028 __restrictedErrorString : 0x000001b4`c1164608 Void\r\n   +0x030 __restrictedErrorReference : (null)\r\n   +0x038 __capabilitySid  : (null)\r\n   +0x040 __hresult        : 0n-2147023170\r\n   +0x048 __restrictedInfo : 0x000001b4`bf2c8108 Void\r\n   +0x050 __throwInfo      : 0x00007fff`ebcc9158 Void\r\n   +0x058 __size           : 0x40\r\n   +0x060 __prepare        : Platform::IntPtr\r\n   +0x068 __abi_reference_count : __abi_FTMWeakRefData\r\n   +0x078 __abi_disposed   : 0\r\n<\/pre>\n<p>That <code>HRESULT<\/code> is<\/p>\n<pre>C:\\&gt; certutil \/error -2147023170\r\n0x800706be (WIN32: 1726 RPC_S_CALL_FAILED) -- 2147944126 (-2147023170)\r\nError message text: The remote procedure call failed.\r\nCertUtil: -error command completed successfully.\r\n<\/pre>\n<p>You can also ask the <code>!pde.err<\/code> command to decode it.<\/p>\n<pre style=\"white-space: pre-wrap;\">0:005&gt; !pde.err -0n2147023170\r\n0x800706BE (FACILITY_WIN32 - Win32 Undecorated Error Codes): The remote procedure call failed.\r\n<\/pre>\n<p>This agrees with the <code>__restrictedErrorString<\/code>:<\/p>\n<pre>0:005&gt; dc 0x000001b4`c1164608\r\n000001b4`c1164608  00680054 00200065 00650072 006f006d  T.h.e. .r.e.m.o.\r\n000001b4`c1164618  00650074 00700020 006f0072 00650063  t.e. .p.r.o.c.e.\r\n000001b4`c1164628  00750064 00650072 00630020 006c0061  d.u.r.e. .c.a.l.\r\n000001b4`c1164638  0020006c 00610066 006c0069 00640065  l. .f.a.i.l.e.d.\r\n<\/pre>\n<p>which is a nice reassurance that we haven&#8217;t gotten ourselves all turned around.<\/p>\n<p>The <code>__restrictedInfo<\/code> contains additional details about the exception. Let&#8217;s see what it holds.<\/p>\n<pre>0:005&gt; dps 0x000001b4`bf2c8108\r\n000001b4`bf2c8108  00007fff`fba2ff80 combase!CRestrictedError::`vftable'\r\n000001b4`bf2c8110  00007fff`fba30040 combase!CRestrictedError::`vftable'\r\n000001b4`bf2c8118  00007fff`fba2ffa8 combase!CRestrictedError::`vftable'\r\n000001b4`bf2c8120  00007fff`fba2ffd0 combase!CRestrictedError::`vftable'\r\n000001b4`bf2c8128  00007fff`fba2ff60 combase!CRestrictedError::`vftable'\r\n000001b4`bf2c8130  00007fff`fba300d0 combase!CRestrictedError::`vftable'\r\n000001b4`bf2c8138  00007fff`fba30088 combase!CRestrictedError::`vftable'\r\n000001b4`bf2c8140  00007fff`fba30008 combase!CRestrictedError::`vftable'\r\n000001b4`bf2c8148  00000001`00000000\r\n000001b4`bf2c8150  000001b4`bf2ab4b0\r\n000001b4`bf2c8158  000001b4`c1164a10\r\n000001b4`bf2c8160  00000000`00000000\r\n000001b4`bf2c8168  00000000`800706be\r\n000001b4`bf2c8170  00000000`00000000\r\n000001b4`bf2c8178  00000000`00000000\r\n000001b4`bf2c8180  00000015`00010002\r\n000001b4`bf2c8188  000001b4`c1145310\r\n000001b4`bf2c8190  00000000`00000008\r\n000001b4`bf2c8198  <span style=\"border: solid 1px black;\">53453032<\/span>`00000038\r\n000001b4`bf2c81a0  00003a69`800706be\r\n000001b4`bf2c81a8  00007fff`fb84c3b3 combase!UseSetErrorInfo+0x1c3\r\n000001b4`bf2c81b0  00000015`00000008\r\n000001b4`bf2c81b8  000001b4`c1145310\r\n<\/pre>\n<p>Aha, we found a stowed exception signature. I recognized it from <a href=\"https:\/\/docs.microsoft.com\/en-us\/shows\/inside\/c000027b\"> time code 5:30 of Andrew Richard&#8217;s presentation<\/a>.<\/p>\n<pre>0:005&gt; dt combase!STOWED_EXCEPTION_INFORMATION_V2 000001b4`bf2c8198\r\n   +0x000 Header           : _STOWED_EXCEPTION_INFORMATION_HEADER\r\n   +0x008 ResultCode       : 800706be\r\n   +0x00c ExceptionForm    : 0y01\r\n   +0x00c ThreadId         : 0y000000000000000000111010011010 (0xe9a)\r\n   +0x010 ExceptionAddress : 0x00007fff`fb84c3b3 Void\r\n   +0x018 StackTraceWordSize : 8\r\n   +0x01c StackTraceWords  : 0x15\r\n   +0x020 StackTrace       : 0x000001b4`c1145310 Void\r\n   +0x010 ErrorText        : 0x00007fff`fb84c3b3  \"????\"\r\n   +0x028 NestedExceptionType : 0\r\n   +0x030 NestedException  : (null)\r\n<\/pre>\n<p>The <code>STOWED_<wbr \/>EXCEPTION_<wbr \/>INFORMATION_<wbr \/>V2<\/code> structure is <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/wer\/stowed-exception-information-v2\"> documented<\/a> on <span style=\"text-decoration: line-through;\">MSDN<\/span> <span style=\"text-decoration: line-through;\">docs.microsoft.com<\/span> learn.microsoft.com.<\/p>\n<p>Okay, so now we can get a stack trace.<\/p>\n<pre>0:005&gt; dpS 0x000001b4`c1145310 L15\r\n 00007fff`fb84b431 combase!RoOriginateError+0x51\r\n 00007fff`ebc73264 wincorlib!Platform::Details::ReCreateException+0x6c\r\n 00007fff`ebc72859 wincorlib!__abi_WinRTraiseCOMException+0x9\r\n 00007fff`9b517ee5 contoso!__abi_WinRTraiseException+0xb5\r\n 00007fff`9b5219a6 contoso!TypedEventHandler&lt;...&gt;::__abi_IDelegate::Invoke+0x26\r\n 00007fff`9b519746 contoso!EventSource::DoInvokeVoid&lt;...&gt;+0xea\r\n 00007fff`9b530b22 contoso!DialWidgetManager::DialWidgetChanged::raise+0x72\r\n 00007fff`9b53d5b7 contoso!DialWidgetManager::OnWidgetVectorChanged+0x387\r\n 00007fff`9b53bb5b contoso!`VectorChangedEventHandler&gt;...&gt;::::`2'::\r\n                   __abi_PointerToMemberWeakRefCapture::Invoke+0xcb\r\n 00007fff`9b52e9ed contoso!?__abi_...+0xd\r\n 00007fff`881f2071 contoso!EventSource::DoInvokeVoid&lt;...&gt;+0x131\r\n 00007fff`881f2948 contoso!EventSource::InvokeVoid&lt;...&gt;+0xd8\r\n 00007fff`88217553 contoso!Vector&lt;Widget ^&gt;::Notify+0xa3\r\n 00007fff`8821644f contoso!Vector&lt;Widget ^&gt;::[IVector&lt;Widget ^&gt;]::InsertAt+0x9f\r\n 00007fff`88243256 contoso!WidgetManager::[WidgetManager]::OnWidgetAdded+0x706\r\n 00007fff`8823dd94 contoso!&lt;lambda_...&gt;::operator()+0x204\r\n 00007fff`fa58fd0a SHCore!WorkThreadManager::CThread::ThreadProc+0x31a\r\n 00007fff`fa58c712 SHCore!WorkThreadManager::CThread::s_ExecuteThreadProc+0x22\r\n 00007fff`fa5abff1 SHCore!&lt;lambda_...&gt;::&lt;lambda_invoker_cdecl&gt;+0x11\r\n 00007fff`fa9f458d kernel32!BaseThreadInitThunk+0x1d\r\n 00007fff`fc637558 ntdll!RtlUserThreadStart+0x28\r\n<\/pre>\n<p>The bottom part of this stack trace matches up with the non-inline functions in the stack trace that crashed, which is another confirmation that we haven&#8217;t gone too far astray.<\/p>\n<p>The top of this stack trace tells us more information: The vector change handler that crashed is <code>Dial\u00adWidget\u00adManager::<wbr \/>On\u00adWidget\u00adVector\u00adChanged<\/code>. It appears that it looked at the newly-added widget and realized that it was a <code>Dial\u00adWidget<\/code> (whatever that is), and it raised the <code>Dial\u00adWidget\u00adChanged<\/code> event, and the handler of <i>that<\/i> event threw an exception.<\/p>\n<p>So we have to find that second event handler.<\/p>\n<p>How are we going to find it? All we have is a stack trace. We don&#8217;t have any local variables.<\/p>\n<p>We&#8217;ll have to work forward from what we <i>do<\/i> have, which is the <code>Widget\u00adManager<\/code>.<\/p>\n<pre>0:005&gt; .frame 13\r\n13 0000001b`81eff300 contoso!EventSource::DoInvokeVoid&lt;...&gt;+0x1e6\r\n0:005&gt; dv\r\n           this = 0x000001b4`c1146098\r\n      __lockArg = 0x000001b4`c11460b8\r\n __invokeOneArg = 0x0000001b`81eff3e0\r\n    __dummylock = struct Platform::Details::EventLock\r\n   __targetsLoc = 0x000001b4`c19aec70\r\n __EvSrcGTA_ret = 0x000001b4`c19aec70\r\n         __size = 1\r\n        __index = 0\r\n        __token = class Windows::Foundation::EventRegistrationToken\r\n      __element = 0x000001b4`c1159210\r\n              e = 0x000001b4`c1138ec0 \"The remote procedure call failed.\r\n\"\r\n<\/pre>\n<p>We can look at the code for <code>Event\u00adSource::<wbr \/>Do\u00adInvoke<wbr \/>Void<\/code>, since it&#8217;s included with the Visual C++ compiler, in the header <code>vccorlib.h<\/code>. Here&#8217;s the code, though I&#8217;ve cleaned it up a bit to make it easier to read.<\/p>\n<pre>template &lt;typename TDelegate, typename TInvokeMethod&gt;\r\nvoid DoInvoke(EventLock* __lockArg, TInvokeMethod __invokeOneArg)\r\n{\r\n    Object^ __targetsLoc;\r\n    EventLock __dummylock = { nullptr, nullptr };\r\n    AcquireSRWLockShared(&amp;__lockArg-&gt;targetsLock);\r\n    void* __EvSrcGTA_ret = EventSourceGetTargetArray(targets, &amp;__dummylock);\r\n    ReleaseSRWLockShared(&amp;__lockArg-&gt;targetsLock);\r\n    *reinterpret_cast&lt;void**&gt;(&amp;targetsLoc) = __EvSrcGTA_ret;\r\n\r\n    if (__targetsLoc != nullptr)\r\n    {\r\n        const unsigned int __size = EventSourceGetTargetArraySize(__targetsLoc);\r\n\r\n        for (unsigned int __index = 0; __index &lt; __size; __index++)\r\n        {\r\n            EventRegistrationToken __token = {};\r\n\r\n            try\r\n            {\r\n                TDelegate^ __element = EventSourceGetTargetArrayEvent(\r\n                        __targetsLoc,\r\n                        __index,\r\n                        &amp;__uuidof(__TDelegate^),\r\n                        &amp;__token.Value\r\n                    );\r\n\r\n                (__invokeOneArg)(__element);\r\n            }\r\n            catch(::Platform::Exception^ e)\r\n            {\r\n                int __hr = e-&gt;HResult;\r\n                if (__hr == 0x800706BA \/* HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) *\/\r\n                 || __hr == 0x80010108 \/* RPC_E_DISCONNECTED *\/\r\n                 || __hr == 0x89020001 \/* JSCRIPT_E_CANTEXECUTE *\/)\r\n                {\r\n                    EventSourceRemove(&amp;__targets, __lockArg, __token);\r\n                }\r\n                else\r\n                {\r\n                    throw e;\r\n                }\r\n            }\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>The code loops through the registered event handlers (which it calls &#8220;targets&#8221;) and calls each one (which it keeps in <code>__element<\/code>). If an exception occurs, the code checks if it&#8217;s one of the three special exceptions, and if not, it rethrows the exception, which means that all subsequent handlers are skipped, and an exception propagates out of the event&#8217;s <code>raise<\/code> method.<\/p>\n<p>This means that in our stack trace, the <code>__element<\/code> is the failed delegate.<\/p>\n<pre>0:005&gt; dps 0x000001b4`c1159210\r\n000001b4`c1159210 00007fff`9b5a00e8 contoso!VectorChangedEventHandler&lt;Widget ^&gt;::`vftable'\r\n000001b4`c1159218 00007fff`9b59e9c0 contoso!VectorChangedEventHandler&lt;Widget ^&gt;::`vftable'\r\n000001b4`c1159220 00007fff`9b59e988 contoso!VectorChangedEventHandler&lt;Widget ^&gt;::`vftable'\r\n000001b4`c1159228 000001b4`c1162c60\r\n000001b4`c1159230 ffffffff`ffffffff\r\n000001b4`c1159238 00007fff`9b5a4710 contoso!`VectorChangedEventHandler&lt;...&gt;::\r\n                                    VectorChangedEventHandler&lt;...&gt;&lt;DialWidgetManager,...&gt;\r\n                                    ::`2'::__abi_PointerToMemberWeakRefCapture::`vftable'\r\n000001b4`c1159240 000001b4`bf25b300\r\n000001b4`c1159248 00007fff`9b53d230 contoso!DialWidgetManager::OnDevicesVectorChanged\r\n<\/pre>\n<p>There is the delegate. We see the member function pointer, and right in front of it is highly like to be <code>this<\/code> pointer.<\/p>\n<pre>0:005&gt; dps 000001b4`bf25b300\r\n000001b4`bf25b300  00007fff`ebcd0670 wincorlib!Platform::Details::ControlBlock::`vftable'\r\n000001b4`bf25b308  00000004`00000005\r\n000001b4`bf25b310  000001b4`bf23ed90\r\n<\/pre>\n<p>Okay, I was close. It&#8217;s not the <code>Dial\u00adWidget\u00adManager<\/code>, but rather a control block that presumably represents a weak pointer to the <code>Dial\u00adWidget\u00adManager<\/code>.<\/p>\n<p>In practice, all weak pointer control blocks work basically the same way. You have a weak reference count, a strong reference count, and a pointer to the object (which is valid only if the strong reference count is nonzero).<\/p>\n<p>Channel9 had a video by Stephan T. Lavavej that explained how control blocks work, but that video seems to have been lost as part of Channel9 being folded into Microsoft Learn, so I&#8217;ll have to point you to <a href=\"https:\/\/www.youtube.com\/watch?v=XH4xIyS9B2I&amp;t=16m44s\"> a different video<\/a>. Here&#8217;s <a href=\"https:\/\/www.nextptr.com\/tutorial\/ta1358374985\/shared_ptr-basics-and-internals-with-examples\"> a different description in text form<\/a> if you&#8217;re the sort of person who learns better by reading.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200504-00\/?p=103725\/\"> I covered the details of the C++\/CX weak reference earlier<\/a>, but the idea is the same.<\/p>\n<p>After the strong and weak reference counts is probably a pointer to the object.<\/p>\n<pre>0:005&gt; dps 000001b4`bf23ed90\r\n000001b4`bf23ed90  00007fff`9b5a3278 contoso!DialWidgetManager::`vftable'\r\n000001b4`bf23ed98  00007fff`9b5a3230 contoso!DialWidgetManager::`vftable'\r\n\r\n0:005&gt; ?? ((contoso!DialWidgetManager*)0x000001b4`bf23ed90)\r\nclass DialWidgetManager * 0x000001b4`bf23ed90\r\n   +0x000 __VFN_table : 0x00007fff`9b5a3278\r\n   +0x008 __VFN_table : 0x00007fff`9b5a3230\r\n   +0x010 __VFN_table : 0x00007fff`9b5a31e8\r\n   +0x018 __VFN_table : 0x00007fff`9b5a31a0\r\n   +0x020 __VFN_table : 0x00007fff`9b5a3088\r\n   +0x028 __VFN_table : 0x00007fff`9b5a3030\r\n   +0x030 __VFN_table : 0x00007fff`9b5a2fd8\r\n   +0x038 __VFN_table : 0x00007fff`9b5a2fa0\r\n   +0x040 __VFN_table : 0x00007fff`9b5a2f70\r\n   +0x048 __VFN_table : 0x00007fff`9b5a2f38\r\n   +0x050 &lt;backing_store&gt;DialWidgetChanged : Platform::EventSource\r\n   ... other members ...\r\n<\/pre>\n<p>Now we can look at the handlers of the <code>Dial\u00adWidget\u00adChanged<\/code> event.<\/p>\n<pre>0:005&gt; ?? ((contoso!Platform::EventSource*)(0x000001b4`bf23ed90+0x50))\r\nclass Platform::EventSource * 0x000001b4`bf23ede0\r\n   +0x000 __targets        : 0x000001b4`c19aea00 Void\r\n\r\n0:005&gt; dps 0x000001b4`c19aea00\r\n000001b4`c19aea00 00007fff`ebccfc10 wincorlib!EventTargetArray::`vftable'\r\n000001b4`c19aea08 00007fff`ebccfbe8 wincorlib!EventTargetArray::`vftable'\r\n000001b4`c19aea10 00007fff`ebccfb98 wincorlib!EventTargetArray::`vftable'\r\n000001b4`c19aea18 00000000`00000000\r\n000001b4`c19aea20 00000000`00000000\r\n000001b4`c19aea28 00007fff`fbae44c8 combase!CStaticMarshaler::s_Instance\r\n000001b4`c19aea30 00000000`00000000\r\n000001b4`c19aea38 00000000`00000001\r\n000001b4`c19aea40 000001b4`bf3e2088\r\n000001b4`c19aea48 000001b4`bf3e2098\r\n<\/pre>\n<p>Unfortunately, we don&#8217;t know the internal structure of an <code>Event\u00adTarget\u00adArray<\/code>, but we can guess: From the last three values we see a <code>1<\/code> and two pointers close together.<\/p>\n<p>One possibility is that the <code>1<\/code> is the count of registered handlers, and the first pointer points to the first such handler.<\/p>\n<p>Another possibility is that the <code>1<\/code> is the reference count of the <code>Event\u00adTarget\u00adArray<\/code>, the first pointer points to the first handler, and the second pointer points to one past the last handler (in the style of a <code>std::vector<\/code>).<\/p>\n<p>Under both of these theories, the first pointer points to the handler, so let&#8217;s go with it.<\/p>\n<pre>0:005&gt; dps 000001b4`bf3e2088 L2\r\n000001b4`bf3e2088  000001b4`bf3e71e0\r\n000001b4`bf3e2090  000001b4`bf3e71d0\r\n<\/pre>\n<p>These two pointers are very close to each other, with the second one coming numerically before the first, so that smells like a <code>std::<wbr \/>shared_ptr<\/code> control block.<\/p>\n<pre>0:005&gt; dps 000001b4`bf3e71d0\r\n000001b4`bf3e71d0 00007fff`ebccfa38 wincorlib!std::_Ref_count_obj&lt;\r\n        Platform::Agile&lt;Platform::Object,1&gt; &gt;::`vftable'\r\n000001b4`bf3e71d8  00000001`00000001\r\n000001b4`bf3e71e0  000001b4`bf240678\r\n<\/pre>\n<p>Looks like we guessed right. We have a vtable that suggests that this is a control block, and we have two small integers (the weak and strong reference counts), and the pointer is probably the actual thing we care about.<\/p>\n<pre>0:005&gt; dps 000001b4`bf240678\r\n000001b4`bf240678  00007fff`fba23e90 combase!g_StublessClientVtbl\r\n000001b4`bf240680  00000000`00000001\r\n000001b4`bf240688  000001b4`bf23eb88\r\n000001b4`bf240690  000001b4`bf23eed0\r\n000001b4`bf240698  00000000`00000000\r\n000001b4`bf2406a0  00000000`00000000\r\n000001b4`bf2406a8  00007fff`f2b30db0 OneCoreUAPCommonProxyStub!gPFactory\r\n000001b4`bf2406b0  00000000`00000000\r\n000001b4`bf2406b8  00000000`00000000\r\n000001b4`bf2406c0  00007fff`fba22348 combase!CStdProxyBuffer_ReleaseMarshalBuffersVtbl\r\n000001b4`bf2406c8  00007fff`f25e6e48 OneCoreUAPCommonProxyStub!\r\n    ___FITypedEventHandler_2_IInspectable_IInspectableProxyVtbl\r\n<\/pre>\n<p>And here at last we have located the COM proxy that failed.<\/p>\n<p>The customer shared this crash dump with the Windows team, who were able to confirm that this is indeed a COM proxy to their UWP app. And the customer&#8217;s logging told them that the UWP app was suspended and subsequently terminated shortly before this crash occurred. That explains why we got an <code>RPC_<wbr \/>S_<wbr \/>CALL_<wbr \/>FAILED<\/code>: The process was terminated before it could respond to the event.<\/p>\n<p>Next time, we&#8217;ll look at what the customer can do to address this problem.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Digging into the event dispatch code.<\/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-108070","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Digging into the event dispatch code.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108070","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=108070"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108070\/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=108070"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108070"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108070"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}