potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++20&Win API&WIL IUIAutomationElementを列挙する

COMとWILを使ってIUIAutomationElementを列挙するコードです。

実行例(ExplorerPatcher起動中)

Source.cpp

#include <format>
#include <iostream>
#include "string_util.hpp"

#define STRICT
#define NOMINMAX
#include <Windows.h>
#include <UIAutomation.h>
#include "stl_win.hpp"

#include "wil/com.h"

int main()
{
    std::wcout.imbue(std::locale("", std::locale::ctype));

    auto coinit = wil::CoInitializeEx(COINIT_APARTMENTTHREADED);
    auto uiAutomation = wil::CoCreateInstance<CUIAutomation, IUIAutomation>(CLSCTX_INPROC_SERVER);

    auto hwnds = GetWindowHandles();
    std::wcout << L"WindowHandle, ClassName, WindowText, UIAutomation ElementName, ClassName" << std::endl;
    for (auto hwnd : hwnds)
    {
        wil::com_ptr<IUIAutomationElement> uiAutoElem;
        if (FAILED(uiAutomation->ElementFromHandle(hwnd, uiAutoElem.put())))
            continue;

        wil::unique_bstr name, className;
        uiAutoElem->get_CurrentName(name.put());
        uiAutoElem->get_CurrentClassName(className.put());

        std::wcout
            << std::format(L"{}, {}, {}, {}, {}",
                pad_start(to_wstring_hex_uc(std::bit_cast<size_t>(hwnd)), MAX_SIZE_T_HEX_LENGTH, L'0'),
                GetClassNameW(hwnd),
                GetWindowTextW(hwnd),
                wil::string_get_not_null(name),
                wil::string_get_not_null(className))
            << std::endl;
    }

    return 0;
}

string_util.hpp

#pragma once

#include <string>

const size_t MAX_SIZE_T_HEX_LENGTH = sizeof(size_t) * 2;

constexpr std::wstring to_wstring_hex_uc(size_t x)
{
    std::wstring s;
    for (; x != 0; x /= 16)
    {
        auto m = x % 16;
        s.push_back(static_cast<wchar_t>((m < 10 ? L'0' : L'A' - 10) + m));
    }
    return s;
}

constexpr std::wstring pad_start(std::wstring_view source, size_t length, wchar_t fill)
{
    if (source.size() >= length)
        return std::wstring(source.cbegin(), source.cend());
    std::wstring s;
    s.reserve(length);
    s.append(length - source.size(), fill);
    s.append(source);
    return s;
}

stl_win.hpp

#pragma once

#include <bit>
#include <string>
#include <vector>

BOOL CALLBACK GetWindowHandlesWorker(HWND hwnd, LPARAM lParam)
{
    auto& v = *std::bit_cast<std::vector<HWND>*>(lParam);
    v.emplace_back(hwnd);
    EnumChildWindows(hwnd, GetWindowHandlesWorker, lParam);
    return TRUE;
}

std::vector<HWND> GetWindowHandles()
{
    std::vector<HWND> v;
    EnumWindows(GetWindowHandlesWorker, std::bit_cast<LPARAM>(std::addressof(v)));
    return v;
}

std::wstring GetClassNameW(HWND hwnd)
{
    std::wstring s;
    for (int size = 0xff; ; size += 0xff)
    {
        s.resize(size);
        auto copied = ::GetClassNameW(hwnd, s.data(), size + 1);
        if (copied != 0)
        {
            s.resize(copied);
            s.shrink_to_fit();
            break;
        }
        else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
        {
            return {};
        }
    }
    return s;
}

std::wstring GetWindowTextW(HWND hwnd)
{
    auto len = GetWindowTextLengthW(hwnd);
    if (len == 0)
        return {};

    std::wstring s(len, L'\0');
    if (GetWindowTextW(hwnd, s.data(), len + 1) == 0)
        return {};
    return s;
}