{"id":107353,"date":"2022-11-04T07:00:00","date_gmt":"2022-11-04T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=107353"},"modified":"2022-11-04T06:10:42","modified_gmt":"2022-11-04T13:10:42","slug":"20221104-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20221104-00\/?p=107353","title":{"rendered":"Why am I getting a <CODE>RPC_E_WRONG_THREAD<\/CODE> exception when I&#8217;m on the right thread?"},"content":{"rendered":"<p>A customer was having trouble diagnosing a <code>RPC_<wbr \/>E_<wbr \/>WRONG_<wbr \/>THREAD<\/code> exception in their code. The customer&#8217;s code is written in C++\/CX and PPL, but the same principles apply to C++\/WinRT and C#, so I&#8217;ll provide equivalents along the way.<\/p>\n<p>In C++\/CX, the <code>RPC_<wbr \/>E_<wbr \/>WRONG_<wbr \/>THREAD<\/code> exception is projected as a <code>Platform::<wbr \/>Wrong\u00adThread\u00adException<\/code>, and they were getting it from an attempt to create a custom class that derives from <code>Xaml::<wbr \/>Dependency\u00adObject<\/code>:<\/p>\n<pre>public ref class MyViewModel :\r\n    Windows::UI::Xaml::DependencyObject,\r\n    Windows::UI::Xaml::Data::INotifyPropertyChanged\r\n{\r\npublic:\r\n    virtual event Windows::UI::Xaml::Data::\r\n        PropertyChangedEventHandler^ PropertyChanged;\r\n\r\n    ...\r\n};\r\n\r\nvoid MyFlyout::OnLoaded()\r\n{\r\n    create_task(ApplicationData::Current-&gt;LocalFolder-&gt;\r\n                    GetFileAsync(L\"cache.json\"))\r\n    .then([](StorageFile^ file)\r\n    {\r\n        return FileIO::ReadTextAsync(file);\r\n    }).then([this](String^ contents)\r\n    {\r\n        auto json = JsonObject::Parse(contents);\r\n        m_viewModel = ref new MyViewModel(json);\r\n    }).then([this](task&lt;void&gt; t)\r\n    {\r\n        try {\r\n            t.get();\r\n        } catch (Platform::COMException^) {\r\n            \/\/ Something went wrong. Start from scratch.\r\n            m_viewModel = ref new MyViewModel();\r\n        }\r\n    });\r\n}\r\n\r\n\/\/ C++\/WinRT equivalent\r\nwinrt::fire_and_forget MyFlyout::OnLoaded()\r\n{\r\n    auto lifetime = get_strong();\r\n\r\n    try {\r\n        auto file = co_await ApplicationData::Current().LocalFolder().\r\n                        GetFileAsync(L\"cache.json\");\r\n        auto contents = co_await FileIO::ReadTextAsync(file);\r\n        auto json = JsonObject::Parse(contents);\r\n        m_viewModel = MyViewModel(json);\r\n    } catch (...) {\r\n        \/\/ Something went wrong. Start from scratch.\r\n        m_viewModel = MyViewModel();\r\n    }\r\n}\r\n\r\n\/\/ C# equivalent\r\nasync void OnLoaded()\r\n{\r\n    try {\r\n        var file = await ApplicationData.Current.LocalFolder.\r\n                        GetFileAsync(L\"cache.json\");\r\n        var contents = await FileIO.ReadTextAsync(file);\r\n        var json = JsonObject.Parse(contents);\r\n        m_viewModel = new MyViewModel(json);\r\n    } catch (...) {\r\n        \/\/ Something went wrong. Start from scratch.\r\n        m_viewModel = new MyViewModel();\r\n    }\r\n}\r\n<\/pre>\n<p>The <code>RPC_<wbr \/>E_<wbr \/>WRONG_<wbr \/>THREAD<\/code> exception was coming from the catch block.<\/p>\n<p>The customer concluded that somehow the PPL library (or the C++\/WinRT library, or the C# await operator) was catching the exception on the wrong thread, but a closer look at the crash stack showed a different story:<\/p>\n<pre>KERNELBASE!RaiseFailFastException+0x152\r\ncombase!RoFailFastWithErrorContextInternal2+0x4d9\r\nWindows_UI_Xaml!ErrorHelper::ProcessUnhandledError+0xf4\r\nWindows_UI_Xaml!FinalUnhandledErrorDetectedRegistration::\r\n                               OnFinalUnhandledErrorDetected+0xbc\r\nWindows_UI_Xaml!&lt;lambda_...&gt;::operator()+0x1d\r\n...\r\ntwinapi_appcore!UnhandledErrorInvokeHelper::Invoke+0x24\r\ntwinapi_appcore!CoreApplication::ForwardLocalError+0x79\r\ntwinapi_appcore!CoreApplicationFactory::ForwardLocalError+0x77\r\ncombase!CallErrorForwarder+0x150\r\ncontoso!`_ExceptionHolder::ReportUnhandledError'::catch$1+0x39\r\nucrtbase!_CallSettingFrame_LookupContinuationIndex+0x20\r\nucrtbase!__FrameHandler4::CxxCallCatchBlock+0x115\r\nntdll!RcFrameConsolidation+0x6\r\ncontoso!_ExceptionHolder::ReportUnhandledError+0x27\r\ncontoso!_ExceptionHolder::~_ExceptionHolder+0x2b\r\ncontoso!std::_Ref_count_base::_Decref+0x2c\r\ncontoso!std::_Ptr_base&lt;_Task_completion_event&gt;::_Decref+0x12\r\ncontoso!std::shared_ptr&lt;_ExceptionHolder&gt;::{dtor}+0x5\r\ncontoso!_Task_completion_event::~_Task_completion_event+0x53\r\ncontoso!std::_Ref_count_base::_Decref+0x2c\r\ncontoso!std::_Ptr_base&lt;_Task_completion_event&gt;::_Decref+0x2f\r\ncontoso!std::shared_ptr&lt;_Task_completion_event&gt;::{dtor}+0x2f\r\n...\r\ncontoso!__abi_FunctorCapture&lt;&lt;lambda_...&gt;, ...&gt;::Invoke+0x5d\r\ncontoso!&lt;lambda_...&gt;::operator()+0x74\r\ncontoso!_PPLTaskContextCallbackBridge+0xd\r\ncombase!CRemoteUnknown::DoCallback+0x4e\r\nrpcrt4!Invoke+0x73\r\nrpcrt4!Ndr64StubWorker+0xb7c\r\nrpcrt4!NdrStubCall3+0xd2\r\ncombase!CStdStubBuffer_Invoke+0x65\r\ncombase!&lt;lambda_...&gt;::operator()+0x22\r\ncombase!ObjectMethodExceptionHandlingAction&lt;&lt;lambda_...&gt; &gt;+0x4d\r\ncombase!InvokeStubWithExceptionPolicyAndTracing+0xda\r\ncombase!DefaultStubInvoke+0x257\r\ncombase!SyncServerCall::StubInvoke+0x38\r\ncombase!StubInvoke+0x2d8\r\ncombase!ServerCall::ContextInvoke+0x46b\r\ncombase!CServerChannel::ContextInvoke+0x84\r\ncombase!DefaultInvokeInApartment+0xc1\r\ncombase!ASTAInvokeInApartment+0x85\r\ncombase!ComInvokeWithLockAndIPID+0xa13\r\ncombase!ThreadDispatch+0x3d5\r\ncombase!ModernSTAState::HandleMessage+0x55\r\ncombase!ModernSTAWaitContext::Wait+0x632\r\ncombase!ModernSTAWaitInNewContext+0xd4\r\ncombase!ModernSTAThreadWaitForHandles+0xa9\r\ncombase!CoWaitForMultipleHandles+0x80\r\ncombase!ASTAState::WaitForPendingGitRegistrations+0x3e\r\ncombase!CComApartment::WaitForPendingGitRegistrations+0x49\r\ncombase!ApartmentUninitialize+0x10c640\r\ncombase!wCoUninitialize+0x13a\r\ncombase!CoUninitialize+0xeb\r\ntwinapi_appcore!Windows::Foundation::Uninitialize+0x7\r\ntwinapi_appcore!&lt;lambda_...&gt;::operator()+0xe9\r\nSHCore!_WrapperThreadProc+0x10f\r\nkernel32!BaseThreadInitThunk+0x10\r\nntdll!RtlUserThreadStart+0x2b\r\n<\/pre>\n<p>The thread is in the process of uninitializing: Notice that at the bottom of the stack are <code>Apartment\u00adUninitializing<\/code> and <code>CoUninitialize<\/code>. The problem isn&#8217;t that we&#8217;re resuming on the wrong thread. The problem is that XAML has already shut down on this thread, so you can&#8217;t create any new XAML objects.<\/p>\n<p>And the exception that XAML throws when you try to create a XAML object on a thread that is not initialized for XAML is <code>RPC_<wbr \/>E_<wbr \/>WRONG_<wbr \/>THREAD<\/code> because &#8220;You&#8217;re on the wrong thread. Go find a XAML thread.&#8221;<\/p>\n<p>The customer came to the conclusion that while the <code>OnLoaded<\/code> function was loading the content asynchronously, the user dismissed the flyout by clicking on something else, and that triggered the tear-down of the thread. When <code>OnLoaded<\/code> resumed execution, it tried to create a <code>MyViewModel<\/code> object, but failed because XAML had already left the thread.<\/p>\n<p>In this case, the proper course of action is just to abandon the initialization of the <code>MyFlyout<\/code> object, since the user has already dismissed it.<\/p>\n<pre>\/\/ C++\/CX with PPL\r\nvoid MyFlyout::OnLoaded()\r\n{\r\n    create_task(ApplicationData::Current-&gt;LocalFolder-&gt;\r\n                    GetFileAsync(L\"cache.json\"))\r\n    .then([](StorageFile^ file)\r\n    {\r\n        return FileIO::ReadTextAsync(file);\r\n    }).then([](String^ contents)\r\n    {\r\n        auto json = JsonObject::Parse(contents);\r\n        m_viewModel = ref new MyViewModel(json);\r\n    }).then([this](task&lt;void&gt; t)\r\n    {\r\n        try {\r\n            t.get();\r\n        } catch (COMException^) {\r\n            \/\/ Something went wrong. Start from scratch.\r\n            <span style=\"color: blue;\">try {\r\n                m_viewModel = ref new MyViewModel();\r\n            } catch (Platform::WrongThreadException^) {\r\n                \/\/ We were already dismissed - abandon.\r\n            }<\/span>\r\n        }\r\n    });\r\n}\r\n\r\n\/\/ C++\/WinRT equivalent\r\nwinrt::fire_and_forget MyFlyout::OnLoaded()\r\n{\r\n    auto lifetime = get_strong();\r\n\r\n    try {\r\n        auto file = co_await ApplicationData::Current().LocalFolder().\r\n                        GetFileAsync(L\"cache.json\");\r\n        auto contents = co_await FileIO::ReadTextAsync(file);\r\n        auto json = JsonObject::Parse(contents);\r\n        m_viewModel = MyViewModel(json);\r\n    } catch (...) {\r\n        \/\/ Something went wrong. Start from scratch.\r\n        <span style=\"color: blue;\">try {\r\n            m_viewModel = MyViewModel();\r\n        } catch (winrt::hresult_wrong_thread const&amp;) {\r\n            \/\/ We were already dismissed - abandon.\r\n        }<\/span>\r\n    }\r\n}\r\n\r\n\/\/ or with function try\r\nwinrt::fire_and_forget MyFlyout::OnLoaded() <span style=\"color: blue;\">try<\/span>\r\n{\r\n    auto lifetime = get_strong();\r\n\r\n    try {\r\n        auto file = co_await ApplicationData::Current().LocalFolder().\r\n                        GetFileAsync(L\"cache.json\");\r\n        auto contents = co_await FileIO::ReadTextAsync(file);\r\n        auto json = JsonObject::Parse(contents);\r\n        m_viewModel = MyViewModel(json);\r\n    } catch (...) {\r\n        \/\/ Something went wrong. Start from scratch.\r\n        m_viewModel = MyViewModel();\r\n    }\r\n} <span style=\"color: blue;\">catch (winrt::hresult_wrong_thread const&amp;) {\r\n    \/\/ We were already dismissed - abandon.\r\n}<\/span>\r\n\r\n\r\n\/\/ C# equivalent\r\nasync void OnLoaded()\r\n{\r\n    try {\r\n        var file = await ApplicationData.Current.LocalFolder.\r\n                        GetFileAsync(L\"cache.json\");\r\n        var contents = await FileIO.ReadTextAsync(file);\r\n        var json = JsonObject.Parse(contents);\r\n        m_viewModel = new MyViewModel(json);\r\n    } catch (Exception) {\r\n        \/\/ Something went wrong. Start from scratch.\r\n        <span style=\"color: blue;\">try {\r\n            m_viewModel = new MyViewModel();\r\n        } catch (Exception e)\r\n            when ((uint)e.HResult == 0x8001010E) {\r\n            \/\/ We were already dismissed - abandon.\r\n        }<\/span>\r\n    }\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s the wrong thread from XAML&#8217;s point of view.<\/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-107353","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It&#8217;s the wrong thread from XAML&#8217;s point of view.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107353","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=107353"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107353\/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=107353"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=107353"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=107353"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}