В процессе разработки на платформе 1С:Предприятие 8 (особенно в режиме обычного приложения) часто возникает необходимость программно получить текст, который был выведен в окно «Служебные сообщения». Это может потребоваться для логгирования ошибок, автоматического анализа результатов выполнения процедур или передачи данных в другие системы. Поскольку стандартные механизмы не всегда позволяют дотянуться до содержимого этого окна, разработчики прибегают к имитации действий пользователя.
В данной статье мы подробно разберем, как реализовать такой перехват, почему возникают ошибки при использовании разных раскладок клавиатуры и какие альтернативные подходы существуют для решения этой задачи.
Один из наиболее распространенных способов — использование объекта Wscript.Shell и его метода SendKeys. Этот метод позволяет отправлять команды нажатия клавиш активному окну. Рассмотрим типичный алгоритм действий: нам нужно перевести фокус в окно сообщений, выделить весь текст и скопировать его в буфер обмена.
Проанализируем ситуацию, когда стандартный код перестает работать при смене раскладки клавиатуры. Рассмотрим пример кода, который часто используется для этой цели:
WshShell = Новый COMОбъект("Wscript.Shell");
// Переводим фокус в окно "Служебные сообщения" (Ctrl+Alt+O)
WshShell.sendKeys("^%(o)");
// Выделяем все содержимое (Ctrl+A)
WshShell.SendKeys("^(a)");
Проблема заключается в том, что метод SendKeys крайне чувствителен к текущей раскладке клавиатуры в операционной системе. Если у пользователя включена русская раскладка, то команда ^(a) (Ctrl + A) не будет распознана как команда «Выделить всё». Логичным кажется заменить латинскую букву на кириллическую, но здесь кроется важный нюанс, связанный с регистром.
Выясним причину ошибки: когда мы пишем WshShell.SendKeys("^(Ф)"), символ «Ф» является заглавным. В языке SendKeys использование заглавной буквы автоматически интерпретируется как нажатие клавиши Shift. Таким образом, команда превращается в Ctrl + Shift + а, что в интерфейсе 1С не приводит к выделению текста.
Правильный вариант для русской раскладки — использование строчной (маленькой) буквы. Рассмотрим исправленный код:
WshShell = Новый COMОбъект("Wscript.Shell");
// Для английской раскладки:
WshShell.sendKeys("^%(o)");
WshShell.SendKeys("^(a)");
// Для русской раскладки (обратите внимание на маленькую 'щ' и 'ф'):
WshShell.sendKeys("^%(щ)");
WshShell.SendKeys("^(ф)");
Важный совет: Чтобы код был универсальным, необходимо либо проверять текущую раскладку через WinAPI, либо посылать последовательно команды для обеих раскладок, хотя это может вызвать нежелательные побочные эффекты.
Часто программисты пытаются использовать встроенный метод платформы ПолучитьСообщенияПользователю(). Разберем, почему в обычном приложении для перехвата вывода через процедуру Сообщить() он бесполезен.
Метод ПолучитьСообщенияПользователю() предназначен для работы с объектами типа СообщениеПользователю. Эти объекты создаются в управляемом интерфейсе или при выполнении кода на стороне сервера. В обычном приложении (толстый клиент) классическая команда Сообщить() отправляет строку напрямую в текстовый поток окна сообщений, минуя очередь объектов сообщений.
Проанализируем контексты применения этого метода:
СообщениеПользователю.В обычном же приложении окно «Служебные сообщения» является визуальным элементом, содержимое которого после вывода методом Сообщить() платформой больше не кэшируется в виде программных объектов.
Если решение с SendKeys кажется нестабильным (так как оно зависит от фокуса окна и раскладки), рассмотрим более профессиональный подход — использование функций Windows API. Окно служебных сообщений в 1С — это стандартное дочернее окно операционной системы.
Посмотрим на алгоритм реализации через WinAPI:
FindWindowEx, чтобы найти дескриптор (HWND) окна 1С, а затем внутри него — дочернее окно сообщений. Класс этого окна обычно имеет программное имя, специфичное для версии платформы (например, V8TextControl).WM_GETTEXT.Этот метод гораздо надежнее, так как он работает «в тихом режиме» и не зависит от того, какая раскладка выбрана у пользователя в данный момент.
Самый стабильный способ — это не перехватывать уже выведенные сообщения, а контролировать процесс их вывода. Проанализируем, как это можно внедрить в существующую конфигурацию.
Вместо прямого использования встроенной функции Сообщить(), создадим общую процедуру-обертку в глобальном модуле:
Процедура СообщитьПользователю(ТекстСообщения) Экспорт
// 1. Выводим стандартно на экран
Сообщить(ТекстСообщения);
// 2. Дублируем в массив для последующего анализа
ГлобальныйКонтейнерСообщений.Добавить(ТекстСообщения);
// 3. Дополнительно можно записывать в текстовый лог на диске
ЗаписатьВЛогФайл(ТекстСообщения);
КонецПроцедуры
После создания такой обертки мы можем воспользоваться механизмом «Поиск и замена» по всей конфигурации, чтобы заменить все вызовы Сообщить( на ОбщегоНазначения.СообщитьПользователю(. Это гарантирует, что ни одно сообщение не будет упущено, и нам не придется полагаться на нестабильные методы имитации пользовательского ввода.
Мы рассмотрели несколько подходов к решению задачи. Если вам нужно быстрое решение «здесь и сейчас» для разовой задачи, используйте WshShell.SendKeys, но обязательно помните про строчный регистр символов для русской раскладки (ф вместо Ф). Для создания надежных тиражируемых решений лучше использовать рефакторинг кода с внедрением собственной процедуры вывода сообщений или низкоуровневое чтение через WinAPI.