原文:The case of the UWP application that crashes at launch on Windows 10X – The Old New Thing (microsoft.com)September 24th, 2021
Windows 10X应用程序兼容性测试发现一个程序在启动时崩溃。
我们能够获得应用程序运行崩溃时的栈跟踪。让我们进行一些应用程序兼容性debugging。
尽管Windows10x项目已经被搁置,但对于其他非桌面平台(如Xbox)来说,这个case仍然是一个教训。所以,让我们深入研究。
应用程序在此终止点:
# Call Site 00 SharedLibrary!$8_NativePrimitiveDecoder.DecodeUnsigned+0xe3 01 SharedLibrary!$11_NativeReader.DecodeUnsigned+0x3b 02 SharedLibrary!$14_NativeParser.GetUnsigned+0x23 03 SharedLibrary!$11_ExecutionEnvironmentImplementation.TryGetMethodForOriginalLdFtnResult_Inner+0x30a 04 SharedLibrary!$11_ExecutionEnvironmentImplementation.TryGetMethodForOriginalLdFtnResult+0xa9 05 SharedLibrary!$11_ReflectionExecutionDomainCallbacksImplementation.GetMethodNameFromStartAddressIfAvailable+0x45 06 SharedLibrary!DeveloperExperience.CreateStackTraceString+0x65 07 SharedLibrary!StackTraceHelper.FormatStackTrace+0x8b 08 SharedLibrary!Exception.get_StackTrace+0x47 09 Contoso+0x1f6eaa 0a Contoso+0x5a2dc3 0b Windows_UI_Xaml!GetStringRawBuffer+0x1028 0c Windows_UI_Xaml!GetStringRawBuffer+0x1225 0d Windows_UI_Xaml!GetStringRawBuffer+0x46bc1 0e Windows_UI_Xaml!GetStringRawBuffer+0x46a68 0f Windows_UI_Xaml!GetStringRawBuffer+0x46443 10 Windows_UI_Xaml!GetStringRawBuffer+0x46269 11 twinapi_appcore!GitInvokeHelper<IEventHandler<Windows::ApplicationModel::Core::UnhandledErrorDetectedEventArgs *>>::Invoke+0x5a 12 twinapi_appcore!UnhandledErrorInvokeHelper::Invoke+0x1a 13 twinapi_appcore!EventSource<IEventHandler<UnhandledErrorDetectedEventArgs *>>::InvokeAll::lambda::operator()+0x1d 14 twinapi_appcore!InvokeTraits<2>::InvokeDelegates+0x4f 15 twinapi_appcore!EventSource<IEventHandler<UnhandledErrorDetectedEventArgs *>>::DoInvoke+0x7b 16 twinapi_appcore!EventSource<IEventHandler<UnhandledErrorDetectedEventArgs *>>::InvokeAll+0x29 17 twinapi_appcore!CoreApplication::ForwardLocalError+0x73 18 twinapi_appcore!CoreApplicationFactory::ForwardLocalError+0xf0 19 combase!CallErrorForwarder+0x138 1a SharedLibrary!$8_ExceptionHelpers.ReportUnhandledError+0xcd 1b SharedLibrary!$8_InteropCallbacks.ReportUnhandledError+0x9 1c Contoso+0x35d8d 1d SharedLibrary!RuntimeExceptionHelpers.ReportUnhandledException+0x63 1e SharedLibrary!RuntimeAugments.ReportUnhandledException+0x9 1f SharedLibrary!$13_Invoker.InvokeCore$catch$0+0xa 20 mrt100_app!RhpCallCatchFunclet2 21 mrt100_app!RhRethrow+0x4c9 22 mrt100_app!RhThrowEx+0x4f 23 mrt100_app!RhpThrowEx2 24 SharedLibrary!ExceptionServices::ExceptionDispatchInfo.Throw+0x22 25 SharedLibrary!$22_ExceptionDispatchHelper::<>c__DisplayClass0.<ThrowAsync>b__3+0x1f 26 SharedLibrary!$13_WinRTSynchronizationContext::Invoker.InvokeCore+0x4d 27 SharedLibrary!$13_WinRTSynchronizationContext::Invoker.Invoke+0x1c 28 SharedLibrary!Func$2<__Canon,TimeSpan>.InvokeOpenStaticThunk+0x1a 29 SharedLibrary!$25_AsyncOperationWithProgressCompletedHandler$2<__Canon,UInt32>.Invoke+0x14 2a Contoso+0x35d78 2b Contoso+0x5a320b 2c Windows_UI!CDispatcher::ProcessInvokeItem+0x2cc 2d Windows_UI!CDispatcher::ProcessMessage+0x347 2e Windows_UI!CDispatcher::WaitAndProcessMessagesInternal+0xc9 2f Windows_UI!CDispatcher::ProcessEvents+0x132 30 Windows_UI_Xaml!DllGetActivationFactory+0xbaee0 31 Windows_UI_Xaml!DllGetActivationFactory+0xbae7f 32 twinapi_appcore!CoreApplicationView::Run+0x3a 33 twinapi_appcore!lambda::operator()+0xf2 34 shcore!_WrapperThreadProc+0xfb 35 ntdll!RtlUserThreadStart+0x2f
从调用栈底部看,我们看到UI线程正在调度一项工作。此工作正在报告应用程序中其他位置的未处理异常,即:应用程序在终止时尝试构建堆栈跟踪,可能是因为它触发了某个系统超时异常。
让我们找出崩溃栈中的异常,看看错误是什么。这个未处理的异常在事件参数中。
⟦ .frame命令用来更改为特定的堆栈帧⟧ 0:008> .frame 12 12 0000004b`0a1fe9d0 00007fff`be4fa76b twinapi_appcore!UnhandledErrorInvokeHelper::Invoke+0x1a ⟦dv命令用来dump当前帧的局部变量⟧ 0:008> dv this = <value unavailable> source = <value unavailable> args = 0x00000219`7fa93160 ⟦??命令打印c++表达式⟧ 0:008> ?? args struct Windows::ApplicationModel::Core::IUnhandledErrorDetectedEventArgs * 0x00000219`7fa93160 +0x000 __VFN_table : 0x00007fff`be5c1290
这个参数是IUnhandledErrorDetectedEventArgs,它是Unhandled-Error-Detected-Event-Args运行时类的专用接口。
所以我们可以采取一个简易方式,并假设底层对象是一个Unhandled-Error-Detected-Event-Args对象(如果我们想要花费长时间去验证,可以转储虚表,看看它来自哪个具体类)。
⟦dt命令用来dump类型⟧ 0:008> dt twinapi_appcore!Windows::ApplicationModel::Core::UnhandledErrorDetectedEventArgs 0x00000219`7fa93160 +0x000 __VFN_table : 0x00007fff`be5c1290 +0x008 __VFN_table : 0x00007fff`be5c1270 +0x010 __VFN_table : 0x00007fff`be5c1228 +0x028 marshaller_ : Microsoft::WRL::ComPtr<IMarshal> +0x038 refCount_ : Microsoft::WRL::Details::ReferenceCountOrWeakReferencePointer +0x040 _error : Microsoft::WRL::ComPtr<Windows::ApplicationModel::Core::UnhandledError>
我猜_error包含要报告的异常(最好是这样,因为其他成员似乎没有任何有用的东西!)。
⟦? ?命令对于更复杂的dumping非常方便⟧ 0:008> ?? ((twinapi_appcore!Windows::ApplicationModel::Core::UnhandledErrorDetectedEventArgs*) 0x00000219`7fa93160)->_error class Microsoft::WRL::ComPtr<Windows::ApplicationModel::Core::UnhandledError> +0x000 ptr_ : 0x00000219`7fa93250 Windows::ApplicationModel::Core::UnhandledError
ComPtr是一个智能指针类,因此我们必须深入到ptr_内部,以获取它所管理的原始指针。不同的智能指针类对内部原始指针有不同的名称。试图记住这些名字是没有意义的,否则会被转储类型签着你的鼻子走。
0:008> ?? ((twinapi_appcore!Windows::ApplicationModel::Core::UnhandledErrorDetectedEventArgs*) 0x00000219`7fa93160)->_error.ptr_ class Windows::ApplicationModel::Core::UnhandledError * 0x00000219`7fa93250 +0x000 __VFN_table : 0x00007fff`be5c1bf0 +0x008 __VFN_table : 0x00007fff`be5c1c38 +0x010 __VFN_table : 0x00007fff`be5c1c58 +0x028 marshaller_ : Microsoft::WRL::ComPtr<IMarshal> +0x038 refCount_ : Microsoft::WRL::Details::ReferenceCountOrWeakReferencePointer +0x040 _handled : 0x1 '' +0x048 _restrictedError : Microsoft::WRL::ComPtr<IRestrictedErrorInfo> 0:008> ?? ((twinapi_appcore!Windows::ApplicationModel::Core::UnhandledErrorDetectedEventArgs*) 0x00000219`7fa93160)->_error.ptr_->_restrictedError class Microsoft::WRL::ComPtr<IRestrictedErrorInfo> +0x000 ptr_ : 0x00000219`777a6888 IRestrictedErrorInfo
我们定位到底层的指向COM接口的原始指针。现在还不清楚哪个类实现了这个接口,所以我们必须使用vtable帮忙我们识别它。
⟦the dps command dumps pointer-sized data in the style of a stack⟧ 0:008> dps 0x00000219`777a6888 L1 00000219`777a6888 00007fff`c2b89208 combase!CRestrictedError::`vftable' Okay, 这是一个CRestrictedError对象. 我们 dump 虚表里的第一个入口看看需要调整多少这个指针才能到达对象的起点 ⟦the dpp command dumps pointer-sized data, and then dereferences it as a pointer⟧ 0:008> dpp 0x00000219`777a6888 L1 00000219`777a6888 00007fff`c2b89208 00007fff`c2a85090 combase![thunk]:...::QueryInterface`adjustor{8}'
调试告诉我们该对象比起身位置少了8个字节,所以我们需要在将指针显示为CRestricted-Error之前从指针偏移值中减去8。用一个以8结尾的值减去8很容易,只要把8换成0就行了。
0:008> dt combase!CRestrictedError 00000219`777a6880 +0x000 __VFN_table : 0x00007fff`c2b89230 ... +0x050 _pszDescription : 0x00000219`7faaa650 "Class not registered.." +0x058 _pszRestrictedDescription : 0x00000219`7fade760 "Class not registered (Excep_FromHResult 0x80040154)" +0x068 _hrError : 80040154 +0x084 _cStackBackTrace : 0x1e +0x088 _ppvStackBackTrace : 0x00000219`7fafc040 -> 0x00007fff`c2a3d884 Void
到目前为止,我们已经了解到原始异常是一个“Class not registered”错误。我不确定,但根据名称,很可能_ppvStackBackTrace指向一个堆栈回溯,而_cStackBackTrace是回溯中的元素数。
0:008> dps 0x00000219`7fafc040 l1e 00000219`7fafc040 combase!RoOriginateLanguageException+0x54 00000219`7fafc048 SharedLibrary!$8_::ExceptionHelpers.OriginateLanguageException+0xf8 00000219`7fafc050 SharedLibrary!$8_ExceptionHelpers.ReportUnhandledError+0x7b 00000219`7fafc058 SharedLibrary!$8_InteropCallbacks.ReportUnhandledError+0x9 00000219`7fafc060 Contoso+0x35d8d 00000219`7fafc068 SharedLibrary!SystemRuntimeExceptionHelpers.ReportUnhandledException+0x63 00000219`7fafc070 SharedLibrary!RuntimeAugments.ReportUnhandledException+0x9 00000219`7fafc078 SharedLibrary!$13_WinRTSynchronizationContext::Invoker.InvokeCore$catch$0+0xa 00000219`7fafc080 mrt100_app!RhpCallCatchFunclet2 00000219`7fafc088 mrt100_app!RhRethrow+0x4c9 00000219`7fafc090 mrt100_app!RhThrowEx+0x4f 00000219`7fafc098 mrt100_app!RhpThrowEx2 00000219`7fafc0a0 SharedLibrary!ExceptionDispatchInfo.Throw+0x22 00000219`7fafc0a8 SharedLibrary!$22_ExceptionDispatchHelper::<>c__DisplayClass0.<ThrowAsync>b__3+0x1f 00000219`7fafc0b0 SharedLibrary!$13_Invoker.InvokeCore+0x4d 00000219`7fafc0b8 SharedLibrary!$13_Invoker.Invoke+0x1c 00000219`7fafc0c0 SharedLibrary!Func$2<__Canon,TimeSpan>.InvokeOpenStaticThunk+0x1a 00000219`7fafc0c8 SharedLibrary!$25_AsyncOperationWithProgressCompletedHandler$2<__Canon,UInt32>.Invoke+0x14 00000219`7fafc0d0 Contoso+0x35d78 00000219`7fafc0d8 Contoso+0x5a320b 00000219`7fafc0e0 Windows_UI!CDispatcher::ProcessInvokeItem+0x2cc 00000219`7fafc0e8 Windows_UI!CDispatcher::ProcessMessage+0x347 00000219`7fafc0f0 Windows_UI!CDispatcher::WaitAndProcessMessagesInternal+0xc9 00000219`7fafc0f8 Windows_UI!CDispatcher::ProcessEvents+0x132 00000219`7fafc100 Windows_UI_Xaml!DllGetActivationFactory+0xbaee0 00000219`7fafc108 Windows_UI_Xaml!DllGetActivationFactory+0xbae7f 00000219`7fafc110 twinapi_appcore!CoreApplicationView::Run+0x3a 00000219`7fafc118 twinapi_appcore!lambda::operator()+0xf2 00000219`7fafc120 shcore!_WrapperThreadProc+0xfb 00000219`7fafc128 ntdll!RtlUserThreadStart+0x2f
OKay,堆栈跟踪不是很有趣,因为它和我们现在所处的堆栈是一样的。我们想要的是生成原始异常的堆栈。
因此,让我们找到排队到这个线程上的工作项,看看是谁在队列中。
此时,我们利用一个时序跟踪的实际情况:向后执行到当前工作项所在队列中的地址。
我们需要回到堆栈跟踪的这一个地址:
2b 0000004b`0a1ff4c0 00007fff`ad9f9140 Contoso+0x5a320b 2c 0000004b`0a1ff510 00007fff`ad9f790b Windows_UI!Windows::UI::Core::CDispatcher::ProcessInvokeItem+0x2cc
我们想要的地址是在堆栈深处的下一个函数返回地址之前的指令。
⟦u命令用来反汇编 ("unassembles")⟧ 0:008> u 00007fff`ad9f9140-20 00007fff`ad9f9140 00007fff`ad9f9122 488b01 mov rax,qword ptr [rcx] 00007fff`ad9f9125 488b4068 mov rax,qword ptr [rax+68h] 00007fff`ad9f9129 ff15e1a70900 call qword ptr [Windows_UI!__guard_dispatch_icall_fptr (00007fff`ada93910)] 00007fff`ad9f912f 488b4b10 mov rcx,qword ptr [rbx+10h] 00007fff`ad9f9133 488b01 mov rax,qword ptr [rcx] 00007fff`ad9f9136 488b4018 mov rax,qword ptr [rax+18h] 00007fff`ad9f913a ff15d0a70900 call qword ptr [Windows_UI!__guard_dispatch_icall_fptr (00007fff`ada93910)] 00007fff`ad9f9140 8be8 mov ebp,eax
返回地址是00007fff ‘ad9f9140,所以我们想回到它之前的指令,它是00007fff ‘ad9f913a。
⟦g-命令向后执行,直到命中一个断点⟧ 0:008> g- 00007fff`ad9f913a Time Travel Position: 1D6637:68 Windows_UI!Windows::UI::Core::CDispatcher::ProcessInvokeItem+0x2c6: 00007fff`ad9f913a ff15d0a70900 call qword ptr [Windows_UI!__guard_dispatch_icall_fptr (00007fff`ada93910)] 0:008> dv this = 0x00000219`74134a30 pbInvokeItemProcessed = 0x0000004b`0a1ff691 hr = 0x00000000 pInvokeItem = 0x00000219`7f41a170 dwStatus = <value unavailable> requiredPriority = <value unavailable> msg = {msg=0x270 wp=0x0 lp=0x1} bucketAssist = 0x00007fff`7d731330 spIdleDispatchedArgs = {...} WPP_GLOBAL_Control = <value unavailable> 0:008> ?? pInvokeItem struct Windows::UI::Core::_InvokeEntry * 0x00000219`7f41a170 +0x000 pNext : 0x00000219`7f41ab30 Windows::UI::Core::_InvokeEntry +0x008 dwTickCount : 0x115a777 +0x00c Priority : 0 ( CoreDispatcherPriority_Normal ) +0x010 spHandler : Microsoft::WRL::ComPtr<Windows::UI::Core::IDispatchedHandler> +0x018 spIdleHandler : Microsoft::WRL::ComPtr<Windows::UI::Core::IIdleDispatchedHandler> +0x020 spCoreAsyncInfo : Microsoft::WRL::ComPtr<Windows::UI::Core::ICoreAsyncInfo>
现在,我们已经执行到了将要分派工作项的地方,我们可以查看局部变量来获取项目。(如果这不起作用,我们可以将它从rcx寄存器中取出,因为代码设置为调用。)
pInvokeItem看起来像是已进入队列并即将被分派的工作项。我想回到创建工作项的时候,所以我选择了一个可能只在构建时设置的字段:The priority(优先级)。
0:008> ?? pInvokeItem->Priority Windows::UI::Core::CoreDispatcherPriority CoreDispatcherPriority_Normal (0n0) 0:008> ?? &pInvokeItem->Priority Windows::UI::Core::CoreDispatcherPriority * 0x00000219`7f41a17c 0:008> ba w4 0x00000219`7f41a17c
我们向调试器请求Priority成员的地址,并在其上设置一个4字节的写断点。然后再向后执行一些,看看是谁完成的。
0:008> g- Breakpoint 6 hit Time Travel Position: 1CE288:87 Windows_UI!Windows::UI::Core::CDispatcher::EnqueueAsyncWork+0x390: 00007fff`ad9f88bc 48ff15eda90900 call qword ptr [Windows_UI!_imp_GetTickCount (00007fff`ada932b0)]
从函数名Enqueue-Async-Work看来,我们找到了创建工作项的代码。让我们看看为什么要创建它。
⟦k命令用来堆栈跟踪⟧ 0:008> k # Call Site 00 Windows_UI!Windows::UI::Core::CDispatcher::EnqueueAsyncWork+0x390 01 Windows_UI!Windows::UI::Core::CDispatcher::RunAsyncWorker+0xb0 02 Windows_UI!Windows::UI::Core::CDispatcher::RunAsync+0x58 03 Contoso+0x150d0d 04 Contoso+0x150c07 05 Contoso+0x150b98 06 Contoso+0x150b79 07 Contoso+0x35d46 08 SharedLibrary!$13_System::Threading::WinRTSynchronizationContext.Post+0x68 09 SharedLibrary!$13_System::Runtime::CompilerServices::AsyncMethodBuilderCore.ThrowAsync+0x7f 0a SharedLibrary!$13_System::Runtime::CompilerServices::AsyncVoidMethodBuilder.SetException+0x86 0b Contoso+0x45a79c 0c mrt100_app!RhpCallCatchFunclet2 0d mrt100_app!RhRethrow+0x4c9 0e mrt100_app!RhThrowEx+0x4f 0f mrt100_app!RhpThrowEx2 10 SharedLibrary!$8_Interop::WinRT.RoGetActivationFactory+0x19c 11 SharedLibrary!$8_System::Runtime::InteropServices::FactoryCache.GetActivationFactoryInternal+0x5e 12 SharedLibrary!$8_System::Runtime::InteropServices::FactoryCache.GetActivationFactory+0x126 13 SharedLibrary!$8_System::Runtime::InteropServices::McgMarshal.GetActivationFactory+0x4a 14 SharedLibrary!$8_System::Runtime::InteropServices::McgModuleManager.GetActivationFactory+0x50 15 Contoso+0x3fe578 16 Contoso+0x3fdb71 17 Contoso+0x454fab 18 Contoso+0x454f10 19 Contoso+0x45a446 1a Contoso+0x45a3a7 1b Contoso+0x45a36c 1c Contoso+0x45a351 1d Contoso+0x5a2593 1e Windows_UI_Xaml!GetErrorContextIndex+0x40f58 1f Windows_UI_Xaml!GetErrorContextIndex+0x40cc4 20 Windows_UI_Xaml!DllCanUnloadNow+0x26484 21 Windows_UI_Xaml!DllCanUnloadNow+0x27e91 22 Windows_UI_Xaml!DllCanUnloadNow+0x27a9d 23 Windows_UI_Xaml!DllGetActivationFactory+0x243ea 24 Windows_UI_Xaml!GetErrorContextIndex+0x51cf2 25 Windows_UI_Xaml!GetErrorContextIndex+0x51b1e 26 Windows_UI_Xaml!GetErrorContextIndex+0x519fa 27 minuser!Core::Yield::WndProc+0x6b 28 minuser!Core::Window::DeliverMessage+0x30f 29 minuser!Core::Window::SendCommon+0x82 2a minuser!Core::Window::Send+0x3a 2b minuser!Core::Api::SendMessageAW+0x48 2c minuser!minSendMessageAW+0x5d 2d Windows_UI_Xaml!GetErrorContextIndex+0x24de7 2e Windows_UI_Xaml!GetErrorContextIndex+0x4cc7c 2f Windows_UI_Xaml!GetErrorContextIndex+0x47b05 30 Windows_UI_Xaml!GetErrorContextIndex+0x47a0f 31 Windows_UI_Xaml!GetErrorContextIndex+0x2c29f 32 Windows_UI_Xaml!GetErrorContextIndex+0x9e01d 33 Windows_UI_Xaml!GetErrorContextIndex+0x9deaf 34 Windows_UI_Xaml!GetErrorContextIndex+0x9d7e6 35 Windows_UI_Xaml!DllGetActivationFactory+0x3d26d 36 Windows_UI_Xaml!DllGetActivationFactory+0x3d183 37 Windows_UI_Xaml!GetErrorContextIndex+0x51a89 38 Windows_UI_Xaml!GetErrorContextIndex+0x9dc1b 39 Windows_UI_Xaml!GetErrorContextIndex+0x9db0c 3a CoreMessaging!Microsoft__CoreUI__Dispatch__TimeoutHandler$CallbackThunk+0x11b 3b CoreMessaging!Microsoft::CoreUI::Dispatch::TimeoutHandler::Invoke+0x1a 3c CoreMessaging!Microsoft::CoreUI::Dispatch::TimeoutManager::Callback_OnDispatch+0x18b 3d CoreMessaging!Microsoft::CoreUI::Dispatch::Dispatcher::DispatchNextItem+0x885 3e CoreMessaging!Microsoft::CoreUI::Dispatch::Dispatcher::Callback_DispatchLoop+0x9e3 3f CoreMessaging!Microsoft::CoreUI::Dispatch::EventLoop::Callback_RunCoreLoop+0xc45 40 CoreMessaging!Microsoft::CoreUI::Dispatch::UserAdapterBase::DrainCoreMessagingQueue+0x14d 41 CoreMessaging!Microsoft::CoreUI::Dispatch::UserAdapter::OnUserDispatch+0x1d7 42 CoreMessaging!Microsoft::CoreUI::Dispatch::UserAdapter::OnUserDispatchRaw+0x9c 43 CoreMessaging!Microsoft::CoreUI::Dispatch::UserAdapter_DoWork+0xe9 44 CoreMessaging!Microsoft::CoreUI::Dispatch::UserAdapter_WindowProc+0xa3 45 minuser!Core::Yield::WndProc+0x6b 46 minuser!Core::Window::DeliverMessage+0x30f 47 minuser!Input::EventMessageDeliveryManager::OnDispatchNotify+0x27 48 minuser!Input::EventMessageDeliveryManager$R::Delegate0+0x2b 49 minuser!Core::OnWindowEventMessage::Invoke+0x65 4a minuser!Input::EventMessageDeliveryManager::CheckAndConsumeMinQEventMessage+0x193 4b minuser!Input::InputQueue::PeekForInput+0x2f0 4c minuser!Core::ThreadInfo::ReadMessageEntryWorker+0x155 4d minuser!Core::ThreadInfo::ReadMessageEntry+0x50 4e minuser!Core::Api::PeekMessageAW+0x88 4f minuser!minPeekMessageAW+0x66 50 Windows_UI!Windows::UI::Core::CDispatcher::ProcessMessage+0xdf 51 Windows_UI!Windows::UI::Core::CDispatcher::WaitAndProcessMessagesInternal+0xc9 52 Windows_UI!Windows::UI::Core::CDispatcher::ProcessEvents+0x132 53 Windows_UI_Xaml!DllGetActivationFactory+0xbaee0 54 Windows_UI_Xaml!DllGetActivationFactory+0xbae7f 55 twinapi_appcore!Windows::ApplicationModel::Core::CoreApplicationView::Run+0x3a 56 twinapi_appcore!<lambda_643db08282a766b00cec20194396f531>::operator()+0xf2 57 shcore!_WrapperThreadProc+0xfb 58 ntdll!RtlUserThreadStart+0x2f
这是一个巨大的堆栈,但它可以整洁地分成三部分。
靠近堆栈顶部的部分是记录Ro-Get-Activation-Factory中失败的运行时。
靠近底部的部分是导致Ro-Get-Activation-Factory失败的代码。看起来是由计时器触发的。
看看我们想激活什么。
0:008> .frame 11 11 0000004b`0a1fd8d0 00007fff`7c209536 SharedLibrary!$8_FactoryCache.GetActivationFactoryInternal+0x5e 0:008> dv typeName = 0x00007fff`7ce64060 typeInfo = 0x0000004b`0a1fdb58 currentContext = 0x00000219`000109b0 pFactory = struct System::IntPtr itfGuid = struct System::Guid 0:008> ?? typeName class System::String * 0x00007fff`7ce64060 +0x000 __VFN_table : 0x00007fff`7c554030 +0x000 m_pEEType : System::IntPtr +0x008 m_stringLength : 0n38 +0x00c m_firstChar : 0x57 'W' 0:008> du 0x00007fff`7ce64060+c 00007fff`7ce6406c "Windows.Devices.Portable.Storage" 00007fff`7ce640ac "Device"
这个程序试图创建一个Windows.Devices.Portable.StorageDevice。我打赌它失败了。
⟦g-命令反向执行前设置参数指定的临时断点⟧ 0:008> g- combase!RoGetActivationFactory Time Travel Position: 1CD829:AE combase!RoGetActivationFactory: 00007fff`c29ca2f0 4d8bc8 mov r9,r8 0:008> dv activatableClassId = 0x0000004b`0a1fd860 "Windows.Devices.Portable.StorageDevice" iid = 0x0000004b`0a1fd920 {5ECE44EE-1B23-4DD2-8652-BC164F003128} factory = 0x0000004b`0a1fd900 ⟦gu命令运行直到函数返回 ("go up")⟧ 0:008> gu Time Travel Position: 1CE201:20 SharedLibrary!$8_Interop::WinRT.RoGetActivationFactory+0x136: 00007fff`7c209836 488b4db0 mov rcx,qword ptr [rbp-50h] ss:0000004b`0a1fd7f0=000002197408c8e0 0:008> r rax=0000000080040154 rbx=00007fff7ce64060 rcx=fff83836ff080000 rdx=0000000000000006 rsi=0000004b0a1fd920 rdi=0000004b0a1fd900 rip=00007fff7c209836 rsp=0000004b0a1fd7c0 rbp=0000004b0a1fd840 r8=0000000000000001 r9=0000000000000001 r10=0000000000005dc0 r11=0000004b0a1fcee0 r12=00000000e409abda r13=000002197fabec70 r14=0000000000000000 r15=0000000000000000 iopl=0 nv up ei pl nz na po nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 SharedLibrary!$8_Interop::WinRT.RoGetActivationFactory+0x136: 00007fff`7c209836 488b4db0 mov rcx,qword ptr [rbp-50h] ss:0000004b`0a1fd7f0=000002197408c8e0
函数的返回值被放在rax寄存器中,我们看到它确实是0x80040154,也就是“Class not registered”。
Windows.Devices.Portable。Storage-Device类不是Universal协议的一部分。它位于一个单独的PortableDevice协议中。应用程序在其manifest中将自己标记为Universal,但它使用Universal协议之外的类,而没有首先验证协议是否可用。PortableDevice协议在桌面Windows上是可用的,这就是为什么它们在桌面Windows上运行时没有受到影响。但在Windows 10X上并没有PortableDevice协议,这就是为什么该应用程序会崩溃。
这个程序在Xbox上可能也会崩溃,因为Xbox也不支持PortableDevice合约。
所以这个应用程序在商店里有一个特殊的兼容性标志,上面写着,“是的,当这个应用程序说它是通用的,他们在撒谎。不要在非桌面系统上安装它。”
额外的闲聊:既然我们知道了答案是什么,我们回过头来就会意识到我们本可以更快地找到答案。错误消息“Class not registered”表示对Co-Create-Instance、Co-Get-Class-Factory、Ro-Activate-Instance或Co-Get-Activation-Factory的调用失败。我们可以让时序堆栈跟踪对象模型找到所有返回错误代码的调用:
0:008> dx -r2 @$currsession.TTD.Calls("combase!CoCreateInstance", "combase!CoGetClassFactory", "combase!RoActivateInstance", "combase!RoGetActivationFactory").Where(c => c.ReturnValue == (HRESULT)0x80040154) [0x97] EventType : 0x0 ThreadId : 0x2fc0 UniqueThreadId : 0xd TimeStart : 1CD829:AE TimeEnd : 1CE201:20 Function : combase!RoGetActivationFactory FunctionAddress : 0x7fffc29ca2f0 ReturnAddress : 0x7fff7c209836 ReturnValue : 0x80040154 (Class not registered) [Type: HRESULT] Parameters Boom, there’s the failure, at call number 0x97. Boom,出现了故障,调用地址是0x97。 现在,我们可以使用!tt命令跳转到该时间码,以查看对象是什么。 0:008> !tt 1CD829:AE Setting position: 1CD829:AE (4420.3130): Break instruction exception - code 80000003 (first/second chance not available) Time Travel Position: 1CD829:AE combase!RoGetActivationFactory: 00007fff`c29ca2f0 4d8bc8 mov r9,r8