.Net Core, обмен с 1C по TCP/IP между различными устройствами

    Начну с «Вести с полей». Вышло обновление Updates in .NET Core 1.0.1. Главное из этого для меня было Access violation on Windows – coreclr 6460:
    In Jitstartup, JIT creates a file descriptor for stdout and unconditionally passes it to setmode, without checking for failures. This happens at. Failure to check for invalid descriptors can result in setmode triggering failfast.

    Из-за этой ошибки вылетало исключение при вызове статического .Net-метода в 64 разрядном клиенте 1С.

    Необработанное исключение по адресу 0x00007FFD76FB8528 (ucrtbase.dll) в 1cv8.exe: Недопустимый параметр был передан функции, для которой недопустимые параметры вызывают неустранимую ошибку.

    Сейчас починили и код прекрасно выполняется под 64 разрядным клиентом на 8.3.9. В примерах заменил библиотеки .NET Core на 1.0.1. Хотел написать про SignalR, но пока можно написать только сервер на .Net Core — ASP.NET Core SignalR for Windows 10 UWP App

    aspnet/SignalR-Server

    Клиента пока нет. В WCF пока только клиент под Web сервис. ServiceHost нет. Есть стороннее решение .NET core cross platform remote service invocation

    Но решил написать решение из своего опыта 8 летней давности для обмена данными по TCP/IP между ТСД на Win CE и 1С еще 7-ки. Конечно с 1С можно обмениваться через Web-сервисы, но есть задачи, где нужно взаимодействие с оператором для выбора данных, брать данные подготовленные на клиенте, печать на мобильный принтер.

    Основные проблемы связаны с сетями с плохим соединением на складах. Поэтому, нужно было уменьшить трафик за счет сжатия данных. Так при работе в терминальных сессиях были проблемы с проброской портов в медленных сетях — Тормозит печать чека на фискальный регистратор через RDP.

    Также были проблемы при считывании двумерного штрих кода. Медленная печать с терминального сервера. Для решения этих проблем на машине клиента устанавливалась локальная 1С которая работала как клиент и сервер. Данные со сканеров отправлялись на терминальный сервер и там обрабатывались. Для печати на фискальный регистратор отправлялись данные с сервера по TCP/IP и с локальной 1С печатался чек. При печати этикеток с сервера оправлялись данные, на основании которых на локальной 1С формировался документ и отправлялся на печать.

    Кроме того под многое оборудование для Linux нет драйверов. Можно используя виртуализацию держать Linux и Windows на одной машине, на Windows считывать данные и обмениваться с Linux по TCP/IP.

    Сейчас много у кого есть ТСД под WinCe, WinMo (недавно предлагали работу по настройке обмена на них). Кроме того можно использовать ТСД на других осях используя UWP и Xamarin.

    Кроме того можно обмениваться сообщениями между клиентами 1С, наподобие чата.

    В большом .Net я часто использую обмен по TCp/IP
    Использование сборок .NET в 1С 7.x b 8.x. Создание внешних Компонент.

    Использование ТСД на WM 6 как беспроводной сканер с получением данных из 1С

    Поэтому я решил написать этот же обмен, но на .Net Core и добавить новый подход.

    Чистые 1С ники могут пропустить вражеский код и перейти к родному в конце статьи, как использовать данную компоненту.

    Нужно было создать класс для обмена сообщениями с сжатыми данными. Для отправки данных использовался метод:

            // Отправляем команду на сервер 
            // Отправляем данные на сервер
            // string Команда имя метода который будет обрабатывать данные
            // string ДанныеДляКоманды это сериализованные данные в виде строки
            // bool ЕстьОтвет признак функции или процедуры метода обрабатывающего данные
            public ДанныеОтветаПоTCP ОтправитьКоманду(string АдресСервера, int порт, string Команда, string ДанныеДляКоманды, bool ЕстьОтвет)
    

    На стороне 1С принимается такой класс

     // Данные отправляемые в 1С для обработки запроса
        public class ДанныеДляКлиета1С
        {
    
            public bool ЕстьОтвет;
            public string Команда;
            public string Данные;
            TcpClient Клиент;
            public ДанныеДляКлиета1С(СтруктураСообщения Даннные, TcpClient Клиент)
            {
    
                this.ЕстьОтвет = Даннные.ЕстьОтвет;
                this.Команда = Даннные.Команда;
                this.Данные = Даннные.Данные;
    
                if (ЕстьОтвет)
                    this.Клиент = Клиент;
                else // Если нет ответа то закрываем соединение
                {
                    Клиент.Dispose();
                    this.Клиент = null;
                }
            }
    
    
            // Отсылаем данные клиенту
            //Создадим новую задачу, что бы основной поток 1С не ждал отпраки
            //Ответ пытаемся сжать
            public void Ответить(string Ответ)
            {
                Task.Run(() =>
                {
                    var strim = Клиент.GetStream();
                    ДляОбменаПоТСП.WriteCompressedString(strim, Ответ);
    // Закроем соединение
                    strim.Dispose();
                    Клиент.Dispose();
    
                });
    
            }
    
            public override string ToString()
            {
                return $"ЕстьОтвет={ЕстьОтвет}, Команда={Команда}, Данные={Данные}";
            }
        }
    
    

    Модуль для формирования сообщений который был написан 8 лет назад с небольшими изменениями.

    Уже тогда я вовсю использовал Руслиш.

    Много кода
    public class ДляОбменаПоТСП
    {
    public static readonly Encoding CurrentEncoder;//=Encoding.GetEncoding(1251);

    static ДляОбменаПоТСП()
    {

    //Вот здесо особенность .Net Core
    // Нужно зарегистрировать провайдера
    // и прописать в зависимости «System.Text.Encoding.CodePages»
    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
    // CurrentEncoder = Encoding.GetEncoding(«windows-1251»);
    // Так как мы используем Руслиш то используем кодировку 1251
    CurrentEncoder = Encoding.GetEncoding(1251);

    }

    public static byte[] РасжатьДанные(byte[] массивДанныхДляКоманды)
    {
    var memStream = new MemoryStream(массивДанныхДляКоманды);
    var DecompressStream = new MemoryStream();
    using (GZipStream gzipStream = new GZipStream(memStream, CompressionMode.Decompress, false))
    {

    Byte[] buffer = new Byte[1 << 16];
    int h;
    while ((h = gzipStream.Read(buffer, 0, buffer.Length)) > 0)
    {
    DecompressStream.Write(buffer, 0, h);
    }
    }
    return DecompressStream.ToArray();
    }

    //
    public static byte[] СжатьДанные(byte[] Value)
    {
    var memStream = new MemoryStream();
    memStream.Position = 0;
    using (GZipStream gzipStream = new GZipStream(memStream, CompressionMode.Compress))
    {
    gzipStream.Write(Value, 0, Value.Length);
    gzipStream.Flush();
    }
    return memStream.ToArray();

    }

    // Классичекое чтение из NetworkStream зная размер получаемых данных
    private static byte[] МассивБайтовИзСтрима(NetworkStream стрим, int размерМассива)
    {
    byte[] result = new byte[размерМассива];
    int количествоСчитанныхСимволов = 0;
    while (размерМассива > количествоСчитанныхСимволов)
    {
    количествоСчитанныхСимволов += стрим.Read(result, количествоСчитанныхСимволов, размерМассива — количествоСчитанныхСимволов);
    }

    return result;
    }

    public static void ЗаписатьМассивБайтовВСтрим(NetworkStream стрим, byte[] Массив)
    {

    стрим.Write(Массив, 0, Массив.Length);
    }

    // Считываем из потока 1 байт и конвертируем в bool
    public static bool ReadBool(NetworkStream стрим)
    {
    return BitConverter.ToBoolean(МассивБайтовИзСтрима(стрим,1), 0);
    }

    // Конвертирум bool в 1 байт и записываем в поток
    public static void Write(NetworkStream стрим, bool Value)
    {
    ЗаписатьМассивБайтовВСтрим(стрим, BitConverter.GetBytes(Value));

    }

    // Считываем из потока 4 байта и конвертируем в int
    public static Int32 ReadInt32(NetworkStream стрим)
    {
    return BitConverter.ToInt32(МассивБайтовИзСтрима(стрим,4), 0);
    }

    // Конвертирум int в 4 байта и записываем в поток
    public static void Write(NetworkStream стрим, Int32 Value)
    {
    ЗаписатьМассивБайтовВСтрим(стрим, BitConverter.GetBytes(Value));

    }

    // Считываем строку. Сначала идет размер данных int
    //затем считываем данные и получаем строку используя кодировку 1251
    public static string ReadString(NetworkStream стрим)
    {
    int РазмерДанных=ReadInt32(стрим);
    if (РазмерДанных == 0) return "";

    return CurrentEncoder.GetString(МассивБайтовИзСтрима(стрим, РазмерДанных));
    }

    // Записываем строку. Сначала записываем размер строки, затем конвертируем в byte[] используя кодировку 1251
    public static void Write(NetworkStream стрим, string Value)
    {
    if (Value.Length == 0)
    {
    Write(стрим, 0);
    return;
    }
    byte[] result = CurrentEncoder.GetBytes(Value);
    Write(стрим, result.Length);
    ЗаписатьМассивБайтовВСтрим(стрим,result);

    }

    // Смотри WriteCompressedString это обратная операция
    public static string ReadCompressedString(NetworkStream стрим)
    {
    // int РазмерДанных = ReadInt32(стрим);
    // return CurrentEncoder.GetString(МассивБайтовИзСтрима(стрим, РазмерДанных));
    bool ЭтоСжатаяСтрока = ReadBool(стрим);

    if (! ЭтоСжатаяСтрока) return ReadString(стрим);

    int РазмерДанныхДляКоманды = BitConverter.ToInt32(МассивБайтовИзСтрима(стрим, 4), 0);
    byte[] массивДанныхДляКоманды = МассивБайтовИзСтрима(стрим, РазмерДанныхДляКоманды);
    массивДанныхДляКоманды = РасжатьДанные(массивДанныхДляКоманды);
    return CurrentEncoder.GetString(массивДанныхДляКоманды);

    }

    // Пытаемся сжать строку GZIP. Если размер сжатых данных меньше оригинала то записываем сжатые танные
    //иначе оригинал
    // Записываем данные в следующей последовательности
    //bool флаг сжатия данных
    //int размер данных
    //byte[] данные
    public static void WriteCompressedString(NetworkStream стрим, string Value)
    {
    if (Value.Length == 0)
    {
    Write(стрим, false);
    Write(стрим, 0);
    return;
    }

    byte[] result = CurrentEncoder.GetBytes(Value);
    var СжатыеДанные=СжатьДанные(result);
    if (result.Length>СжатыеДанные.Length)
    {
    Write(стрим, true);
    Write(стрим, СжатыеДанные.Length);
    ЗаписатьМассивБайтовВСтрим(стрим, СжатыеДанные);
    }
    else
    {
    Write(стрим, false);
    Write(стрим, result.Length);
    ЗаписатьМассивБайтовВСтрим(стрим,result);
    }


    }

    // Отправляем данные на сервер
    // string Команда имя метода который будет обрабатывать данные
    // string ДанныеДляКоманды это сериализованные данные ввиде строки
    // bool ЕстьОтвет признак функции или процедуры метода обрабатывающего данные
    public static void ОтправитьКоманду(NetworkStream strim,string Команда, string ДанныеДляКоманды, bool ЕстьОтвет)
    {
    Write(strim, ЕстьОтвет);
    Write(strim, Команда);
    WriteCompressedString(strim, ДанныеДляКоманды);
    }

    // Прочитать данные с клиента
    public static СтруктураСообщения ПринятьКоманду(NetworkStream strim)
    {
    bool ЕстьОтвет=ReadBool(strim);
    string Команда=ReadString(strim);
    string ДанныеДляКоманды=ReadCompressedString(strim);
    return new СтруктураСообщения(Команда, ДанныеДляКоманды, ЕстьОтвет);
    }
    }



    На сервере создается класс для прослушивания:

    // Класс для получения и отправки сообщений
        public class TCPConnector
        {
            
            TcpListener Server;
    
            // Будем записывать ошибки в файл
            // Нужно прописать в зависимости "System.Diagnostics.TextWriterTraceListener"
            // Файл будет рядом с этой DLL
            TextWriterTraceListener myTextListener;
    
            // Устанавливаем флаг при закрытии
            bool ЭтоЗакрытие = false;
            // Клиент для отпраки сообщений на сервер
            Socket клиент;
           
            // делегат для вызова внешнего события в 1С
            // Который ставит сообщение в очередь событий в 1С
            public Action<string, string, object> ВнешнееСобытие1С;
    
            //Делегат для вывода ошибки в окне сообщений
            public Action<string> СообщитьОбОшибкев1С;
    
            // Получаем директорию сборки содержащий данный класс
            string AssemblyDirectory
            {
                get
                {
                    string codeBase = typeof(TCPConnector).GetTypeInfo().Assembly.Location;
                    UriBuilder uri = new UriBuilder(codeBase);
                    string path = Uri.UnescapeDataString(uri.Path);
                    return Path.GetDirectoryName(path) + @"\";
                }
            }
    
            public TCPConnector()
            {
    
    
                myTextListener = null;
    
            }
    
            // Записываем ошибку a файл и сообщаем об ошибке в 1С
            void ЗаписатьОшибку(string Ошибка)
            {
                if (myTextListener == null)
                {
                    try
                    {
                        FileStream fs = new FileStream(AssemblyDirectory + @"ТрассировкаОтладки",
                        FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
    
                        StreamWriter myOutputWriter = new StreamWriter(fs, Encoding.GetEncoding(1251));
                        myTextListener = new TextWriterTraceListener(myOutputWriter);
                        Trace.Listeners.Add(myTextListener);
    
                    }
                    catch (Exception)
                    {
    
                     // проглотим ошибку что бы приложение закрылось
                    }
                }
    
                Trace.WriteLine(Ошибка);
                Trace.Flush();
                СообщитьОбОшибкев1С?.DynamicInvoke(Ошибка);
            }
    
    
    
            // Откроем порт и количество слушющих задач которое обычно равно подсоединенным устройствам
            // Нужно учитывть, что 1С обрабатывает все события последовательно ставя события в очередь
            public void Открыть(int НомерПорта = 6891, int КоличествоСлушателей = 1)
            {
                ЭтоЗакрытие = false;
    
                IPEndPoint ipEndpoint = new IPEndPoint(IPAddress.Any, НомерПорта);
                Server = new TcpListener(ipEndpoint);
                Server.Start();
    
                // Создадим задачи для прослушивания порта
                //При подключении клиента запустим метод ОбработкаСоединения
                // Подсмотрено здесь https://github.com/imatitya/netcorersi/blob/master/src/NETCoreRemoveServices.Core/Hosting/TcpServerListener.cs
                for (int i = 0; i < КоличествоСлушателей; i++)
                    Server.AcceptTcpClientAsync().ContinueWith(ОбработкаСоединения);
    
            }
    
    
    // Метод для обработки сообщения от клиента
            private void ОбработкаСоединения(Task<TcpClient> task)
            {
    
                if (task.IsFaulted || task.IsCanceled)
                {
                    // Скорее всего вызвано  Server.Stop();
                    return;
                }
    
                // Получим клиента
                TcpClient client = task.Result;
    
                // И вызовем метод для обработки данных
                // 
                ВыполнитьКоманду(client);
    
                // Если Server не закрыт то запускаем нового слушателя
                if (!ЭтоЗакрытие)
                    Server.AcceptTcpClientAsync().ContinueWith(ОбработкаСоединения);
    
            }
    
    
           
    
            private void ВыполнитьКоманду(TcpClient client)
            {
    
                NetworkStream стрим = client.GetStream();
                try
                {
    
                    // Получим данные с клиента и на основании этих данных
                    //Создадим ДанныеДляКлиета1С котрый кроме данных содержит 
                    //TcpClient для отправки ответа
                    var Данные = new ДанныеДляКлиета1С(ДляОбменаПоТСП.ПринятьКоманду(стрим), client);
    
                    // Вызвается метод 1С для постановки сообщения в очередь
                    // Которое будет обработано через ВнешнееСобытие
                    ВнешнееСобытие1С?.DynamicInvoke("TCPConnector", Данные.Команда, Данные);
    
                }
                catch (Exception e)
                {
                    ЗаписатьОшибку(DateTime.Now.ToString() + e.ToString());
    
                }
            }
    
    
            // Закроем ресурсы
            public void Закрыть()
            {
                if (Server != null)
                {
                    ЭтоЗакрытие = true;
                    Server.Stop();
                    Server = null;
    
    
                }
                if (myTextListener != null)
                {
    
                    Trace.Listeners.Remove(myTextListener);
                    myTextListener.Dispose();
                }
    
            }
    

    Все достаточно просто. При соединении считываем данные, создаем объект для отправки в 1С. Запускаем нового слушателя.

    Отправка сделана на голых сокетах можно посмотреть в исходниках.

    Упрощенно это выглядит так:

    IPEndPoint ipEndpoint = new IPEndPoint(IPAddress.Parse(АдресСервера), порт); //6891 по умолчанию
                    клиент = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    клиент.Connect(ipEndpoint);
    
                    var поток= new NetworkStream(клиент);
                    ДляОбменаПоТСП.ОтправитьКоманду(поток, Команда, ДанныеДляКоманды, ЕстьОтвет);
                    
                    // считываем сжатые данные в строку
                    if (ЕстьОтвет) result = ДляОбменаПоТСП.ReadCompressedString(strim);
    
                   поток.Dispose();
                   клиент.Dispose();
    

    Вот как это обрабатывается в 1С:

    // В Net core для NetStandard System.Threading.Tasks не существует 
    	 Task=ъТип("System.Threading.Tasks.Task","System.Threading.Tasks");
    
    Процедура СоздатьСерверTCP()
    	 
    	 Если СерверTCP<>Неопределено Тогда
    		 возврат
    	 КонецЕсли; 
    	 
    	 TCPConnector=ъТип("TCPConnectTo1C.TCPConnector","ОбменПоTCPIPCore.dll");
    	 СерверTCP=ъНовый(TCPConnector.ПолучитьСсылку());
    	 Ссылка=СерверTCP.ПолучитьСсылку();
    	 Врап.УстановитьДелегатДляВызоваВнешнегоСобытия(Ссылка,"ВнешнееСобытие1С");
    	 Врап.УстановитьДелегатДляСообщенииОбОшибке(Ссылка,"СообщитьОбОшибкев1С");
    	 
     КонецПроцедуры// СоздатьTCP()
     
     Процедура ТестTCPConnectНажатие(Элемент)
    	 
    	 // Установим размер очереди событий равный удвоенному количеству
    	 //обслуживаемых устройств
    	 // Но нужно учесть, что запросы без ответа ставятся в очередь 1С 
    	 // и сразу закрывается соединение
    	 // Клиент не ждет
    	 // Если будут проблемы нужно посылать запрос с ответом
    	 Сообщить(Врап.УстановитьРазмерОчередиСобытий(3*2));
    	 Сообщить(Врап.УстановитьРазмерОчередиСобытий(3*2));
    	 СоздатьСерверTCP();
    	 СерверTCP.Открыть(6891,3);
    	 
    	 ЭлементыФормы.ДанныеДляОтправки.Видимость=ложь;
    	 ЭлементыФормы.ОтправитьКоманды.Видимость=ложь;
    	 ЭлементыФормы.НадписьДанныеДляОтправки.Видимость=ложь;
    	 
     КонецПроцедуры
     
     Процедура СканированШК(знач Данные)
    	 
    	 // Съэмулируем долгую обработку для проверки очереди событий
    	 ъ(Task.Delay(1000)).Wait();
    	 Ответ="Ответ на команду "+Данные.Команда+"
    	 |Данные "+Данные.Данные+"
    	 |ВремяНаСервере="+XmlСтрока(ТекущаяДата());
    	 Данные.Ответить(Ответ);
     КонецПроцедуры
     
     Процедура ВыполнитьБезОтвета(знач Данные)
    	 // Съэмулируем долгую обработку для проверки очереди событий
    	 ъ(Task.Delay(1000)).Wait();
     КонецПроцедуры
     
     // Для теста из других компонент
     Процедура ПолучениеДанныхПоTCP(знач Данные)
    	 Сообщить("Команда="+Данные.Команда);
    	 Сообщить("Данные="+Данные.Данные);
    	 Сообщить("ЕстьОтвет="+Данные.ЕстьОтвет);	
    	 
    	 ъ(Task.Delay(1000)).Wait();
    	 Если Данные.ЕстьОтвет Тогда
    		 Ответ="Ответ на команду "+Данные.Команда+"
    		 |Данные "+Данные.Данные+"
    		 |ВремяНаСервере="+XmlСтрока(ТекущаяДата());
    		 Данные.Ответить(Ответ);
    	 КонецЕсли; 
    	 
     КонецПроцедуры
     
     
     Процедура ВнешнееСобытие(Источник, Событие, Данные)
    	 
    	 Если Источник="TCPConnector" Тогда
                   //Получим объект по переданной ссылке
    		 Данные=ъ(Данные);
    		 Сообщить("Данные="+Врап.ВСтроку(Данные.ПолучитьСсылку()));
    		 // Тест из отчета  ТестNetObjectToIDispatch
    		 Если Событие="Тест Отправки Сообщения" Тогда
    			 
    			 ПолучениеДанныхПоTCP(Данные)	
    		 иначе
                   // Запускаем метод переданный в коанде
    			 Выполнить(Событие+"(Данные)");
    			 
    		 КонецЕсли; 
    	 КонецЕсли; 
    	 
     КонецПроцедуры
     
     Процедура ОтправитьКоманду(знач КлиентTCP,ServerAdress,порт,Команда,ДанныеДляКоманды,ЕстьОтвет)
    	 
    	 резулт=ъ(КлиентTCP.ОтправитьКоманду(ServerAdress,порт,Команда,ДанныеДляКоманды,ЕстьОтвет));
    	 Сообщить(Врап.ВСтроку(резулт.ПолучитьСсылку()));	
    	 Если резулт.ОшибкаСоединения Тогда
    		 СтрОшибки="ОшибкаСоединения
    		 |"+резулт.Данные;
    		 Предупреждение(СтрОшибки);
    	 КонецЕсли;
    	 
    	 
     КонецПроцедуры
     
     Процедура ОтправитьКомандыНажатие(Элемент)
    	 СоздатьСерверTCP();
    	 КлиентTCP=СерверTCP;
    	 ServerAdress="127.0.0.1";
    	 порт=6891;
    	 Команда="Тест Отправки Сообщения";
    	 ДанныеДляКоманды=XmlСтрока(ТекущаяДата());
    	 
    	 ЕстьОтвет=истина;
    	 ЗакрытьСоединение=истина;
    	 ОшибкаСоединения=false;
    	 
    	 Для сч=1 По 3 Цикл
    		 
    		 ОтправитьКоманду(КлиентTCP,ServerAdress,порт,Команда,ДанныеДляКоманды,истина);
    		 ОтправитьКоманду(КлиентTCP,ServerAdress,порт,"ВыполнитьБезОтвета",ДанныеДляОтправки,ложь);
    		 ОтправитьКоманду(КлиентTCP,ServerAdress,порт,"СканированШК","12345678901",истина);
    	 КонецЦикла; 	
    	 
     КонецПроцедуры
     
     Процедура ПриЗакрытии()
    	 // Вставить содержимое обработчика.
    	 Если СерверTCP<> неопределено Тогда
    		 
    		 СерверTCP.Закрыть();
    		 СерверTCP=Неопределено;
    	 КонецЕсли; 
    	 
    	 GC=ъТип("System.GC");
    	 GC.Collect();
    	 GC.WaitForPendingFinalizers();
    	 Врап=Неопределено;
    	 
     КонецПроцедуры
    
    

    Ответ передаем через полученный объект:

     Данные.Ответить(Ответ);
    

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

    Так как можно работать с несколькими устройствами то нужно установить нужный размер очереди через:

    Врап.УстановитьРазмерОчередиСобытий(размер очереди));
    

    Который возвращает текущий размер очереди.

    Конечно, можно запустить несколько приложений 1С и запустить TCP/IP сервер под разными портами. но по практике операторы путаются. Чем проще для них, тем лучше.

    Для установки нужных делегатов используются методы

    Врап.УстановитьДелегатДляВызоваВнешнегоСобытия(Ссылка,"ВнешнееСобытие1С");
    Врап.УстановитьДелегатДляСообщенииОбОшибке(Ссылка,"СообщитьОбОшибкев1С");
    

    В зависимости от типа делегата устанавливается нужный делегат:

       if (ReturnType == typeof(Action<string, string, object>)) return new Action<string, string, object>(ВызватьВнешнееСобытиеСОбъектом);
    
                if (ReturnType == typeof(Action<string, string, string>)) return new Action<string, string, string>(AutoWrap.ВызватьВнешнееСобытие1С);
    


    Конечно, можно использовать события и динамическую компиляцию, см публикацию «Разработка → 1С,.Net Core. Динамическая компиляция класса обертки для получения событий .Net объекта в 1С».

    Но раз пишем под 1С, то проще объявить делегаты нужного типа, и установить из 1С.

    Для теста нужно использовать 3 клиентов 1С и вызвать ТестОбменПоTCPIP.epf для проверки очереди событий в 1С.

    Исходники можно скачать здесь.
    Поделиться публикацией

    Похожие публикации

    Комментарии 16

      +2
      Закройте портал в ад.
        0
        Уже закрыли. По просьбам трудящихся.
          +1
          if (!ЭтоЗакрытие)
                          Server.AcceptTcpClientAsync().ContinueWith(ОбработкаСоединения);
          

          Это же какой-то кошмар. Если приводите код на c# будьте любезны писать код на английском языке.
            +1
            Мне одному кажется вот это
            NetworkStream стрим = client.GetStream()
            ужасным? Это прям как метание между двумя ориентациями. Определитесь уже кто вы такой.
              0
              Я 1С ник пишущий на других языках и использующий Руслиш и транслитерацию
                0
                Так будьте любезны на других языках писать по английски, а на своём, кх кх кх, 1С по русски.

                Просто получается у Вас мухи и котлеты в перемешку идут.
                  0
                  На самом деле это проблема обсуждалась в предыдущих 8 статьях. Все ответы я дал там.
                0
                Имхо. Жутко не удобно писать в двух раскладках. Это потеря скорости и отвлекание от процесса.
                  0
                  Наоборот. У меня пунто стоит, но при этом мне не нужно тратить время на переводы.
                • НЛО прилетело и опубликовало эту надпись здесь
                    +1
                    Вставлю свои 5 копеек. Про Хабр часто говорят, что дескать нет технических статей, только реклама. Вот вам автор, который написал 9 статей. Хороших таких статей про работу .NET/1C, котоые потом вполне возможно будутпользоваться спросом, в свое время. Но его заминусовали (карма) за руслиш. Видите-ли он не пишет именно так, как нравится вам. Ему за это не платят, замечу. Он хотел сделать вам приятно. А теперь его карма = 0 и он больше не может писать статьи, хотя хочет.
                      0
                      Спасибо большое за поддержку. Но и эту статью задвинули в чулан.
                        0

                        Если бы в этих статьях было хотя бы понятно какая проблема решается столь запутанным образом...

                          –1
                          Например работа с ТСД. Простейший вариант Использование ТСД на WM 6 как беспроводной сканер с получением данных из 1С

                          Но можем используя Xamarin использовать ТСД на Андроид или IOS
                            0

                            Вы можете хоть раз объяснить что вы вообще делаете не ссылаясь на другие свои посты?

                              0
                              В данном случае приведен алгоритм обменна данных по Tcp/IP с любым устройством. 1С может выступать как в качестве сервера Tcp/Ip так и клиента. В качестве устройства обмена по Tcp/IP могут выступать ТСД, компьютеры в локальной сети или в сети VPN итд

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое