Udostępnij za pośrednictwem


MFC application crashes in Windows 8 touch Devices.

Recently we had a chance to debug an issue where an application was only crashing in Windows 8 Touch devices , it crashed with EXCEPTION_CODE: (NTSTATUS) 0xc000041d - An unhandled exception was encountered during a user callback. . As usual we had taken full memory dumps for the application . From the dumps I saw that the crash was coming from tiptsf which is Touch Keyboard and Handwriting Panel Text Services Framework. On more search I found out an immediate work around which is to manually stop Touch Keyboard and Handwriting Panel Service . However if we open on screen keyboard, the service would automatically start and the application would crash again. For doing this,
Go through the following steps:

  1. Go to Manage --> Services and Application-->Services`.
  2. Double click on Touch Keyboard and Handwriting Panel Service.
  3. In Startup type .. .
  4. Click the small arrow and go to Manual.
  5. Click stop service.

We do not recommend you to stop the service rather go for a code addition which would be a permanent and a better solution . Now coming back to the reason this blog was pursued .

Since we had the repro , We collected time travel traces (available internally) and did some analysis. Before I proceed with the debugging startegy used to debug here , I want to point out the resolution.

The resolution is to implement the below functions in your application specifically in CFormView derivative.

acchittest
acclocation
get_accDescription
get_accHelp
get_accKeyboardShortcut
get_accName
get_accRole
get_accSelection
get_accState
get_accValue

Here is the link which speaks about the acclocation and all the above functions , msdn.microsoft.com/en-us/library/windows/desktop/dd318472(v=vs.85).aspx .
Please override them in CFormView derivative. After overriding the functions , our application stopped crashing on Windows 8 Touch Devices .

If you are interested in knowing how we debugged it , continue reading .
Below is the call stack for the crash . We also see that oleacc!AccWrap_Base::accLocation is happening which further leads to a _purecall. In case you are wondering what is a _purecall , please refer msdn.microsoft.com/en-us/library/ff798096.aspx . Essentially it will be called if any function is not implemented in the derived class. The blog gives you good examples on when we see such behvior.

ChildEBP RetAddr
010df060 773a0f7b kernelbase!RaiseException
010df074 783edeb8 msvcrt!_purecall+0x11
010df0a8 783ee21e mfc90!CWnd::accLocation+0x28
010df0fc 00872079 mfc90!CWnd::GetAccessibilityLocation+0x31
010df120 783ed8e5 viewswitch!CFormView::accLocation+0x39
010df174 783ef675 mfc90!CWnd::XAccessible::accLocation+0x43
010df1a8 7490269e mfc90!ATL::IAccessibleProxyImpl<ATL::CAccessibleProxy>::accLocation+0x53
(Inline) -------- oleacc!AccWrap_Base::accLocation+0x49
010df2c0 73db6375 oleacc!AccWrap_LocationEtcFix::accLocation+0x65
010df344 73db658d tiptsf!CARET::UpdateMSAAEditFieldState+0x81
010df37c 73db3a59 tiptsf!CARET::UpdateEditFieldState+0x31
010df820 73db1b2a tiptsf!CARET::_ProcessCaretEvents+0x34e
010df840 7672a259 tiptsf!CARET::ProcessCaretEvents+0x69
010df880 77a92c92 user32!__ClientCallWinEventProc+0x2e
010df888 77a92c92 ntdll!KiUserCallbackDispatcher+0x2e
010df8b4 783eb0ee ntdll!KiUserCallbackDispatcher+0x2e
010df8c4 00871643 mfc90!CWnd::DestroyWindow+0x4e
010df8fc 00871907 viewswitch!CViewSwitchApp::CreateView+0xc3
010df924 0087150e viewswitch!CViewSwitchApp::GoToView2+0xa7
010df954 00871338 viewswitch!CViewSwitchApp::InitApp+0xce
010df9b0 783f71c7 viewswitch!CViewSwitchApp::InitInstance+0x118
010df9c4 00872c6e mfc90!AfxWinMain+0x49
010dfa54 774e8543 viewswitch!__tmainCRTStartup+0x140
010dfa60 77aabf39 kernel32!BaseThreadInitThunk+0xe
010dfaa4 77aabf0c ntdll!__RtlUserThreadStart+0x72
010dfabc 00000000 ntdll!_RtlUserThreadStart+0x1b

Let’s dump cwnd and we can see that m_pStdobject is a member of CWND .

0:000> dt cwnd
viewswitch!CWnd
   +0x000 __VFN_table : Ptr32
   =00870000 classCObject : CRuntimeClass
   =00870000 classCCmdTarget : CRuntimeClass
   =00870000 _commandEntries : [0] AFX_OLECMDMAP_ENTRY
   =00870000 commandMap : AFX_OLECMDMAP
   =00870000 _dispatchEntries : [0] AFX_DISPMAP_ENTRY
   =00870000 _dispatchEntryCount : Uint4B
   =00870000 _dwStockPropMask : Uint4B
   =00870000 dispatchMap : AFX_DISPMAP
   =00870000 _connectionEntries : [0] AFX_CONNECTIONMAP_ENTRY
   =00870000 connectionMap : AFX_CONNECTIONMAP
   =00870000 _interfaceEntries : [0] AFX_INTERFACEMAP_ENTRY
   =00870000 interfaceMap : AFX_INTERFACEMAP
   =00870000 _eventsinkEntries : [0] AFX_EVENTSINKMAP_ENTRY
   =00870000 _eventsinkEntryCount : Uint4B
   =00870000 eventsinkMap : AFX_EVENTSINKMAP
   +0x004 m_dwRef : Int4B
   +0x008 m_pOuterUnknown : Ptr32 IUnknown
   +0x00c m_xInnerUnknown : Uint4B
   +0x010 m_xDispatch : CCmdTarget::XDispatch
   +0x014 m_bResultExpected : Int4B
   +0x018 m_xConnPtContainer : CCmdTarget::XConnPtContainer
   +0x01c m_pModuleState : Ptr32 AFX_MODULE_STATE
   =00870000 classCWnd : CRuntimeClass
   +0x020 m_hWnd : Ptr32 HWND__
   =00870000 wndTop : CWnd
   =00870000 wndBottom : CWnd
   =00870000 wndTopMost : CWnd
   =00870000 wndNoTopMost : CWnd
   +0x024 m_bEnableActiveAccessibility : Bool
   +0x028 m_pStdObject : Ptr32 IAccessible
   =00870000 m_pfnNotifyWinEvent : (null)
   +0x02c m_pProxy : Ptr32 IAccessibleProxy
   =00870000 _interfaceEntries : [0] AFX_INTERFACEMAP_ENTRY
   =00870000 interfaceMap : AFX_INTERFACEMAP
   +0x030 m_xAccessible : CWnd::XAccessible
   +0x034 m_xAccessibleServer : CWnd::XAccessibleServer
   +0x038 m_hWndOwner : Ptr32 HWND__
   +0x03c m_nFlags : Uint4B
   +0x040 m_pfnSuper : Ptr32 long
   =00870000 m_nMsgDragList : Uint4B
   +0x044 m_nModalResult : Int4B
   +0x048 m_pDropTarget : Ptr32 COleDropTarget
   +0x04c m_pCtrlCont : Ptr32 COleControlContainer
   +0x050 m_pCtrlSite : Ptr32 COleControlSite

So from above we know that IAccessible is part of Cwnd. CAccessible implements IAccessible, please refer to the source code of atlmfc which is available at C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\atlmfc .Let’s travel to the point at which the pure call is getting invoked.

eax=010df104 ebx=01539d18 ecx=01539d18 edx=038b5cab esi=010df114 edi=010df0d0
eip=783ee219 esp=010df0b0 ebp=010df0fc iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
mfc90!CWnd::GetAccessibilityLocation+0x2c:
783ee219 e872fcffff call mfc90!CWnd::accLocation (783ede90)

Let’s try to see if the function is implemented by CAccessible, details are below.

0:000> ?? this->m_pStdObject
struct IAccessible * 0x01376b58
+0x000 lpVtbl : 0x74902a88 IDispatchVtbl

Dumping out the vtable to see the functions which are overridden.

0:000> dds 0x74902a88
74902a88 749032a9 oleacc!CAccessible::QueryInterface
74902a8c 749033a4 oleacc!CProgressBar::AddRef
74902a90 74903085 oleacc!CAccessible::Release
74902a94 74929c35 oleacc!CAccessible::GetTypeInfoCount
74902a98 74929c60 oleacc!CAccessible::GetTypeInfo
74902a9c 74929cba oleacc!CAccessible::GetIDsOfNames
74902aa0 74929ce9 oleacc!CAccessible::Invoke
74902aa4 7491d390 oleacc!CAccessible::get_accParent
74902aa8 7491b87d oleacc!CAccessible::get_accChildCount
74902aac 74904713 oleacc!CAccessible::get_accChild
74902ab0 7491f9f1 oleacc!purecall
74902ab4 74929d22 oleacc!CAccessible::get_accDefaultAction
74902ab8 74929d22 oleacc!CAccessible::get_accDefaultAction
74902abc 7491f9f1 oleacc!purecall
74902ac0 7491f9f1 oleacc!purecall
74902ac4 74929d22 oleacc!CAccessible::get_accDefaultAction
74902ac8 74929d53 oleacc!CAccessible::get_accHelpTopic
74902acc 74929d22 oleacc!CAccessible::get_accDefaultAction
74902ad0 74929d8d oleacc!CAccessible::get_accFocus
74902ad4 74929d8d oleacc!CAccessible::get_accFocus
74902ad8 74929d22 oleacc!CAccessible::get_accDefaultAction
74902adc 74929b53 oleacc!CAccessible::accSelect
74902ae0 7491f9f1 oleacc!purecall
74902ae4 74929b90 oleacc!CAccessible::accNavigate
74902ae8 7491f9f1 oleacc!purecall
74902aec 74929c0d oleacc!CAccessible::accDoDefaultAction
74902af0 74929da6 oleacc!CAccessible::put_accValue
74902af4 74929da6 oleacc!CAccessible::put_accValue
74902af8 7490b099 oleacc!CListBoxFrame::`scalar deleting destructor'
74902afc 74903e7f oleacc!CAccessible::ValidateChild+0x41
74902b00 74903e3e oleacc!CAccessible::ValidateChild
74902b04 74929dce oleacc!CAccessible::GetPatternProvider

The above list does not have accLocation and other functions . Since we are seeing that our application is calling accocation after which it crashes with a _purecall. Once we implemented this function, application started crashing at some other functions which are listed below .Our suggestion would be to override acclocation and all other functions from the below link which are not implemented namely , 

acchittest
acclocation
get_accDescription
get_accHelp
get_accKeyboardShortcut
get_accName
get_accRole
get_accSelection
get_accState
get_accValue

Here is the link which speaks about the acclocation and all the above functions , msdn.microsoft.com/en-us/library/windows/desktop/dd318472(v=vs.85).aspx .
Please override them in CFormView derivative. After overriding the functions , our application stopped crashing on Windows 8 Touch Devices .

Comments

  • Anonymous
    October 31, 2013
    Think I have this same issue, customer is complaining one of our MFC applications crashes but I couldn't reproduce it on the Windows 8 VM that I have. The stack trace from the crash dump is very similar to what you have posted (I found this article by searching for CWnd::XAccessible::accLocation and have the same __purecall entries). Is there any way to recreate this behaviour without an actual Windows 8 touch device? We don't have any... I've tried enabling the on-screen keyboard and running the Touch Keyboard and Handwriting Panel Service on the VM I have for testing Windows 8 compatibility, but no luck.

  • Anonymous
    October 31, 2013
    Yes, Chris, this is NOT limited to touch devices.  The problem comes when Active Accessibility is expected and the form view has not implemented the required functions.  The resolution should be the same even though the cause might be different. Scot

  • Anonymous
    October 31, 2013
    Thanks Scot, how does one achieve this so that I can reproduce the issue? Is it limited to Windows 8 or can I enable something similar on Windows 7?

  • Anonymous
    December 02, 2013
    The real problem here seems to be that when CWnd::EnsureStdObj() calls CreateStdAccessibleObject in wincore.cpp, the OLEACC.DLL returns a CAccessible object that only partially implements IAccessible.  That call should either return an object that fully implements IAccessible or return a failure HRESULT. If the CWnd::m_pStdObject contains an object that fully implements IAccessible, there would be no reason for the form view to override these functions. Is there any update to OLEACC.DLL that returns a fully implemented IAccessible object?

  • Anonymous
    December 04, 2013
    The CFormView constructor calls EnableActiveAccessibility.  So, a simpler work-around than implementing all these functions is to set the m_bEnableActiveAccessibility member to false in the constructor for the derived class(es).

  • Anonymous
    December 16, 2013
    Tyler, you are our hero :-) I don't quite understand how MS can enable usage of this interface by default but then doesn't deliver a working implementation through OLEACC!CreateStdAccessibleObject().

  • Anonymous
    March 25, 2014
    Tyler, your fix is amazing! Thank you so much! I have zero experience with C++, and had no idea how to implement the functions. But the MFC application I am working is experiencing the same and your fix, well fixed it. Thanks again!