在独立线程中调用GetFocusedElement时崩溃的问题求助
我开发了一个基于Electron的应用,配套使用MSVC编译、CMakeJS链接的C++原生Node模块。模块通过SetWindowsHookEx在独立线程中安装了鼠标点击(WH_MOUSE_LL)与按键按下(WH_KEYBOARD_LL)的钩子回调,每当用户点击或按键时会调用GetFocusedElement;若返回UIA_E_ELEMENTNOTAVAILABLE,会重试三次,每次间隔Sleep(10)。目前调用该接口时出现致命崩溃,查阅微软官方文档未找到GetFocusedElement的特定前置条件,希望能避免崩溃,哪怕无法获取焦点元素也可。
错误追踪
OS Version: Windows 10.0.19044 (3086) Report Version: 104 Crashed Thread: 10164 Application Specific Information: Fatal Error: EXCEPTION_ACCESS_VIOLATION_READ / 0xffffffffffffffff Thread 10164 Crashed: 0 uiautomationcore.dll 0x7fffb46cdfe2 LocalUiaNodeProxy::RealRemoteNodeFromProviderEntryPoint 1 uiautomationcore.dll 0x7fffb46fb1fb LocalUiaNodeProxy::RemoteNodeFromProviderEntryPoint 2 uiautomationcore.dll 0x7fffb46fb15e LocalUiaNodeProxy::RemoteNodeFromProviderEntryPoint 3 uiautomationcore.dll 0x7fffb47267c5 UiaNodeFactory::InProcProxyFromHwnd 4 uiautomationcore.dll 0x7fffb471a47a ClientProxyManager::InternalGetProxyProvider 5 uiautomationcore.dll 0x7fffb471a087 ClientProxyManager::GetProxyProvider 6 uiautomationcore.dll 0x7fffb4709767 UiaNodeFactory::CompleteNodeInfo 7 uiautomationcore.dll 0x7fffb4708289 UiaNodeFactory::FromPartialNodeInfo 8 uiautomationcore.dll 0x7fffb472643c UiaNodeFactory::FromLocalProvider 9 uiautomationcore.dll 0x7fffb472654f UiaNodeFactory::FromLocalProvider 10 uiautomationcore.dll 0x7fffb46e8b4f UiaNode::ProviderDrillForPointOrFocus 11 uiautomationcore.dll 0x7fffb46e84e4 UiaNode::ProviderDrillForPointOrFocusInternal 12 uiautomationcore.dll 0x7fffb46e83f1 InProcClientAPIStub::UiaNode_DrillForPointOrFocus 13 uiautomationcore.dll 0x7fffb46c60fd ComInvoker::CallTarget 14 uiautomationcore.dll 0x7fffb46fa998 InProcClientAPIStub::InvokeInProcAPI 15 uiautomationcore.dll 0x7fffb46be327 UiaNode::CrossProcess_DrillForPointOrFocus 16 uiautomationcore.dll 0x7fffb4778d7b CUIAutomation::GetFocusedElement 17 nativeapp.node 0x7fffdec1187d GetFocusedElementRetry (iohook.cc:182) 18 nativeapp.node 0x7fffdec1432f focusChangeHandler (iohook.cc:304) 19 nativeapp.node 0x7fffdec0fbfa <lambda>::operator() (iohook.cc:779) 20 nativeapp.node 0x7fffdec0c70d [inlined] std::invoke (type_traits:1564) 21 nativeapp.node 0x7fffdec0c70d std::thread::_Invoke<T> (thread:55) 22 ucrtbase.dll 0x7fffe3b91bb1 thread_start<T> 23 KERNEL32.DLL 0x7fffe48d7613 BaseThreadInitThunk 24 ntdll.dll 0x7fffe63c26f0 RtlUserThreadStart
解决方案建议
1. 确保COM线程模型正确初始化
GetFocusedElement属于UI Automation COM接口,调用前必须在当前线程初始化COM环境。由于是在独立线程中调用,需确保线程启动时调用CoInitializeEx(NULL, COINIT_APARTMENTTHREADED),退出时调用CoUninitialize()。注意每个线程都要单独初始化,且不能重复初始化。
2. 捕获Win32异常
崩溃是未处理的访问违例,可通过__try/__except块包裹GetFocusedElement调用,直接捕获异常并返回失败,避免进程崩溃:
HRESULT hr; IUIAutomationElement* pElement = nullptr; __try { hr = pAutomation->GetFocusedElement(&pElement); } __except(EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
3. 调整重试逻辑的时机
当前重试仅针对UIA_E_ELEMENTNOTAVAILABLE,但崩溃发生在接口内部,可能是目标进程正在退出或窗口句柄失效。可在重试前先检查当前焦点窗口的有效性:
HWND focusedHwnd = GetForegroundWindow(); if (focusedHwnd == nullptr || !IsWindow(focusedHwnd)) { return nullptr; // 窗口无效,直接跳过 }
4. 避免在钩子回调线程直接调用UI Automation
WH_MOUSE_LL和WH_KEYBOARD_LL属于低级别钩子,回调线程是系统注入的线程(或你的独立线程),直接调用跨进程的COM接口容易引发线程同步问题。建议将获取焦点元素的任务投递到应用的主线程(或专用的COM线程)处理,比如用PostMessage或线程队列异步执行。
5. 检查UI Automation对象的生命周期
确保IUIAutomation实例是在正确的线程创建,且未被提前释放。如果是在主线程创建的实例,跨线程调用可能导致崩溃,需在调用线程单独创建IUIAutomation对象。
内容的提问来源于stack exchange,提问作者Gaurang Tandon

