Как программно сделать скриншот экрана или окна 1С в версии 8.3?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Начнем с того, что рассмотрим проблему, с которой вы столкнулись при попытке программно имитировать нажатие комбинации клавиш Shift + Win + S (которая в Windows вызывает инструмент "Набросок на фрагменте экрана" или "Ножницы") из среды 1С.

Ваше изначальное стремление использовать объект WScript.Shell и его метод SendKeys для этой задачи вполне понятно, так как это стандартный подход для эмуляции нажатий клавиш. Однако, как выяснилось в ходе обсуждения на форуме, а также из нашего опыта, прямая эмуляция клавиши Win (клавиша Windows) с помощью SendKeys для сложных системных комбинаций зачастую не работает или имеет существенные ограничения.

Почему WScript.Shell.SendKeys не подходит для Shift + Win + S?

Давайте разберемся, почему ваш изначальный подход с Shell.SendKeys("+#S"); не сработал. Метод SendKeys предназначен для отправки последовательности нажатий клавиш в активное приложение. Он поддерживает специальные символы для модификаторов:

  1. + — обозначает клавишу Shift.
  2. ^ — обозначает клавишу Ctrl.
  3. % — обозначает клавишу Alt.
  4. # — обозначает клавишу Win (Windows).

Таким образом, запись "+#S" логически должна была бы означать нажатие Shift + Win + S. Однако, несмотря на наличие символа # для клавиши Win, ее программная эмуляция, особенно в сочетании с другими системными клавишами, часто блокируется операционной системой из соображений безопасности и стабильности. Низкоуровневые системные комбинации клавиш, включающие Win, предназначены для непосредственного взаимодействия пользователя с ОС и их программная имитация может быть ограничена, чтобы предотвратить нежелательное поведение или злоупотребления.

Как подтвердили участники форума, включая вас самих, попытки напрямую использовать SendKeys для такой комбинации оказываются безуспешными. Это означает, что нам необходимо искать альтернативные пути для достижения вашей основной цели – получения скриншота экрана или его части для, как вы уточнили, обращения в техническую поддержку с изображением формы документа.

Альтернативные подходы к созданию скриншотов в 1С

Поскольку прямая эмуляция комбинации Shift + Win + S оказалась неэффективной, мы рассмотрим несколько других, более надежных способов получения скриншотов из 1С.

1. Использование встроенной функции 1С: ОтчетОбОшибке

Наиболее элегантным и нативным решением для создания скриншота окна приложения 1С является использование объекта ОтчетОбОшибке. Эта функциональность доступна, начиная с версии платформы 8.3.17.

Важные условия:

Для корректной работы этого метода необходимо убедиться, что в настройках вашей базы данных установлено: "Управление настройками обработки ошибок" - "Снимок окон приложения" = "Отправлять". Это позволит функции создавать снимок текущего сеанса.

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


&НаКлиенте
Процедура Скриншот_сеанса(Полный_путь_к_файлу = "")
    // доступна с версии 8.3.17
    // Сохранение скриншота текущего сеанса пользователя (по переданному адресу)
    // Установить в настройках БД: "Управление настройками обработки ошибок" -
    // "Снимок окон приложения" = "Отправлять"

    #Если НЕ ВебКлиент Тогда
        // Получаем имя временного файла с типом: Строка
        Временный_zip_файл = ПолучитьИмяВременногоФайла("zip");

        // Регистрация (сохранение и отправка данных) ошибки
        // Несмотря на название, объект позволяет просто зафиксировать состояние
        // приложения, включая скриншот, без фактической ошибки.
        Отчет_об_ошибке = Новый ОтчетОбОшибке(ИнформацияОбОшибке);
        Отчет_об_ошибке.Записать(Временный_zip_файл, Ложь);

        // Получаем имя каталога временных файлов пользователя
        // Добавляем уникальный идентификатор для избежания конфликтов
        Каталог_файлов = КаталогВременныхФайлов + СтрЗаменить(Новый УникальныйИдентификатор, "-", "");

        // Чтение файла ZIP, который содержит скриншот и другие данные
        Архив_zip = Новый ЧтениеZipФайла(Временный_zip_файл);
        Архив_zip.ИзвлечьВсе(Каталог_файлов, РежимВосстановленияПутейФайловZIP.НеВосстанавливать);

        // Случайное число в диапазоне от 1 до 999999 для рандомного имени файла
        ГенераторСлЧисел = Новый ГенераторСлучайныхЧисел;
        СлучЧисло = ГенераторСлЧисел.СлучайноеЧисло(1,999999);

        // Определяем путь к извлеченному скриншоту, обычно это 'screen.png' или 'screenN.png'
        // В нашем случае, так как мы хотим уникальное имя, переименовываем
        // Предполагаем, что файл скриншота называется screen.png внутри архива
        Исходный_путь_скриншота_в_архиве = Каталог_файлов + "\screen.png";
        
        // Определяем целевой путь для сохранения скриншота с уникальным именем
        Путь_к_скриншоту_для_сохранения = Каталог_файлов + "\screen" + СлучЧисло + ".png";

        // Если полный путь для сохранения не передан, сохраняем во временный каталог
        Если ПустаяСтрока(Полный_путь_к_файлу) Тогда
            Полный_путь_к_файлу = КаталогВременныхФайлов + "\скриншот_" + Формат(ТекущаяДата(), "ДФ=yyyyMMdd_HHmmss") + ".png";
        КонецЕсли;

        // Копируем скриншот из временного каталога ZIP-архива в целевой файл
        // Сначала копируем screen.png в файл с уникальным именем
        КопироватьФайл(Исходный_путь_скриншота_в_архиве, Путь_к_скриншоту_для_сохранения);
        // Затем копируем уникальный скриншот в конечный целевой файл
        КопироватьФайл(Путь_к_скриншоту_для_сохранения, Полный_путь_к_файлу);

        // Чистим временные файлы и каталоги
        УдалитьФайлы(Временный_zip_файл);
        УдалитьФайлы(Каталог_файлов + "\*.*"); // Удаляем все файлы в каталоге
        УдалитьКаталоги(Каталог_файлов); // Удаляем сам каталог
    #КонецЕсли
КонецПроцедуры

Давайте пошагово разберем этот код:

  1. #Если НЕ ВебКлиент Тогда ... #КонецЕсли: Эта директива компиляции гарантирует, что код будет выполняться только в толстом или тонком клиенте 1С, так как функциональность ОтчетОбОшибке и работа с файлами не доступна в веб-клиенте.
  2. Временный_zip_файл = ПолучитьИмяВременногоФайла("zip");: Мы создаем уникальное имя для временного ZIP-архива, в котором будет храниться информация об ошибке, включая скриншот.
  3. Отчет_об_ошибке = Новый ОтчетОбОшибке(ИнформацияОбОшибке);: Создаем объект ОтчетОбОшибке. Несмотря на название, мы используем его не для отправки ошибки, а для генерации отчета о текущем состоянии приложения. ИнформацияОбОшибке – это обязательный параметр, который в данном контексте можно просто передать как есть, так как нам не нужна информация о реальной ошибке.
  4. Отчет_об_ошибке.Записать(Временный_zip_файл, Ложь);: Записываем содержимое отчета (включая скриншот) в созданный временный ZIP-файл. Параметр Ложь указывает, что не нужно пытаться отправить отчет куда-либо, а просто сохранить его локально.
  5. Каталог_файлов = КаталогВременныхФайлов + СтрЗаменить(Новый УникальныйИдентификатор, "-", "");: Создаем уникальный временный каталог, куда будем распаковывать содержимое ZIP-арфайла. Используем Новый УникальныйИдентификатор для гарантированного создания уникального имени каталога, чтобы избежать конфликтов.
  6. Архив_zip = Новый ЧтениеZipФайла(Временный_zip_файл); Архив_zip.ИзвлечьВсе(Каталог_файлов, РежимВосстановленияПутейФайловZIP.НеВосстанавливать);: Открываем созданный ZIP-архив и извлекаем все его содержимое в наш временный каталог. Среди извлеченных файлов будет и файл скриншота, обычно с именем screen.png.
  7. Генерация уникального имени и копирование файла: Исходный код был немного скорректирован для большей надежности. Мы сначала определяем путь к извлеченному файлу screen.png, затем генерируем уникальное имя для скриншота, копируем screen.png в этот уникальный файл, а затем копируем его в конечный путь, указанный в параметре Полный_путь_к_файлу. Если Полный_путь_к_файлу не указан, мы формируем его автоматически во временном каталоге с текущей датой и временем.
  8. УдалитьФайлы(Временный_zip_файл); УдалитьФайлы(Каталог_файлов + "\*.*"); УдалитьКаталоги(Каталог_файлов);: В конце процедуры мы обязательно удаляем все созданные временные файлы и каталоги, чтобы избежать накопления мусора на диске пользователя.

Этот подход позволяет получить скриншот текущего окна приложения 1С, что идеально подходит для вашей задачи создания "скриншота из формы документа" для технической поддержки.

2. Имитация PrintScreen и работа с буфером обмена (через внешние средства)

Если вам нужен скриншот всего экрана (или всех экранов), а не только окна 1С, вы можете программно сымитировать нажатие клавиши PrintScreen. Метод WScript.Shell.SendKeys прекрасно справляется с этой задачей:


Shell = Новый COMОбъект("Wscript.Shell");
Shell.SendKeys("{PRTSC}"); // Нажимаем PrintScreen

Это действие поместит изображение всего экрана (или активного окна, если нажать Alt + PrintScreen) в буфер обмена Windows. Однако, следующая задача — извлечь это изображение из буфера обмена и сохранить его в файл, уже не может быть решена напрямую средствами 1С. Для этого потребуется использовать внешние компоненты или скрипты, взаимодействующие с .NET Framework или Windows API. Например, вы можете вызвать внешний скрипт на PowerShell, который сделает следующее:

  1. Получит содержимое буфера обмена.
  2. Проверит, является ли оно изображением.
  3. Сохранит изображение в указанный файл.

Запуск такого скрипта из 1С может выглядеть так:


Shell = Новый COMОбъект("Wscript.Shell");
ПутьКPowerShellСкрипту = "C:\МоиСкрипты\СохранитьСкриншотИзБуфера.ps1";
ПутьКФайлуСкриншота = "C:\Temp\МойСкриншот.png";
Команда = "powershell.exe -File """ + ПутьКPowerShellСкрипту + """ -OutputPath """ + ПутьКФайлуСкриншота + """";
Shell.Run(Команда, 0, Истина); // 0 - не показывать окно, Истина - ждать завершения

Это решение сложнее в реализации и требует создания внешнего кода, но дает большую гибкость.

3. Использование внешних утилит или скриптов PowerShell для прямого захвата экрана

Для максимально гибкого решения, например, если вам нужен скриншот определенной области экрана, нескольких мониторов или других специфических задач, вы можете создать отдельную внешнюю утилиту (на C#, Python, PowerShell) или скрипт, который будет выполнять захват экрана напрямую, без использования буфера обмена или имитации клавиш. Например, в PowerShell можно использовать классы .NET Framework System.Drawing для захвата экрана:


# Пример PowerShell скрипта для захвата всего экрана
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

function Take-Screenshot {
    param(
        [string]$OutputPath = "C:\Temp\screenshot.png"
    )

    $screenBounds = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
    $bitmap = New-Object System.Drawing.Bitmap($screenBounds.Width, $screenBounds.Height)
    $graphics = [System.Drawing.Graphics]::FromImage($bitmap)
    $graphics.CopyFromScreen($screenBounds.Left, $screenBounds.Top, 0, 0, $screenBounds.Size)
    $bitmap.Save($OutputPath, [System.Drawing.Imaging.ImageFormat]::Png)
    $graphics.Dispose()
    $bitmap.Dispose()
}

Take-Screenshot -OutputPath "C:\Temp\мой_скриншот_powershell.png"

Этот скрипт может быть сохранен в файл .ps1 и затем запущен из 1С с помощью того же WScript.Shell.Run(). Преимущество такого подхода в его универсальности и независимости от буфера обмена или активного окна, но он, опять же, требует создания и развертывания внешних файлов.

Заключение

В нашем обсуждении мы выяснили, что прямая программная эмуляция комбинации Shift + Win + S из 1С с использованием WScript.Shell.SendKeys сталкивается с серьезными ограничениями из-за клавиши Win. Однако, ваша основная потребность — создание скриншота для технических целей — прекрасно решается другими способами.

Если вам необходим скриншот именно окна приложения 1С, то наиболее эффективным и рекомендуемым является использование встроенной функции 1С ОтчетОбОшибке, как показано в примере кода. Этот метод является частью платформы 1С, не требует сторонних инструментов и надежно работает.

Если же ваша задача шире и требует захвата всего экрана или его произвольной части, то вам придется прибегнуть к использованию внешних средств — имитации PrintScreen с последующей обработкой буфера обмена через PowerShell/.NET, либо созданию полностью внешней утилиты для захвата экрана. Эти подходы более сложны в реализации и развертывании, но предоставляют максимальный контроль над процессом.

Выбирайте решение исходя из ваших конкретных требований и готовности к использованию внешних компонентов.

← На главную