Я пытался сохранить эту процедуру как generic , насколько это возможно, чтобы она работала независимо от того, запущено ли уже просматриваемое приложение при запуске вашего приложения или нет.
Вам просто нужно указать имя процесса просматриваемого приложения или заголовок его главного окна, чтобы процедура могла идентифицировать это приложение.
Используйте одно из этих полей и соответствующий перечислитель:
private string appProcessName = "theAppProcessName"; and
FindWindowMethod.ProcessName
// Or
private string appWindowTitle = "theAppMainWindowTitle"; and
FindWindowMethod.Caption
передача этих значений в процедуру, которая запускает наблюдателя, например:
StartAppWatcher(appProcessName, FindWindowMethod.ProcessName);
Как вы можете видеть - поскольку вы пометили свой вопрос как winforms
- это полная форма (с именем frmWindowWatcher
), который содержит всю логику, необходимую для выполнения этой задачи.
Как это работает:
- Когда вы запускаете
frmWindowWatcher
, процедура проверяет, было ли отслеживаемое приложение (здесь определено с использованием его имени процесса, но вы можете изменить метод,как уже описано), уже запущен.
Если это так, он инициализирует класс поддержки, ElementWindow
, который будет содержать некоторую информацию о наблюдаемом приложении.
Я добавил этот класс поддержкиесли вам нужно выполнить некоторые действия, если отслеживаемое приложение уже запущено (в этом случае поле ElementWindow windowElement
не будет иметь значение null при вызове метода StartAppWatcher()
). Эта информация также может быть полезна в других случаях. - Когда в системе открывается новая Windows, процедура проверяет, принадлежит ли это окно наблюдаемому приложению. Если это произойдет, идентификатор процесса будет таким же. Если Windows является MessageBox (идентифицируется с использованием стандартного
ClassName
: #32770
) и принадлежит наблюдаемому приложению, AutomationEventHandler присоединяется к дочерней OK
кнопке.
Здесь я использую делегат: AutomationEventHandler DialogButtonHandler
для обработчика и поле экземпляра (AutomationElement msgBoxButton
) для элемента Button, поскольку эти ссылки необходимыудалить обработчик нажатия кнопки, когда MessageBox закрыт. - При нажатии кнопки MessageBox
OK
вызывается метод MessageBoxButtonHandler
. Здесь вы можете определить, какое действие предпринять в данный момент. - Когда форма
frmWindowWatcher
закрыта, все обработчики автоматизации удаляются, вызывая метод Automation.RemoveAllEventHandlers () , чтобы обеспечить окончательную очистку и предотвратить утечку ресурсов приложением.
using System.Diagnostics;
using System.Linq;
using System.Windows.Automation;
using System.Windows.Forms;
public partial class frmWindowWatcher : Form
{
AutomationEventHandler DialogButtonHandler = null;
AutomationElement msgBoxButton = null;
ElementWindow windowElement = null;
int currentProcessId = 0;
private string appProcessName = "theAppProcessName";
//private string appWindowTitle = "theAppMainWindowTitle";
public enum FindWindowMethod
{
ProcessName,
Caption
}
public frmWindowWatcher()
{
InitializeComponent();
using (var proc = Process.GetCurrentProcess()) {
currentProcessId = proc.Id;
}
// Identify the application by its Process name...
StartAppWatcher(appProcessName, FindWindowMethod.ProcessName);
// ... or by its main Window Title
//StartAppWatcher(appWindowTitle, FindWindowMethod.Caption);
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
Automation.RemoveAllEventHandlers();
base.OnFormClosed(e);
}
private void StartAppWatcher(string elementName, FindWindowMethod method)
{
windowElement = GetAppElement(elementName, method);
// (...)
// You may want to perform some actions if the watched application is already running when you start your app
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement,
TreeScope.Subtree, (elm, e) => {
AutomationElement element = elm as AutomationElement;
try
{
if (element == null || element.Current.ProcessId == currentProcessId) return;
if (windowElement == null) windowElement = GetAppElement(elementName, method);
if (windowElement == null || windowElement.ProcessId != element.Current.ProcessId) return;
// If the Window is a MessageBox generated by the watched app, attach the handler
if (element.Current.ClassName == "#32770")
{
msgBoxButton = element.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.NameProperty, "OK"));
if (msgBoxButton != null && msgBoxButton.GetSupportedPatterns().Any(p => p.Equals(InvokePattern.Pattern)))
{
Automation.AddAutomationEventHandler(
InvokePattern.InvokedEvent, msgBoxButton, TreeScope.Element,
DialogButtonHandler = new AutomationEventHandler(MessageBoxButtonHandler));
}
}
}
catch (ElementNotAvailableException) {
// Ignore: this exception may be raised if you show a modal dialog,
// in your own app, that blocks the execution. When the dialog is closed,
// AutomationElement element is no longer available
}
});
Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, AutomationElement.RootElement,
TreeScope.Subtree, (elm, e) => {
AutomationElement element = elm as AutomationElement;
if (element == null || element.Current.ProcessId == currentProcessId || windowElement == null) return;
if (windowElement.ProcessId == element.Current.ProcessId) {
if (windowElement.MainWindowTitle == element.Current.Name) {
windowElement = null;
}
}
});
}
private void MessageBoxButtonHandler(object sender, AutomationEventArgs e)
{
Console.WriteLine("Dialog Button clicked at : " + DateTime.Now.ToString());
// (...)
// Remove the handler after, since the next MessageBox needs a new handler.
Automation.RemoveAutomationEventHandler(e.EventId, msgBoxButton, DialogButtonHandler);
}
private ElementWindow GetAppElement(string elementName, FindWindowMethod method)
{
Process proc = null;
try {
switch (method) {
case FindWindowMethod.ProcessName:
proc = Process.GetProcessesByName(elementName).FirstOrDefault();
break;
case FindWindowMethod.Caption:
proc = Process.GetProcesses().FirstOrDefault(p => p.MainWindowTitle == elementName);
break;
}
return CreateElementWindow(proc);
}
finally {
proc?.Dispose();
}
}
private ElementWindow CreateElementWindow(Process process) =>
process == null ? null : new ElementWindow(process.ProcessName) {
MainWindowTitle = process.MainWindowTitle,
MainWindowHandle = process.MainWindowHandle,
ProcessId = process.Id
};
}
Класс поддержки, используемый для хранения информации о наблюдаемом приложении:
Он инициализируется с использованием имени процесса приложения:
public ElementWindow(string processName)
но, конечно, вы можете изменить его по мере необходимости, используя заголовок окна, как описано выше, или даже удалить аргумент инициализации, если хотите (класс просто не должен быть null
, когда наблюдаемое приложение было обнаружено и идентифицировано).
using System.Collections.Generic;
public class ElementWindow
{
public ElementWindow(string processName) => this.ProcessName = processName;
public string ProcessName { get; set; }
public string MainWindowTitle { get; set; }
public int ProcessId { get; set; }
public IntPtr MainWindowHandle { get; set; }
}