Как получить HWND окна 1С для взаимодействия с внешними приложениями

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

При разработке интеграций между 1С:Предприятием и внешними приложениями (например, написанными на C#) часто возникает необходимость сделать внешнее окно «дочерним» или «владеемым» по отношению к окну 1С. Это позволяет избежать ситуации, когда вспомогательное окно теряется под основным интерфейсом 1С, а также обеспечивает синхронное сворачивание и закрытие окон. Основная сложность заключается в том, что встроенный язык 1С не предоставляет прямого метода для получения дескриптора окна (HWND). В данной статье мы подробно разберем несколько проверенных способов решения этой задачи для обычных форм.

Метод 1. Поиск окна по уникальному временному заголовку

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

Рассмотрим пошагово алгоритм действий в 1С:

  1. Генерируем уникальный идентификатор (GUID).
  2. Запоминаем текущий заголовок системы с помощью УстановитьЗаголовокСистемы().
  3. Устанавливаем новый заголовок, содержащий GUID.
  4. Запускаем внешнее приложение на C#, передавая этот GUID в параметрах командной строки.
  5. После паузы или получения сигнала от приложения возвращаем старый заголовок.

На стороне C# приложения используем функцию FindWindow из библиотеки user32.dll. Проанализируем пример кода на C#:


[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

// В методе Main или при инициализации формы
string uniqueTitle = args[0]; // Получаем GUID из параметров запуска
IntPtr hwnd1C = FindWindow(null, uniqueTitle);

if (hwnd1C != IntPtr.Zero) 
{
    // Теперь у нас есть HWND окна 1С, и мы можем установить его владельцем
    var myForm = new MyExternalForm();
    NativeWindow nativeWindow = new NativeWindow();
    nativeWindow.AssignHandle(hwnd1C);
    myForm.Show(nativeWindow);
}

Метод 2. Использование интерфейса IExtWndsSupport в ВК

Если вы разрабатываете полноценную внешнюю компоненту (ВК), 1С предоставляет специализированный интерфейс IExtWndsSupport. Это наиболее «правильный» с точки зрения архитектуры 1С метод. Выясним, как это реализовать в коде компоненты.

Интерфейс IExtWndsSupport содержит метод GetAppMainFrame, который возвращает дескриптор главного окна приложения. Рассмотрим фрагмент кода, который можно использовать внутри вашей ВК:


public string СоздатьОкно() 
{
    IExtWndsSupport n;
    // Object1C — это объект связи с 1С, передаваемый при инициализации ВК
    n = (IExtWndsSupport)Object1C;
    IntPtr hwnd;
    n.GetAppMainFrame(out hwnd);
    
    // Создаем форму C#
    var form = new MyForm();
    
    // Используем WinAPI для установки владельца
    SetOwner(form.Handle.ToInt32(), hwnd.ToInt32());
    
    form.Show();
    return "Окно успешно открыто над 1С";
}

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

Метод 3. Перебор окон через EnumWindows и проверку PID

Если нет возможности менять заголовок окна и использовать интерфейсы ВК, можно прибегнуть к перебору всех окон в операционной системе. Разберем логику этого процесса:

  1. Получаем идентификатор процесса (PID) текущего экземпляра 1С. Это можно сделать через GetCurrentProcessId.
  2. С помощью функции EnumWindows перечисляем все окна верхнего уровня.
  3. Для каждого окна вызываем GetWindowThreadProcessId, чтобы узнать, какому процессу оно принадлежит.
  4. Если PID окна совпадает с PID нашей 1С, дополнительно проверяем класс окна через GetClassName (для 1С 8.3 обычные формы часто имеют класс V8TopLevelFrame или аналогичный).

Посмотрим на пример реализации поиска на стороне внешнего приложения, которое знает PID родителя:


[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);

[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

// Внутри функции поиска:
EnumWindows((hWnd, lParam) => {
    uint processId;
    GetWindowThreadProcessId(hWnd, out processId);
    if (processId == targetPid) { // targetPid передан из 1С
        // Проверяем, является ли окно видимым и главным
        if (IsMainWindow(hWnd)) {
            foundHwnd = hWnd;
            return false; // Останавливаем поиск
        }
    }
    return true;
}, IntPtr.Zero);

Метод 4. Использование PowerShell для быстрого получения HWND

В современных операционных системах можно быстро получить дескриптор главного окна через PowerShell, не прибегая к сложному коду на C++ или C#. Рассмотрим, как это можно сделать прямо из 1С:


Команда = "powershell -command ""(Get-Process -Id " + Строка(ТекущийPID) + ").MainWindowHandle""";
ЗапуститьПриложение(Команда, , Истина, ВыводРезультата);
// В переменной ВыводРезультата будет HWND в десятичном виде

Важный нюанс: MainWindowHandle возвращается операционной системой только для главного окна процесса. Если в 1С открыто модальное окно или несколько окон, этот метод может вернуть не совсем то, что ожидается, но для большинства задач привязки внешнего окна к «шапке» 1С этого достаточно.

Особенности различных версий платформы

Важно помнить о технологических изменениях в платформе 1С:

Поэтому, если ваша задача — привязать окно C# к конкретной кнопке в управляемой форме, помните: у этой кнопки нет своего HWND. Вам в любом случае придется привязываться к главному окну 1С (Main Frame).

Рекомендации по управлению окнами

Чтобы ваше внешнее приложение не только открывалось поверх 1С, но и вело себя корректно, попросите программиста C# использовать функцию SetWindowLongPtr с флагом GWLP_HWNDPARENT. Это создаст связь Owner-Owned.

В отличие от обычного дочернего окна (Child), «владеемое» окно:

  1. Всегда отображается поверх своего владельца.
  2. Автоматически скрывается, когда владелец сворачивается.
  3. Не ограничивается рамками окна-владельца и может быть вынесено на второй монитор.

Это обеспечит максимально бесшовную интеграцию внешнего решения в экосистему 1С.

← На главную