ウィンドウが表示されたことをリアルタイムで知る、UI Automation編です。前回のMSAA編の続きです。

UI AutomationはMicrosoft Active Accessibility (MSAA)の後継として位置づけられています。Windows 7とともに登場し、Windows VistaとWindows XP SP3向けにバックポートされています。

今回のプログラムでは、コールバックをIUIAutomationEventHandlerインタフェースを実装するCOMオブジェクトとして提供しなければなりません。手抜きするため、過去記事参照カウントしないIUnknownのように参照カウントの実装をすっぽかしています。「良い子のみんなはまねしないでね」とは言いませんので、必要ならちゃんと参照カウント実装しましょう。

#include <iostream>
#include <windows.h>
#include <uiautomation.h>
#include <shlwapi.h>
#include <comdef.h>
 
_COM_SMARTPTR_TYPEDEF(IUIAutomation, __uuidof(IUIAutomation));
_COM_SMARTPTR_TYPEDEF(IUIAutomationElement, __uuidof(IUIAutomationElement));
_COM_SMARTPTR_TYPEDEF(IUIAutomationCacheRequest, __uuidof(IUIAutomationCacheRequest));
 
using _com_util::CheckError;
 
class Handler : public IUIAutomationEventHandler
{
public:
  Handler() = default;
  Handler(const Handler&) = delete;
  Handler& operator=(const Handler&) = delete;
  ~Handler() = default;
 
  IFACEMETHOD(QueryInterface)(
    _In_ REFIID riid, _COM_Outptr_ void** ppv) override
  {
    static const QITAB qit[] = {
      {
        &__uuidof(IUIAutomationEventHandler),
        OFFSETOFCLASS(IUIAutomationEventHandler, Handler),
      },
      {},
    };
    return QISearch(this, qit, riid, ppv);
  }
  IFACEMETHOD_(ULONG, AddRef)() override { return 0; }
  IFACEMETHOD_(ULONG, Release)() override { return 0; }
  IFACEMETHOD(HandleAutomationEvent)(
    _In_ IUIAutomationElement* sender, EVENTID eventId) override
  {
    CONTROLTYPEID controlType{};
    if (SUCCEEDED(sender->get_CachedControlType(&controlType)))
    {
      if (controlType == UIA_WindowControlTypeId)
      {
        UIA_HWND hwnd{};
        if (SUCCEEDED(sender->get_CachedNativeWindowHandle(&hwnd)))
        {
          char text[256]{};
          GetWindowText(static_cast<HWND>(hwnd), text, ARRAYSIZE(text));
          std::cout << text << std::endl;
        }
      }
    }
    return S_OK;
  }
};
 
namespace {
  DWORD mainThreadId;
}
 
int main()
{
  mainThreadId = GetCurrentThreadId();
  SetConsoleCtrlHandler([](DWORD controlType)
  {
    PostThreadMessage(mainThreadId, WM_QUIT, 0, 0);
    return TRUE;
  }, TRUE);
 
  CheckError(CoInitialize(nullptr));
  {
    IUIAutomationPtr uia(__uuidof(CUIAutomation));
 
    IUIAutomationElementPtr root;
    CheckError(uia->GetRootElement(&root));
 
    IUIAutomationCacheRequestPtr cacheRequest;
    CheckError(uia->CreateCacheRequest(&cacheRequest));
    CheckError(cacheRequest->AddProperty(UIA_ControlTypePropertyId));
    CheckError(cacheRequest->AddProperty(UIA_NativeWindowHandlePropertyId));
 
    Handler handler;
    CheckError(uia->AddAutomationEventHandler(UIA_Window_WindowOpenedEventId,
      root, TreeScope_Descendants, cacheRequest, &handler));
 
    MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0))
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
    CheckError(uia->RemoveAutomationEventHandler(UIA_Window_WindowOpenedEventId,
      root, &handler));
  }
  CoUninitialize();
}

こちらはIUIAutomation::AddAutomationEventHandlerがイベント登録のメソッドになります。

IUIAutomationElementから値を取り出すには、あらかじめCache requestとして欲しい項目を伝えてあげないといけないというのが面白いです。おそらく性能のためでしょう。

スポンサード リンク

この記事のカテゴリ

  • ⇒ ウィンドウの表示のイベント通知を受け取る(UI Automation編)
  • ⇒ ウィンドウの表示のイベント通知を受け取る(UI Automation編)