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

1,00
р.
Справится ли C# с управлением другой программы? Насколько он хуже или лучше чем C++ в этом плане? Справится ли с этой задачей VB.NET?
Требуется.
Изменить размеры окна стороннего приложения. Отправить приложение на самый нижний Z уровень, чтобы оно не появлялось поверх других открытых окон, в то время когда на нем будут происходить какие то действия, например нажатия кнопок. Требуется выбирать в выпадающем списке нужный пункт и далее нажимать кнопку для применения настроек.
Понимаю что это все можно сделать через WinApi но не понимаю где взять описания функций (желательно на русском) и как их встраивать в код C#. Например, как это делалось в VB 6.0 помню.

Ответ
Для вашей задачи вполне подойдёт стандартный майкрософтовский 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().Resize(300, 300)
// получаем старый текст var edit = window.FirstChildByType(ControlType.Document) TextPattern textPattern = edit.GetPattern() 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().Expand() Thread.Sleep(100)
// нашли пункт Save As var saveAsMenu = fileMenu.FirstDescendantByTypeAndName(ControlType.MenuItem, "Save As...") // и выполнили его saveAsMenu.GetPattern().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().Expand()
// нашли пункт UTF-8 и выбрали его: var utf8item = encodingCombobox.FirstDescendantByTypeAndName(ControlType.ListItem, "UTF-8") utf8item.GetPattern().Select()
// вернули фокус в edit box saveAsEditBox.SetFocus() Thread.Sleep(100)
// послали название файла и Enter System.Windows.Forms.SendKeys.SendWait(@"{%}TEMP{%}\oster.txt{ENTER}")
В коде используется следующий простой хелпер:
static class AutomationHelpers { static public T GetPattern(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 } }

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

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