Разработка внешних компонент на языке C# для платформы 1С:Предприятие — это эффективный способ расширить возможности системы, когда стандартных средств встроенного языка недостаточно. Чаще всего это требуется для низкоуровневой работы с оборудованием, например, с COM-портами сканеров штрихкодов или картридеров. В данной статье мы подробно разберем процесс создания такой компоненты, механизм передачи данных через внешние события и устраним типичные ошибки, возникающие при регистрации и инициализации объектов.
Для начала создадим проект в Microsoft Visual Studio с типом Библиотека классов (.NET Framework). Чтобы 1С могла взаимодействовать с нашей библиотекой как с внешней компонентой, нам необходимо реализовать COM-интерфейсы. Рассмотрим базовую структуру класса, который будет отвечать за работу с торговым оборудованием.
Проанализируем пример кода, в котором описывается основной интерфейс и класс компоненты. Важно использовать атрибуты Guid и ProgId, чтобы система могла однозначно идентифицировать наш объект в реестре Windows.
[GuidAttribute("0E2BFC3D-7FF8-4bb8-AC4D-2FAA332B80A3")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[ComVisible(true)]
public interface IWorkCompsMain
{
string Version { get; }
}
[Guid("38B5DCD1-8FE9-4a46-B9C3-C9A1CD60F6E4")]
[ProgId("AddIn.CardReaderMR2000")]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[ComVisible(true)]
public class CardReader : ExtComponentBase, IWorkCompsMain
{
private SerialPort sp;
private string inputData = "";
public CardReader()
{
ComponentName = "CardReaderMR2000";
}
[Export1c]
public void Initialization()
{
sp = new SerialPort();
sp.PortName = "COM4";
sp.BaudRate = 9600;
sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
sp.Open();
}
}
Обратите внимание на использование атрибута [Export1c]. Это кастомный атрибут, который часто применяется в базовых шаблонах (например, от RSDN) для автоматического экспорта методов и свойств в метаданные 1С. Без правильной реализации интерфейса ILanguageExtender 1С не "увидит" ваши методы.
После сборки проекта (Build) мы получаем DLL-файл. Для работы через COM его необходимо зарегистрировать. Выясним важный нюанс: разрядность утилиты регистрации должна совпадать с разрядностью клиентского приложения 1С. Если вы используете 32-битный клиент 1С (что является стандартом для большинства конфигураций на обычных формах), регистрацию нужно проводить через 32-битную версию regasm.exe, даже если ОС 64-битная.
Выполним регистрацию следующей командой от имени администратора:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe "D:\DLL\New.dll" /codebase /tlb
Параметр /codebase позволяет регистрировать библиотеку, не помещая её в GAC (Global Assembly Cache), что значительно упрощает процесс разработки и тестирования. Параметр /tlb создает библиотеку типов, которая может быть полезна для отладки.
Рассмотрим два способа создания объекта. Первый способ — через механизм Новый COMОбъект. Этот метод позволяет обращаться к свойствам и методам, но не позволяет получать внешние события, так как 1С не инициализирует интерфейс обратного вызова.
Правильный способ для получения событий — использование метода УстановитьВнешнююКомпоненту или ПодключитьВнешнююКомпоненту. Разберем по шагам правильную последовательность действий в 1С:
// 1. Подключаем библиотеку
Результат = ПодключитьВнешнююКомпоненту("AddIn.CardReaderMR2000");
Если Результат Тогда
// 2. Создаем экземпляр объекта
Попытка
ОбъектВК = Новый("AddIn.CardReaderMR2000");
ОбъектВК.Initialization();
Исключение
Сообщить("Не удалось создать объект компоненты!");
КонецПопытки;
КонецЕсли;
Если при вызове Новый("AddIn...") возникает ошибка "Тип не определен", это означает, что ProgID в коде C# не совпадает с тем, что вы указываете в 1С, либо регистрация прошла неудачно. Проверьте ветку реестра HKEY_CLASSES_ROOT на наличие вашего ProgId.
Самая сложная часть — передача данных из фонового потока (чтение из порта) в основной поток 1С. В .NET событие DataReceived возникает в отдельном потоке. Если попытаться вызвать метод 1С напрямую, это может привести к аварийному завершению программы (Crash).
Проанализируем код внутри C#, который отвечает за отправку события:
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string data = sp.ReadExisting();
// Важно: Async - это ссылка на интерфейс IAsyncEvent,
// которую 1С передает в метод Init() компоненты.
if (this.Async != null)
{
this.Async.ExternalEvent("AddIn.CardReaderMR2000", "CardData", data);
}
}
В 1С это событие будет перехвачено в процедуре ВнешнееСобытие формы или модуля приложения:
Процедура ВнешнееСобытие(Источник, Событие, Данные)
Если Источник = "AddIn.CardReaderMR2000" Тогда
Сообщить("Получены данные с карты: " + Данные);
КонецЕсли;
КонецПроцедуры
GetNParams. В некоторых случаях помогает добавление фиктивного параметра в метод на стороне C#.Новый COMОбъект. Используйте только Новый("AddIn.Имя") после предварительного подключения компоненты.Control.Invoke, если есть скрытое окно, или специализированные обертки для 1С).Выясним еще один момент: при разработке на C# крайне важно корректно завершать работу с COM-портом. Обязательно реализуйте метод Close() или Dispose(), чтобы при закрытии 1С порт не оставался заблокированным в системе, что предотвратит ошибки при повторном запуске приложения.
Соблюдение этих правил позволит вам создать надежную и быструю внешнюю компоненту, которая обеспечит стабильную связь 1С с любым внешним оборудованием через COM-интерфейс.