我正在用 C++ 编写一个 Node.js 原生插件(使用 node-addon-api )来与 Microsofts UIAutomation API 交互.我正在尝试收听焦点事件,包装 IUIAutomationElement
这导致了事件并将包装的元素传递给javascript。
我可以附加一个成功接收焦点事件和 IUIAutomationElement
的事件监听器(按照 Handling Focus Events 的示例) .但是,所有 UIAutomation 事件监听器都在单独的线程中运行
It is safe to make UI Automation calls in a UI Automation event handler, because the event handler is always called on a non-UI thread. (see: 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
的正确方法是什么?线程之间?根据我的阅读,我需要阻止微软在非主线程停止时回收对象。我相信要做到这一点,我需要获取并存储对该对象的引用,但没有找到任何有关如何操作的文档。
最佳答案
为了来自 IUIAutomation API
的实例要跨线程传递,您需要保持强引用。 IUIAutomationElement
基于 IUnknown
所以这可以使用 IUnknown::AddRef
来完成(https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-addref)。
这添加了对引用计数的引用,这意味着一旦创建它的线程停止并因此停止持有它,该对象就不会失效。
最终释放对象及其内存也很重要,这可以通过 IUnknown::Release
来完成。 (https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-release)。
这可以通过制作像 这样的包装器来概括。 std::shared_ptr 这将有助于管理引用,但是我无法弄清楚如何做到这一点。
TL;DR: 创建 IUIAutomationElement
的线程拥有该对象及其内存。为了将它传递给另一个线程,您需要增加引用计数,否则线程一旦停止就会释放对象/内存。
关于c++ - 如何在线程之间传递 IUIAutomationElement,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64644006/