Справится ли C# с управлением другой программы

xstriker

  • Справится ли C# с управлением другой программы?
  • Насколько он хуже или лучше чем C++ в этом плане?
  • Справится ли с этой задачей VB.NET?

Требуется.

  1. Изменить размеры окна стороннего приложения.
  2. Отправить приложение на самый нижний Z уровень, чтобы оно не появлялось поверх других открытых окон, в то время когда на нем будут происходить какие то действия, например нажатия кнопок.
  3. Требуется выбирать в выпадающем списке нужный пункт и далее нажимать кнопку для применения настроек.

Понимаю что это все можно сделать через WinApi но не понимаю где взять описания функций (желательно на русском) и как их встраивать в код C#. Например, как это делалось в VB 6.0 помню.

2 ответа

xstriker

  • c# справится. Справится любой язык умеющий вызывать winapi
  • Хуже только тем, что нужно объявить функции winapi и возможно структуры
  • у vb те же возможности, что и у c#

Вам сюда http://www.pinvoke.net/ за тем как объявлять функции winapi в c# и vb.net

Возможно подойдет что-то более высокоуровневое вроде Cruciatus от 2GIS


xstriker

Для вашей задачи вполне подойдёт стандартный майкрософтовский UI Automation. Вот пример без P/Invoke:

var notepadProcess = Process.GetProcessesByName("Notepad").FirstOrDefault();
var window = AutomationElement.FromHandle(notepadProcess.MainWindowHandle);
var transformPattern = (TransformPattern)window.GetCurrentPattern(TransformPattern.Pattern);
transformPattern.Resize(300, 300);

(изменяет размер окна notepad.exe на 300х300).

Не забудьте подключить сборки UIAutomationClient и UIAutomationTypes и добавить контроль ошибок.

В MSDN есть документация об остальной функциональности UI Automation. Например, как активировать контрол (нажать на кнопку, выбрать пункт меню и т. п.).

Список всех подокон и их доступных свойств легко подсмотреть при помощи утилиты Inspect.exe из Windows SDK.

После небольшого упражнения в гуглопоиске на стэковерфлоу, вот более серьёзный пример:

// находим бегущий notepad
var notepadProcess = Process.GetProcessesByName("Notepad").FirstOrDefault();
var window = AutomationElement.FromHandle(notepadProcess.MainWindowHandle);
// меняем размер окна
window.GetPattern<transformpattern>().Resize(300, 300);
// получаем старый текст
var edit = window.FirstChildByType(ControlType.Document);
TextPattern textPattern = edit.GetPattern<textpattern>();
var oldText = textPattern.DocumentRange.GetText(-1);
// заменяем его на новый. для этого придётся посылать нажатия на клавиатуру
// основано на этом примере: https://msdn.microsoft.com/en-us/library/ms750582.aspx
textPattern.DocumentRange.Select();
edit.SetFocus();
Thread.Sleep(100);
System.Windows.Forms.SendKeys.SendWait("{DEL}");
System.Windows.Forms.SendKeys.SendWait( "Тормозите прямо в папу. Папа мягкий. Он простит.");
// сохраним изменённый файл под новым именем
// вызовем File -> Save as... (на русскоязычной системе понадобятся другие строки!)
var menuBar = window.FirstChildByType(ControlType.MenuBar);
var fileMenu = menuBar.FirstDescendantByTypeAndName(ControlType.MenuItem, "File");
// раскрыли меню File:
fileMenu.GetPattern<expandcollapsepattern>().Expand();
Thread.Sleep(100);
// нашли пункт Save As
var saveAsMenu = fileMenu.FirstDescendantByTypeAndName(ControlType.MenuItem, "Save As...");
// и выполнили его
saveAsMenu.GetPattern<invokepattern>().Invoke();
Thread.Sleep(100);
// запомнили элемент с фокусом, это edit box для ввода имени файла
var saveAsEditBox = AutomationElement.FocusedElement;
// поменяем ещё кодировку через комбобокс Encoding
// нашли окно:
var saveAsDialog = AutomationHelpers.FindWindowFrom(saveAsEditBox);
// и комбобокс
var encodingCombobox = saveAsDialog.FirstDescendantByTypeAndName(ControlType.ComboBox, "Encoding:");
// раскрыли его:
encodingCombobox.GetPattern<expandcollapsepattern>().Expand();
// нашли пункт UTF-8 и выбрали его:
var utf8item = encodingCombobox.FirstDescendantByTypeAndName(ControlType.ListItem, "UTF-8");
utf8item.GetPattern<selectionitempattern>().Select();
// вернули фокус в edit box
saveAsEditBox.SetFocus();
Thread.Sleep(100);
// послали название файла и Enter
System.Windows.Forms.SendKeys.SendWait(@"{%}TEMP{%}\oster.txt{ENTER}");
</selectionitempattern></expandcollapsepattern></invokepattern></expandcollapsepattern></textpattern></transformpattern>

В коде используется следующий простой хелпер:

static class AutomationHelpers
{ static public T GetPattern<t>(this AutomationElement element) where T : BasePattern { var pattern = (AutomationPattern)typeof(T).GetField("Pattern").GetValue(null); return (T)element.GetCurrentPattern(pattern); } static public AutomationElement FirstChildByType( this AutomationElement element, ControlType ct) { return element.FindFirst( TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ct)); } static public AutomationElement FirstDescendantByTypeAndName( this AutomationElement element, ControlType ct, string name) { return element.FindFirst( TreeScope.Descendants, new AndCondition( new PropertyCondition(AutomationElement.ControlTypeProperty, ct), new PropertyCondition(AutomationElement.NameProperty, name))); } static public AutomationElement FindWindowFrom(AutomationElement control) { var walker = TreeWalker.ControlViewWalker; while (control.Current.ControlType != ControlType.Window) control = walker.GetParent(control); return control; }
}
</t>

VB.NET справится точно так же, функциональность UI Automation доступна в обоих языках.

Добавил паузы в секунду между действиями и адаптировал к русской системе (другие названия пунктов меню), результат:

licensed under cc by-sa 3.0 with attribution.