一个UWP应用在Windows 10X上启动时崩溃的案例

原文: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

这个参数是IUnhandled­Error­Detected­Event­Args,它是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, 这是一个CRestricted­Error对象. 我们 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