我正在用 C++ 编写一个 Node.js 原生插件(使用node-addon-api)来与Microsoft 的 UIAutomation API交互。我正在尝试收听焦点事件,包装IUIAutomationElement
导致事件的事件并将包装的元素传递给 javascript。
我可以附加一个事件侦听器(按照处理焦点事件的示例),它成功接收焦点事件和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",
0,
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);
我试过的
El
并且passedEl
具有相同的内存地址,所以我相信IUIAutomationElement
当非主线程停止时它会失效。callback.NonBlockingCall
与其他变量(int
,string
, 自定义类)完美配合
我的问题是IUIAutomationElement
在线程之间传递的正确方法是什么?
根据我的阅读,我需要阻止微软在非主线程停止时回收对象。我相信要做到这一点,我需要获取并存储对该对象的引用,但没有找到任何有关如何操作的文档。