首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >COM UIA事件仅在注销后调用

COM UIA事件仅在注销后调用
EN

Stack Overflow用户
提问于 2017-01-25 05:50:08
回答 1查看 278关注 0票数 0

我通过C++/CLI接口使用MS (COM),而我的C#应用程序正在使用该C++/CLI接口(让我们将此接口/dll称为uiacpp)

我在uiacpp中创建了事件处理机制,主要遵循https://msdn.microsoft.com/en-us/library/windows/desktop/ff625914(v=vs.85).aspx中的示例。

我面临的问题是,我注册到UIA的事件处理程序只有在我注销相同的事件(每次都是相同的/不同的事件/事件类型和测试)之后才会调用。当我注册事件时,我可以看到我的event类的QueryInterface方法被调用了两次,显然是从UIA调用的,所以UIA用它做了一些事情。然后,我在测试中触发事件,但什么也没有发生。当我注销事件时,QueryInterface又被调用了几次,然后调用了事件处理程序,然后调用release方法来清理由UIA创建的其余引用(目前大约有6个)。

代码如下:

C++/CLI类:

代码语言:javascript
复制
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;
    }
};

下面是一个ref (托管c++)类方法,C#调用它来注册事件(通过使用最后的代码):

代码语言:javascript
复制
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指针,它是由以下人员创建的:

代码语言:javascript
复制
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#调用:

使用uiacpp的automationhandler类实现:

代码语言:javascript
复制
    class AutomationHandler : IUIAutomationEventHandler
    {
        public AutomationHandler()
        {
        }

        public void HandleAutomationEvent(IUIAutomationElement sender, int eventId)
        {
            Console.WriteLine("IUIAutomationEventHandler called");
        }
    }

和C#寄存器/取消寄存器线:

代码语言:javascript
复制
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);
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-01-26 03:55:30

这些COM事件通过windows消息循环调度。

再加上您没有在register和unregister之间传递消息的事实,导致事件被延迟,直到您注销并返回到主消息循环。

一种解决方案是使用await Task.Delay而不是阻塞休眠。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41839601

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档