COM UIA 事件仅在注销后调用
COM UIA event called only after unregistering
我正在通过 C++/CLI 接口使用 MS UIA (COM),而我的 C# 应用程序正在使用该 C++/CLI 接口(我们称此 interface/dll 为 uiacpp)
我主要按照 https://msdn.microsoft.com/en-us/library/windows/desktop/ff625914(v=vs.85).aspx
中的示例在 uiacpp 中创建了事件处理机制
我面临的问题是,我注册到 UIA 的事件处理程序仅在我注销同一事件(每次相同/不同事件/事件类型和测试)后才被调用。当我注册该事件时,我可以看到我的事件 class 的 QueryInterface 方法被调用了两次,显然是来自 UIA,所以 UIA 对它做了一些事情。然后我在测试中触发事件,但没有任何反应。在我取消注册事件的那一刻,QueryInterface 被调用了几次,然后调用了事件处理程序,然后 release 方法被调用以获取 UIA 进行的剩余引用(此时大约还有 6 个)来清理东西。
代码如下:
C++/CLI class:
class CppUIAutomationEventHandler :
public ::IUIAutomationEventHandler
{
private:
LONG _refCount;
public:
int _eventCount;
gcroot<UIAMan::IUIAutomationEventHandler^> myHandler;
static std::list<IUIAutomationEventHandler*> *EventRegister;
// Constructor.
CppUIAutomationEventHandler() : _refCount(1), _eventCount(0)
{
}
// Constructor.
CppUIAutomationEventHandler(
UIAMan::IUIAutomationEventHandler^ aHandler)
: _refCount(1)
, _eventCount(0)
, myHandler(aHandler)
{
}
// IUnknown methods.
ULONG STDMETHODCALLTYPE AddRef()
{
ULONG ret = InterlockedIncrement(&_refCount);
return ret;
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG ret = InterlockedDecrement(&_refCount);
if (ret == 0)
{
delete this;
return 0;
}
return ret;
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppInterface)
{
if (riid == __uuidof(IUnknown) || riid == __uuidof(IUIAutomationEventHandler))
*ppInterface = static_cast<IUIAutomationEventHandler*>(this);
else
{
*ppInterface = NULL;
return E_NOINTERFACE;
}
this->AddRef();
return S_OK;
}
// IUIAutomationEventHandler methods
HRESULT STDMETHODCALLTYPE HandleAutomationEvent(::IUIAutomationElement * pSender, EVENTID eventID)
{
_eventCount++;
myHandler->HandleAutomationEvent(gcnew CUIAutomationElement(pSender, false), eventID);
return S_OK;
}
};
这是 C# 调用以注册事件的 ref(托管 C++)class 方法(通过使用末尾的最后一个代码):
void CUIAutomation::AddAutomationEventHandler(
int eventId
, IUIAutomationElement^ element
, TreeScope scope
, IUIAutomationCacheRequest^ cacheRequest
, IUIAutomationEventHandler^ handler)
{
::IUIAutomationElement* el = safe_cast<CUIAutomationElement^>(element)->getElement();
::IUIAutomationEventHandler* _handler = new CppUIAutomationEventHandler(handler);
LastHResult = puia->AddAutomationEventHandler(
eventId
, el
, (::TreeScope)(int)scope
, (cacheRequest != nullptr) ? ((CUIAutomationCacheRequest^)cacheRequest)->getElement() : NULL
, _handler);
CppUIAutomationEventHandler::EventRegister->push_back(_handler);
};
我正在使用一个处理程序列表来注销它们。 puia 也是一个 COM 指针,由以下人员创建:
CUIAutomation::CUIAutomation()
{
CoInitializeEx(NULL, COINIT_MULTITHREADED);
::IUIAutomation* _puia;
HRESULT hr = CoCreateInstance(CLSID_CUIAutomation, NULL,
CLSCTX_INPROC_SERVER, IID_IUIAutomation,
(void**)&_puia);
if (SUCCEEDED(hr))
puia = _puia;
}
最后,这是 C# 调用:
automationhandler class 使用 uiacpp 的实现:
class AutomationHandler : IUIAutomationEventHandler
{
public AutomationHandler()
{
}
public void HandleAutomationEvent(IUIAutomationElement sender, int eventId)
{
Console.WriteLine("IUIAutomationEventHandler called");
}
}
和 C# 注册/注销行:
var aHandler = new AutomationHandler();
uia.AddAutomationEventHandler(UIA_EventIds.UIA_Window_WindowOpenedEventId, uia.GetRootElement(), TreeScope.TreeScope_Subtree, null, aHandler);
// for debugging
bool loop = true;
while(loop)
{
Thread.Sleep(500);
}
uia.RemoveAutomationEventHandler(UIA_EventIds.UIA_Window_WindowOpenedEventId, uia.GetRootElement(), aHandler);
这些 COM 事件是通过 windows 消息循环调度的。
再加上您不在注册和取消注册之间发送消息这一事实,导致事件被延迟,直到您取消注册并return进入主消息循环。
一种解决方案是使用 await Task.Delay
而不是阻塞睡眠。
我正在通过 C++/CLI 接口使用 MS UIA (COM),而我的 C# 应用程序正在使用该 C++/CLI 接口(我们称此 interface/dll 为 uiacpp)
我主要按照 https://msdn.microsoft.com/en-us/library/windows/desktop/ff625914(v=vs.85).aspx
中的示例在 uiacpp 中创建了事件处理机制我面临的问题是,我注册到 UIA 的事件处理程序仅在我注销同一事件(每次相同/不同事件/事件类型和测试)后才被调用。当我注册该事件时,我可以看到我的事件 class 的 QueryInterface 方法被调用了两次,显然是来自 UIA,所以 UIA 对它做了一些事情。然后我在测试中触发事件,但没有任何反应。在我取消注册事件的那一刻,QueryInterface 被调用了几次,然后调用了事件处理程序,然后 release 方法被调用以获取 UIA 进行的剩余引用(此时大约还有 6 个)来清理东西。
代码如下:
C++/CLI class:
class CppUIAutomationEventHandler :
public ::IUIAutomationEventHandler
{
private:
LONG _refCount;
public:
int _eventCount;
gcroot<UIAMan::IUIAutomationEventHandler^> myHandler;
static std::list<IUIAutomationEventHandler*> *EventRegister;
// Constructor.
CppUIAutomationEventHandler() : _refCount(1), _eventCount(0)
{
}
// Constructor.
CppUIAutomationEventHandler(
UIAMan::IUIAutomationEventHandler^ aHandler)
: _refCount(1)
, _eventCount(0)
, myHandler(aHandler)
{
}
// IUnknown methods.
ULONG STDMETHODCALLTYPE AddRef()
{
ULONG ret = InterlockedIncrement(&_refCount);
return ret;
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG ret = InterlockedDecrement(&_refCount);
if (ret == 0)
{
delete this;
return 0;
}
return ret;
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppInterface)
{
if (riid == __uuidof(IUnknown) || riid == __uuidof(IUIAutomationEventHandler))
*ppInterface = static_cast<IUIAutomationEventHandler*>(this);
else
{
*ppInterface = NULL;
return E_NOINTERFACE;
}
this->AddRef();
return S_OK;
}
// IUIAutomationEventHandler methods
HRESULT STDMETHODCALLTYPE HandleAutomationEvent(::IUIAutomationElement * pSender, EVENTID eventID)
{
_eventCount++;
myHandler->HandleAutomationEvent(gcnew CUIAutomationElement(pSender, false), eventID);
return S_OK;
}
};
这是 C# 调用以注册事件的 ref(托管 C++)class 方法(通过使用末尾的最后一个代码):
void CUIAutomation::AddAutomationEventHandler(
int eventId
, IUIAutomationElement^ element
, TreeScope scope
, IUIAutomationCacheRequest^ cacheRequest
, IUIAutomationEventHandler^ handler)
{
::IUIAutomationElement* el = safe_cast<CUIAutomationElement^>(element)->getElement();
::IUIAutomationEventHandler* _handler = new CppUIAutomationEventHandler(handler);
LastHResult = puia->AddAutomationEventHandler(
eventId
, el
, (::TreeScope)(int)scope
, (cacheRequest != nullptr) ? ((CUIAutomationCacheRequest^)cacheRequest)->getElement() : NULL
, _handler);
CppUIAutomationEventHandler::EventRegister->push_back(_handler);
};
我正在使用一个处理程序列表来注销它们。 puia 也是一个 COM 指针,由以下人员创建:
CUIAutomation::CUIAutomation()
{
CoInitializeEx(NULL, COINIT_MULTITHREADED);
::IUIAutomation* _puia;
HRESULT hr = CoCreateInstance(CLSID_CUIAutomation, NULL,
CLSCTX_INPROC_SERVER, IID_IUIAutomation,
(void**)&_puia);
if (SUCCEEDED(hr))
puia = _puia;
}
最后,这是 C# 调用:
automationhandler class 使用 uiacpp 的实现:
class AutomationHandler : IUIAutomationEventHandler
{
public AutomationHandler()
{
}
public void HandleAutomationEvent(IUIAutomationElement sender, int eventId)
{
Console.WriteLine("IUIAutomationEventHandler called");
}
}
和 C# 注册/注销行:
var aHandler = new AutomationHandler();
uia.AddAutomationEventHandler(UIA_EventIds.UIA_Window_WindowOpenedEventId, uia.GetRootElement(), TreeScope.TreeScope_Subtree, null, aHandler);
// for debugging
bool loop = true;
while(loop)
{
Thread.Sleep(500);
}
uia.RemoveAutomationEventHandler(UIA_EventIds.UIA_Window_WindowOpenedEventId, uia.GetRootElement(), aHandler);
这些 COM 事件是通过 windows 消息循环调度的。
再加上您不在注册和取消注册之间发送消息这一事实,导致事件被延迟,直到您取消注册并return进入主消息循环。
一种解决方案是使用 await Task.Delay
而不是阻塞睡眠。