При разработке интеграций между 1С:Предприятием и внешними приложениями (например, написанными на C#) часто возникает необходимость сделать внешнее окно «дочерним» или «владеемым» по отношению к окну 1С. Это позволяет избежать ситуации, когда вспомогательное окно теряется под основным интерфейсом 1С, а также обеспечивает синхронное сворачивание и закрытие окон. Основная сложность заключается в том, что встроенный язык 1С не предоставляет прямого метода для получения дескриптора окна (HWND). В данной статье мы подробно разберем несколько проверенных способов решения этой задачи для обычных форм.
Этот способ является одним из самых простых и надежных, если вам не хочется писать сложные внешние компоненты. Суть заключается в том, чтобы на мгновение сделать заголовок окна 1С уникальным, найти его средствами Windows API из внешнего приложения и вернуть заголовок обратно.
Рассмотрим пошагово алгоритм действий в 1С:
УстановитьЗаголовокСистемы().На стороне 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);
}
Если вы разрабатываете полноценную внешнюю компоненту (ВК), 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С, который вызвал компоненту, что критически важно при работе в терминальных сессиях или при запущенных нескольких копиях программы.
Если нет возможности менять заголовок окна и использовать интерфейсы ВК, можно прибегнуть к перебору всех окон в операционной системе. Разберем логику этого процесса:
GetCurrentProcessId.EnumWindows перечисляем все окна верхнего уровня.GetWindowThreadProcessId, чтобы узнать, какому процессу оно принадлежит.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);
В современных операционных системах можно быстро получить дескриптор главного окна через 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С.