如何在线程之间传递IUIAutomationElement

问题描述

我正在用C ++(使用node-addon-api)编写一个Node.js本机插件,以与Microsofts UIAutomation API进行交互。我正在尝试收听Focus Events,包装引起事件的IUIAutomationElement并将包装的元素传递给javascript。

我可以附加一个事件侦听器(在此示例后为 Handling Focus Events ),该侦听器可以成功接收焦点事件和IUIAutomationElement。但是,所有UIAutomation事件侦听器都在单独的线程中运行

在UI自动化事件处理程序中进行UI自动化调用是安全的,因为始终在非UI线程上调用事件处理程序。 (请参阅:https://docs.microsoft.com/en-us/windows/win32/winauto/uiauto-threading)。

例如,在这里我将lambda函数传递给IUIAutomation::AddFocusChangedEventHandler方法周围的包装器。

this->automation_->SubscribeToFocusChange([callback,this](IUIAutomationElement* el){
    // This code here runs in a non-main thread
    // It gets the correct IUIAutomationElemenet
}

为了将IUIAutomationElement传递回Javascript,我需要将其传递到主线程。 node-addon-api提供了Napi::ThreadSafeFunction,旨在在线程之间传递变量。

Napi::ThreadSafeFunction callback = Napi::ThreadSafeFunction::New(
    env,info[0].As<Napi::Function>(),"Callback",1
);

this->automation_->SubscribeToFocusChange([callback,this](IUIAutomationElement* el){
    // Code running in non-main thread
    // el works here 
    callback.BlockingCall(el,[this](Napi::Env env,Napi::Function jsCallback,IUIAutomationElement* passedEl){
       // Code running in main thread
       // passedEl should be the same as el
    }
}

注意:此处info[0]是代表Javascript函数的函数参数。

问题在于,el工作时,现在在passedEl上运行的所有函数都会引发异常。

例如:

BSTR elControlType;
BSTR passedElcontrolType;

// Following works perfectly
HRESULT hr = this->el->get_CurrentLocalizedControlType(&controlType);

// This throws an exception and stops the program
HRESULT hr = this->passedEl->get_CurrentLocalizedControlType(&controlType);

我尝试过的事情

  1. ElpassedEl具有相同的内存地址,因此我相信在非主线程停止时IUIAutomationElement无效。

  2. callback.NonBlockingCall与其他变量(intstring,自定义类)完美配合

我的问题是在线程之间传递IUIAutomationElement的正确方法是什么?

根据我所读的内容,我需要阻止Microsoft在非主线程停止时回收该对象。我相信要做到这一点,我需要获取并存储对该对象的引用,但是没有找到有关该对象的任何文档。

解决方法

为了使IUIAutomation API中的实例跨线程传递,您需要保留一个强大的引用。 IUIAutomationElement基于IUnknown,因此可以使用IUnknown::AddRefhttps://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-addref)来完成。

这会将引用添加到引用计数,这意味着一旦创建对象的线程停止并因此不再保留该对象,该对象就不会失效。

最终释放对象及其内存也很重要,这可以通过IUnknown::Releasehttps://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-release)来完成。

这可以通过制作一个类似于 std :: shared_ptr 的包装程序来进行概括,该包装程序将有助于管理引用,但是我无法弄清楚如何做到这一点。

TL; DR:创建IUIAutomationElement的线程拥有该对象及其内存。为了将其传递给另一个线程,您需要增加引用计数,否则线程将在停止时释放对象/内存。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...