如何在线程之间传递 IUIAutomationElement
How to pass an IUIAutomationElement between threads
我正在用 C++ 编写 Node.js 本机插件(使用 node-addon-api) to interact with Microsofts UIAutomation API。我正在尝试收听 Focus Events,包装导致事件的 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
的正确方法是什么?
根据我的阅读,我需要阻止 Microsoft 在非主线程停止时回收该对象。我相信要做到这一点,我需要获取并存储对该对象的引用,但还没有找到有关如何操作的任何文档。
为了跨线程传递来自 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++ 编写 Node.js 本机插件(使用 node-addon-api) to interact with Microsofts UIAutomation API。我正在尝试收听 Focus Events,包装导致事件的 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
的正确方法是什么?
根据我的阅读,我需要阻止 Microsoft 在非主线程停止时回收该对象。我相信要做到这一点,我需要获取并存储对该对象的引用,但还没有找到有关如何操作的任何文档。
为了跨线程传递来自 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
的线程拥有该对象及其内存。为了将它传递给另一个线程,您需要增加引用计数,否则线程将在停止后释放对象/内存。