Имитация нажатия кнопки Windows Froms ToolStrip в другом процессе

Я нашел дескриптор Windows Forms ToolStrip в другом приложении. (Имя окна — toolStrip1, имя класса — WindowsForms10.Window.8.app.0.378734a.)

Есть ли способ перечислить дочерние кнопки, найти кнопку по заголовку и имитировать нажатие кнопки? Кнопки не являются дочерними окнами, поэтому EnumChildWindows< /а> не работает.

Имитация щелчка мышью с постоянными координатами на самом ToolStrip — не очень хороший вариант, так как доступные кнопки и подписи к ним могут меняться.


person user1768761    schedule 12.12.2018    source источник
comment
Эти кнопки являются дочерними, но у них нет ручки. Используйте Автоматизацию пользовательского интерфейса, чтобы получить их.   -  person Jimi    schedule 12.12.2018
comment
Спасибо, это работает с автоматизацией пользовательского интерфейса. Кнопки перечислены, и вызов Invoke() для них имитирует нажатие кнопок.   -  person user1768761    schedule 13.12.2018
comment
Очень хорошо :) Если вы не знаете об этом, Visual Studio устанавливает утилиту проверки автоматизации пользовательского интерфейса 32/64-битную (обычно в C:\Program Files (x86)\Windows Kits\10\ bin\x64\inspect.exe). Этот инструмент позволяет просматривать все компоненты (и все их свойства), к которым может обращаться автоматизация пользовательского интерфейса. Кроме того, если у вас есть решение, опубликуйте ответ и отметьте его самостоятельно. Кому-то может быть полезно :)   -  person Jimi    schedule 13.12.2018


Ответы (1)


Как предложил @Jimi, решение заключается в использовании пользовательского интерфейса Автоматизация Windows API. Мне удалось найти кнопку, спустившись по дереву автоматизации пользовательского интерфейса элементов пользовательского интерфейса из окна рабочего стола к данному элементу, совпадая с именами элементов. Затем я вызвал Invoke, чтобы имитировать щелчок. Вот мой очень неструктурированный тестовый код C++11 в стиле C для демонстрационных целей, на случай, если кто-то еще сочтет его полезным. Вам понадобится ссылка на ole32.lib и oleaut32.lib.

#include <iostream>
#include <UIAutomation.h>

bool GetChildElementByName(IUIAutomationElement * * Child,
                           IUIAutomationElement * Parent,
                           const wchar_t * Name, IUIAutomation * UIAut);

int main(int argc, char * argv[])
{
    // Initialize COM
    switch ( CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED) )
    {
    case S_OK:
        break;
    case S_FALSE:
        CoUninitialize();
        // fall through
    default:
        std::cout << "CoInitializeEx error.\n";
        return 1;
    }
    // Create a CUIAutomation object and query IUIAutomation interface
    IUIAutomation * uiautomation;
    if ( CoCreateInstance(CLSID_CUIAutomation, nullptr, CLSCTX_INPROC_SERVER,
                          IID_IUIAutomation, reinterpret_cast<LPVOID *>(&uiautomation)) != S_OK )
    {
        std::cout << "CoCreateInstance error.\n";
        CoUninitialize();
        return 1;
    }

    // Get the desktop UI element
    IUIAutomationElement * desktop;
    if ( uiautomation->GetRootElement(&desktop) != S_OK )
    {
        std::cout << "GetRootElement error.\n";
        uiautomation->Release();
        CoUninitialize();
        return 1;
    }

    // Find a button element in a window inside a toolstrip.
    IUIAutomationElement * elem;
    // Fix memory leak...
    if
    (
        !GetChildElementByName(&elem, desktop, L"Thermo Scientific LabWriter  V4.6", uiautomation) ||
        !GetChildElementByName(&elem, elem, L"toolStrip1", uiautomation) ||
        !GetChildElementByName(&elem, elem, L"Print", uiautomation)
    )
    {
        desktop->Release();
        uiautomation->Release();
        CoUninitialize();
        return 1;
    }
    desktop->Release();

    // The invoke control pattern contains the Invoke method that can be used to click the button
    IUIAutomationInvokePattern * invokepattern;
    if ( elem->GetCurrentPattern(UIA_InvokePatternId,
                                 reinterpret_cast<IUnknown * *>(&invokepattern)) != S_OK )
    {
        std::cout << "GetCurrentPattern error.\n";
        elem->Release();
        uiautomation->Release();
        CoUninitialize();
        return 1;
    }
    if ( invokepattern == nullptr )
    {
        // Possibly element is not even a button, as it should have the invoke control pattern.
        std::cout << "Invoke pattern not present.\n";
        elem->Release();
        uiautomation->Release();
        CoUninitialize();
        return 1;
    }

    // Click the button
    if ( invokepattern->Invoke() != S_OK )
    {
        std::cout << "Button click failed.\n";
        invokepattern->Release();
        elem->Release();
        uiautomation->Release();
        CoUninitialize();
        return 1;
    }

    elem->Release();
    invokepattern->Release();
    uiautomation->Release();
    CoUninitialize();

    std::cout << "Done.\n";
    return 0;
}

bool GetChildElementByName(IUIAutomationElement * * Child,
                           IUIAutomationElement * Parent,
                           const wchar_t * Name, IUIAutomation * UIAut)
{
    // Create a condition that matches elements with name *Name
    IUIAutomationCondition * cond;
    // Parameter for the condition is a string-typed VARIANT containing the name.
    VARIANT name;
    VariantInit(&name);
    name.vt = VT_BSTR;
    name.bstrVal = SysAllocString(Name);
    if ( name.bstrVal == nullptr )
    {
        std::cout << "SysAllocString error.\n";
        return false;
    }
    if ( UIAut->CreatePropertyCondition(UIA_NamePropertyId, name, &cond) != S_OK )
    {
        std::cout << "CreatePropertyCondition error.\n";
        VariantClear(&name);
        return false;
    }
    VariantClear(&name);

    // Find the first child element satisfying the condition.
    if ( Parent->FindFirst(TreeScope_Children, cond, Child)  != S_OK )
    {
        std::cout << "FindFirst error.\n";
        cond->Release();
        return false;
    }
    cond->Release();
    if ( *Child == nullptr )
    {
        std::cout << "Child element \"";
        std::wcout << Name;
        std::cout << "\" not found.\n";
        return false;
    }
    // Child element found
    return true;
}
person user1768761    schedule 18.12.2018