При разработке интеграционных решений часто возникает задача создания собственного COM-объекта на языке C#. В то время как обычные методы вызываются из 1С без проблем, реализация событий (callback-механизмов) требует глубокого понимания технологии COM Interop. Если просто объявить событие в классе C#, 1С выдаст ошибку "Событие объекта не обнаружено". Рассмотрим подробнее, как правильно настроить проект, чтобы 1С могла подписаться на события через ДобавитьОбработчик.
Проблема заключается в том, что 1С работает с событиями COM-объектов через механизм точек подключения (Connection Points). Для того чтобы внешнее приложение (клиент, в нашем случае — 1С) могло узнать о наличии событий, COM-объект должен реализовать интерфейс IConnectionPointContainer. В .NET это происходит не автоматически. Нам необходимо явно указать, какой интерфейс будет служить "источником событий" для нашего класса. Без этого 1С видит методы объекта, но не видит его событийную модель.
Первым делом проанализируем структуру интерфейса событий. Он должен быть определен отдельно от основного интерфейса методов. Важно пометить его атрибутом InterfaceType со значением ComInterfaceType.InterfaceIsIDispatch. Это критично, так как 1С использует позднее связывание для обработки событий.
[Guid("7E290D1A-DE2A-4D73-9B87-9F24E72B2AB9")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface IMyEvents
{
[DispId(1)]
void OnDataReceived(string message);
[DispId(2)]
void OnError(int errorCode);
}
Обратим внимание на атрибут DispId. Каждый метод в интерфейсе событий должен иметь уникальный идентификатор. Именно по этим номерам среда выполнения COM будет сопоставлять вызов события в C# с обработчиком в 1С.
Теперь перейдем к реализации основного класса. Чтобы связать класс с интерфейсом событий, используем атрибут ComSourceInterfaces. Рассмотрим пример реализации:
[Guid("Ваш-Guid-Класса")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IMyEvents))]
[ComVisible(true)]
public class MyComServer : IMyMethods
{
// Объявляем событие, соответствующее интерфейсу IMyEvents
public event DataReceivedDelegate OnDataReceived;
public event ErrorDelegate OnError;
public delegate void DataReceivedDelegate(string message);
public delegate void ErrorDelegate(int errorCode);
public void DoWork()
{
// Имитация работы
string result = "Данные обработаны";
// Вызываем событие
RaiseDataReceived(result);
}
private void RaiseDataReceived(string msg)
{
// Обязательная проверка на null
if (OnDataReceived != null)
{
OnDataReceived(msg);
}
}
}
Проанализируем важные детали кода. Мы используем ClassInterface(ClassInterfaceType.None), чтобы заставить COM-клиентов использовать только явно определенные интерфейсы. Атрибут ComSourceInterfaces(typeof(IMyEvents)) — это именно то "магическое" звено, которое сообщает 1С: "Этот объект может генерировать события, описанные в IMyEvents".
Одной компиляции DLL недостаточно. Для того чтобы 1С корректно распознала типы, системе нужна библиотека типов (файл .tlb). Выясним, как правильно зарегистрировать компоненту. Используйте утилиту regasm.exe с ключом /tlb:
regasm MyComLibrary.dll /tlb /codebase
Ключ /tlb создаст файл описания типов и пропишет его в реестре Windows. Ключ /codebase необходим, если ваша библиотека не установлена в GAC (Global Assembly Cache), чтобы система знала путь к файлу DLL.
Посмотрим на пример кода в 1С, который инициализирует объект и подключает обработчик события. Важно помнить, что объект должен существовать в памяти на протяжении всего времени ожидания события.
&НаКлиенте
Процедура ЗапуститьCOMОбъект()
// Создаем объект
МойОбъект = Новый COMОбъект("MyNamespace.MyComServer");
// Подключаем обработчик
// Параметры: Объект, ИмяСобытия, ИмяПроцедурыОбработчика, Контекст
ДобавитьОбработчик МойОбъект.OnDataReceived, ОбработкаПолученияДанных;
// Вызываем метод, который инициирует событие
МойОбъект.DoWork();
КонецПроцедуры
&НаКлиенте
Процедура ОбработкаПолученияДанных(ТекстСообщения)
Сообщить("Событие из C#: " + ТекстСообщения);
КонецПроцедуры
Выясним причину возможной ошибки при вызове ДобавитьОбработчик. Если 1С по-прежнему пишет, что событие не обнаружено, проверьте следующие моменты:
Any CPU.TypeLib, указывающий на созданный .tlb файл.null перед вызовом. Если в 1С не выполнен ДобавитьОбработчик, переменная события в C# будет равна null, и вызов приведет к исключению внутри компоненты.Рассмотрим ситуацию, когда COM-объект создается на сервере 1С. Механизм ДобавитьОбработчик работает преимущественно на клиенте (в управляемых формах). Если вам нужно получать события от COM-объекта, который работает на стороне сервера (например, в фоновом задании), стандартный подход с ДобавитьОбработчик не сработает напрямую для передачи данных на форму пользователя. В таких случаях часто используют промежуточные таблицы базы данных или механизм оповещений, но для классических задач интеграции (весы, сканеры, специфическое оборудование) события на клиенте работают стабильно.
Мы проанализировали процесс создания событий в COM на C#. Главные компоненты успеха: выделенный интерфейс IDispatch с DispId, атрибут ComSourceInterfaces на классе реализации и обязательная генерация TLB при регистрации. Следуя этой структуре, вы сможете создавать надежные компоненты, которые интегрируются в экосистему 1С так же бесшовно, как и стандартные библиотеки.