我提出了一种不同的方法来让应用程序通信(在这种情况下,它是一种在途交互:只有一个应用程序与另一个应用程序通信)。
使用 UI 自动化,您可以在另一个应用程序中以半透明的方式获取或设置控件属性的值。还接收或引发事件,检测应用程序何时运行或关闭其他有趣的活动。此处的一般文档:
Windows Accessibility API reference - UI Automation
.Net Framework UI Automation Fundamentals
您有两个具有简单需求的简单应用程序,因此这项任务非常简单:
- 一个应用程序等待命令(或者它就在那里)。
- 另一个应用程序发送第一个应用程序需要解释和执行的命令。
由于您需要发送一些字符串来改变 PictureBox 的颜色,我们可以使用 TextBox 控件来接收字符串并将 命令 转换为 Color 或触发另一个预定义的行为.
使用 UI 自动化,两个应用程序可以独立运行,然后以不同的方式确认其他应用程序的存在。例如,使用WindowPattern.WindowOpenedEvent,我们可以检测应用程序何时运行并以不同的方式确定它是否有趣。有关实施,请参阅以下问题:
Run event when any Form loads
另一个问题,发给identify an application based on the content of a child control。
在这里(为了简短起见),我只是列举了正在运行的具有接口的应用程序,并使用 ComboBox 作为选择器来选择一个。
private void comboBox1_SelectionChangeCommitted(object sender, EventArgs e)
{
var window = AutomationElement.FromHandle((IntPtr)comboBox1.SelectedValue);
if (window != null) {
GetCommElement(window, ControlType.Edit);
}
}
private void GetCommElement(AutomationElement parent, ControlType controlType)
{
element = parent.FindFirst(TreeScope.Subtree,
new PropertyCondition(AutomationElement.ControlTypeProperty, controlType));
}
如果找到所选应用程序的 TextBox,我们将获取其 ValuePattern(允许设置控件值的 UI 自动化模式)并将其 Text 属性设置为对应于颜色名称的字符串:
(请注意,只有非多行编辑控件(WinForms TextBox)支持ValuePattern。多行编辑控件不支持,只有TextRangePattern)
private void btnColor_Click(object sender, EventArgs e)
{
if (element == null) return;
var ctrl = sender as Control;
if (element.TryGetCurrentPattern(ValuePattern.Pattern, out object pattern)) {
(pattern as ValuePattern).SetValue(ctrl.Text);
this.Activate();
}
}
接收命令的应用程序使用其 TextBox 控件的 TextChanged 事件从其他应用程序接收字符串并决定做什么:
(注意TextBox可以在屏幕外,但它的Visible属性必须设置为true)
private void textBox1_TextChanged(object sender, EventArgs e)
{
var color = Color.FromName((sender as Control).Text);
pictureBox1.BackColor = (color.IsKnownColor) ? color: Color.White;
}
示例功能:
两个应用程序的完整源代码:
UI 自动化需要引用这些程序集:UIAutomationClient 和 UIAutomationTypes
UIAClientApp:
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Automation;
public partial class UIAClientApp : Form
{
AutomationElement element = null;
private void comboBox1_DropDown(object sender, EventArgs e)
{
var dict = new Dictionary<IntPtr, string>();
foreach(var proc in Process.GetProcesses().Where(p => p.Id > 4 &&
p.MainWindowHandle != this.Handle &&
!string.IsNullOrEmpty(p.MainWindowTitle)).ToList())
{
dict.Add(proc.MainWindowHandle, proc.MainWindowTitle);
}
comboBox1.DisplayMember = "Value";
comboBox1.ValueMember = "Key";
comboBox1.DataSource = dict.ToList();
}
private void comboBox1_SelectionChangeCommitted(object sender, EventArgs e)
{
lblCurrentApp.Text = comboBox1.SelectedItem.ToString();
var window = AutomationElement.FromHandle((IntPtr)comboBox1.SelectedValue);
if (window != null) {
GetCommElement(window, ControlType.Edit);
}
}
private void GetCommElement(AutomationElement parent, ControlType controlType)
{
element = parent.FindFirst(TreeScope.Subtree,
new PropertyCondition(AutomationElement.ControlTypeProperty, controlType));
}
private void btnColor_Click(object sender, EventArgs e)
{
if (element is null) return;
var ctrl = sender as Control;
if (element.TryGetCurrentPattern(ValuePattern.Pattern, out object pattern)) {
(pattern as ValuePattern).SetValue(ctrl.Text);
this.Activate();
}
}
}
UIATestApp:
using System.Drawing;
using System.Windows.Forms;
public partial class UIATestApp : Form
{
public UIATestApp() => InitializeComponent();
private void textBox1_TextChanged(object sender, EventArgs e)
{
var color = Color.FromName((sender as Control).Text);
pictureBox1.BackColor = (color.IsKnownColor) ? color: Color.White;
}
}