Стандартная технология создания внешних компонент (ВК), предлагаемая фирмой 1С, накладывает ряд ограничений на разработчика: жесткий жизненный цикл объекта, ограниченные возможности по управлению интерфейсом и необходимость регистрации COM-объектов в реестре Windows. Однако, используя возможности языков низкого уровня (C++) и системные API операционной системы, мы можем выйти за эти рамки. В данной статье мы разберем, как внедрять собственный код в процесс 1С, управлять окнами платформы напрямую и обеспечивать стабильную работу модулей даже после их формального удаления в коде 1С.
Рассмотрим ситуацию, когда стандартных средств отображения информации в 1С недостаточно (например, нужно отрисовать видео или динамическую графику поверх окон платформы). Вместо использования штатных объектов 1С мы можем применить технологию сабклассинга (subclassing). Разберем этот процесс по шагам:
FindWindowEx или перебор дочерних окон через EnumChildWindows.SetWindowLongPtr с флагом GWLP_WNDPROC мы подменяем стандартный обработчик сообщений окна на свою функцию. Это позволяет нам перехватывать системные события, такие как WM_PAINT (перерисовка), WM_SIZE (изменение размера) или события мыши.Проанализируем важность этого подхода: он позволяет создавать прозрачные слои, кастомные меню и элементы управления, которые ведут себя как часть платформы, но не ограничены ее движком. При этом крайне важно не забывать вызывать старую оконную процедуру через CallWindowProc, чтобы не нарушить штатную логику работы программы.
Одной из проблем при работе с внешними компонентами в 1С 7.7 и 8.x является непредсказуемая или нежелательная выгрузка DLL. Когда объект в 1С уничтожается (переменная принимает значение Неопределено), платформа вызывает функцию FreeLibrary. Если вы запустили в компоненте фоновые потоки, это приведет к критической ошибке (Access Violation).
Выясним, как заблокировать выгрузку компоненты нестандартным способом. Рассмотрим решение, которое заключается в повторной загрузке библиотеки самой собой. В секции инициализации компоненты (например, в функции Init или DllMain) выполняется следующий код:
HMODULE hSelf = NULL;
char path[MAX_PATH];
GetModuleFileNameA(hInstance, path, MAX_PATH);
hSelf = LoadLibraryA(path);
Разберем механизм работы этого кода: функция LoadLibrary увеличивает счетчик ссылок модуля в операционной системе. Когда платформа 1С вызывает штатную FreeLibrary, счетчик уменьшается, но не достигает нуля, так как мы "удерживаем" одну лишнюю ссылку. В результате DLL остается в адресном пространстве процесса 1С до момента его завершения. Это позволяет безопасно завершать фоновые операции и сохранять перехваты сообщений (хуки) активными.
Многие системные администраторы ограничивают права пользователей на запись в реестр (ветка HKEY_CLASSES_ROOT), что делает невозможным стандартное использование ПодключитьВнешнююКомпоненту для COM-объектов. Посмотрим на альтернативный способ использования технологии Side-by-Side (SxS).
Для реализации этого метода необходимо создать специальный файл манифеста (XML), который описывает компоненту, ее интерфейсы и CLSID. Если манифест включен в ресурсы DLL или расположен рядом с исполняемым файлом, операционная система позволит создать экземпляр объекта через CoCreateInstance, не обращаясь к реестру. В контексте 1С это можно реализовать через вызов GetClassObject напрямую из загруженной в память библиотеки, минуя стандартные механизмы COM-активации.
В современных версиях платформы 1С разработчики могут столкнуться с тем, что код в секции DllMain выполняется в условиях Loader Lock, что накладывает серьезные ограничения (нельзя создавать потоки, загружать другие библиотеки и т.д.). Рассмотрим правильный алгоритм инициализации:
Проанализируем ситуацию: начиная с версии 8.3.14, платформа стала более строго следить за инициализацией. Если вы используете технологию Native API, всю логику по запуску дополнительных сервисов (видео-плееров, сетевых сокетов) необходимо переносить в метод Init интерфейса IComponentBase. Платформа гарантированно вызывает этот метод после того, как библиотека полностью загружена и все системные блокировки сняты.
Важный момент: если ваша компонента должна работать в фоновом режиме, рекомендуется использовать следующий паттерн для передачи событий в 1С:
// В основном потоке сохраняем указатель на IAsyncEvent
m_iEvent = (IAsyncEvent*)pInterface;
// В фоновом потоке вызываем событие
m_iEvent->ExternalEvent(L"MyComponent", L"StatusUpdate", L"Success");
Этот метод позволяет организовать асинхронное взаимодействие, не блокируя основной интерфейс программы. Мы рассмотрели основные принципы "несоблюдения" технологии в пользу более гибких системных вызовов, что открывает широкие возможности для кастомизации 1С под специфические нужды бизнеса.