在另一个进程中模拟点击 Windows Froms ToolStrip 按钮
Simulate click on Windows Froms ToolStrip button in another process
我在另一个应用程序中找到了 Windows Forms ToolStrip 的句柄。
(Window 名称为 toolStrip1,class 名称为 WindowsForms10.Window.8.app.0.378734a。)
有没有办法枚举child按钮,通过标题找到按钮并模拟按钮点击?这些按钮不是 child windows,因此 EnumChildWindows 不起作用。
在 ToolStrip 本身上使用恒定坐标模拟鼠标单击并不是一个很好的选择,因为可用按钮和按钮标题可能会改变。
正如@Jimi 所建议的,解决方案是使用 UI Automation Windows API。我设法通过从桌面 window 到给定元素的 UI 元素的 UI 自动化树中找到按钮,匹配元素的名称。然后我对其调用 Invoke 以模拟点击。
这是我的一个高度非结构化的 "C-style" C++11 测试代码,用于演示目的,以防其他人发现它有用。您将需要 link 到 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;
}
我在另一个应用程序中找到了 Windows Forms ToolStrip 的句柄。 (Window 名称为 toolStrip1,class 名称为 WindowsForms10.Window.8.app.0.378734a。)
有没有办法枚举child按钮,通过标题找到按钮并模拟按钮点击?这些按钮不是 child windows,因此 EnumChildWindows 不起作用。
在 ToolStrip 本身上使用恒定坐标模拟鼠标单击并不是一个很好的选择,因为可用按钮和按钮标题可能会改变。
正如@Jimi 所建议的,解决方案是使用 UI Automation Windows API。我设法通过从桌面 window 到给定元素的 UI 元素的 UI 自动化树中找到按钮,匹配元素的名称。然后我对其调用 Invoke 以模拟点击。 这是我的一个高度非结构化的 "C-style" C++11 测试代码,用于演示目的,以防其他人发现它有用。您将需要 link 到 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;
}