Имя: Пароль:
1C
1С v8
Как выгрузить COM dll ?
0 DES
 
20.11.15
20:58
сделал на C# свою COM dll.
Загружаю на сервере 1С.
Все работает.
Но вот кажется dll создает проблему при загрузке *.dt.
Так как система ругается что dll захвачена какой -то программой и ее заменить не удается, пока не перезагружу 1с-сервер.
Т.е. при обращении клиента к серверу он грузит dll и захватывает ее файл и держит до скончания веков. Как отпустить файл dll в у конце процесса на сервере?
1 mehfk
 
20.11.15
21:00
"ругается что dll захвачена какой -то программойЙ"

Скриншот покажи.
2 DES
 
20.11.15
21:01
ну не вопрос какой программой, вопрос как освободить.
А ругается при загрузке *.dt winsock откинулся на стороне сервера.
3 ДенисЧ
 
20.11.15
21:12
так и должно быть.
Расслабься и выпей коньяку.
4 DES
 
20.11.15
21:19
ну неужели нельзя вернуть память в кучу ??
5 Serginio1
 
20.11.15
21:37
(4) О ты уже на C# программируешь. Быстро.
По сабжу то это не простая манагед память, а скомпилированная нативная. Она не выгружается. Можно выгрузить только домен полностью, но это по сути отдельный процесс в едином адресном пространстве
6 su_mai
 
20.11.15
21:57
(0) поставь process explorer и по имении dll найди программу её использующую.

https://technet.microsoft.com/ru-ru/sysinternals/processexplorer.aspx
7 su_mai
 
20.11.15
21:59
(2) Какой то поток в dll работает, может TCPListener слушает порт или что то еще?
8 Serginio1
 
20.11.15
22:28
5+ DynamicMethod подвергается сборке мусора.
Кстати, то что DLL не выгружается есть и свои плюсы. Ты можешь в статических полях держать данные. По сути это аналог переменных на сервере правда только в одном рабочем процессе
9 DES
 
20.11.15
22:34
(8) это все понятно, плохо то что не могу загрузить данные с рабочей базы в тестовую на том же сервере. Нужно перезапустить процесс 1С,  а это не всегда возможно. А как перезагрузить сервер 1с для конкретного инстанса ?
10 DES
 
20.11.15
22:36
(2) Да, работаю с winsock , но вроде бы закрываю канал сразу после чтения
11 DES
 
20.11.15
22:36
(10)->(7)
12 H A D G E H O G s
 
20.11.15
22:37
"в статических полях держать данные"
все равно указатель на клиент возвращать
13 DES
 
20.11.15
22:38
Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

sender.Shutdown(SocketShutdown.Both);
sender.Close();
14 H A D G E H O G s
 
20.11.15
22:42
(13) попробуй

FreeLibraryAndExitThread( HlNSTANCE hinstDll, DWORD dwExitCode);
15 H A D G E H O G s
 
20.11.15
22:47
очень годная статья про dll

http://wm-help.net/books-online/print-page/59464/59464-14.html

из нее я только что узнал про переадресацию
16 su_mai
 
20.11.15
22:48
(11) Открой процесс dll в process explorer и посмотри в Threads и LAN что творится
17 DES
 
20.11.15
22:56
(5) кстати, могу выложить код сюда, чтобы маэстро оценил труды.
18 Serginio1
 
20.11.15
23:04
(17) Главное, что мои труды не пропали зря. Молодец.
19 DES
 
20.11.15
23:35
я, вообщем то, удивляюсь на этот код, но он работает, причем на сервере.


using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Net;
using System.Net.Sockets;

namespace Online
{
    [Guid("13011962-0013-0001-1962-001301196200")]
    internal interface IMyClass
    {
        [DispId(1)]
        string Exec(string ConnectString, string CMD, string partXML, bool Otladka);
    }
    [Guid("70DD7E62-7D82-4301-993C-B7D919430992"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IMyEvents
    {
    }
    [Guid("69EE0677-884A-4eeb-A3BD-D407844C0C72"), ClassInterface(ClassInterfaceType.None), ComSourceInterfaces(typeof(IMyEvents))]
    public class Utilites : IMyClass
    {
        public string Exec(string LogPass, string CMD, string oXML, bool Otladka = false)
        {
            string Otvet = "";
            string[] AllHERE;
            AllHERE = LogPass.Split(new char[] { ':', '@' }, 4);
            string myLog = AllHERE[0];
            string myPass = AllHERE[1];
            string IPaddr = AllHERE[2];
            int IPport = Int32.Parse(AllHERE[3]);
            StringBuilder sb = new StringBuilder();
            sb.Append("AUTH\0\0" + (char)(22) + "\0");
            sb.Append((char)myLog.Length);
            foreach (char ch in myLog) sb.Append(ch);
            for (int i = myLog.Length; i < 10; i++) sb.Append(" ");
            sb.Append((char)myPass.Length);
            foreach (char ch in myPass) sb.Append(ch);
            for (int i = myPass.Length; i < 10; i++) sb.Append(" ");
            sb.Append("SEND\0\0");
            string XML = "";
            if (CMD == "ExecNature")
                XML = oXML;
            else
                XML = "<?xml version=\"1.0\" encoding=\"Windows-1251\"?><Request><RequestName>" + CMD + "</RequestName>" + oXML + "</Request>";
            byte[] byteLen = BitConverter.GetBytes((UInt16)XML.Length);
            sb.Append((char)byteLen[0]); // тут режет старший бит, поэтому просто резервируем место  
            sb.Append((char)byteLen[1]);
            foreach (char ch in XML) sb.Append(ch);
            string strXML = sb.ToString();
            byte[] byteXML = Encoding.ASCII.GetBytes(strXML);
            byteXML[36] = byteLen[0]; // а тут перезаписываем длину XML правильно
            byteXML[37] = byteLen[1];
            if (Otladka)
                Otvet = XML;
            else
            {
                try
                {
                    IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(IPaddr), IPport);
                    Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    try
                    {
                        byte[] bytes = new byte[0x1000];
                        sender.Connect(remoteEP);
                        int bytesSent = sender.Send(byteXML);
                        int bytesRec = sender.Receive(bytes);
                        if (Encoding.ASCII.GetString(bytes, 0, 4) == "recv")
                        {
                            bytesRec = sender.Receive(bytes);
                            byte[] AskByte = new byte[bytesRec];
                            System.Buffer.BlockCopy(bytes, 0, AskByte, 0, bytesRec);
                            Otvet = Encoding.GetEncoding("Windows-1251").GetString(AskByte);
                        }
                        else
                        {
                            Otvet = "ERROR";
                        }
                    }
                    catch (ArgumentNullException ane) { Otvet = ane.ToString(); }
                    try
                    {
                        sender.Shutdown(SocketShutdown.Both);
                        sender.Close();
                    }
                    finally {};
                }
                catch (SocketException se) { Otvet = se.ToString(); }
            }
            return Otvet;
        }
    }
}
20 DES
 
20.11.15
23:39
все таки есть какой то метод выгрузки,
только куда его примастырить ?
21 Serginio1
 
20.11.15
23:49
Так на будущее  проще и моднее использовать WCF
22 su_mai
 
21.11.15
06:16
(19) Вызывает смущение:
try
{
   sender.Shutdown(SocketShutdown.Both);
   sender.Close();
}
finally {};

нет catch и finally пустой

Вообще так не пишут используют конструкцию

using(var obj = new MyClass)
{
  трололо
}

https://msdn.microsoft.com/ru-ru/library/yh598w02.aspx
23 DES
 
21.11.15
11:02
(22) как конкретно нужно бы ?
24 su_mai
 
21.11.15
11:37
может так

//Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
using(Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
    try
    {
        byte[] bytes = new byte[0x1000];
        sender.Connect(remoteEP);
        int bytesSent = sender.Send(byteXML);
        int bytesRec = sender.Receive(bytes);
        if (Encoding.ASCII.GetString(bytes, 0, 4) == "recv")
        {
            bytesRec = sender.Receive(bytes);
            byte[] AskByte = new byte[bytesRec];
            System.Buffer.BlockCopy(bytes, 0, AskByte, 0, bytesRec);
            Otvet = Encoding.GetEncoding("Windows-1251").GetString(AskByte);
        }
        else
        {
            Otvet = "ERROR";
        }
    }
    catch (ArgumentNullException ane)
    {
        Otvet = ane.ToString();
    }

    sender.Shutdown(SocketShutdown.Both);
    sender.Close();
}
25 DES
 
21.11.15
15:16
не понял
Убрать try  с sender.Shutdown ?
А если канал не открылся ?
26 su_mai
 
21.11.15
17:08
(25) Нельзя маскировать исключение в фиктивном блоке try или Попытка, что в C#?, что в 1С. Ошибка может скрыться, а неуправляемые ресурсы повиснуть, подвесив сам объект в куче или хрен знает что еще.
27 Serginio1
 
21.11.15
17:34
(26) finally  ничего не маскирует она нужна для очистки ресурсов перед вызовом исключения по стеку
https://msdn.microsoft.com/ru-ru/library/zwc8s4fz.aspx

В его примере в его блок просто не нужен.

В рамках обработки исключений, связанный блок finally гарантированно будет выполнен.  Однако если исключения необработано, то выполнение блока finally зависит от того, как активирована операция очистки исключения.  Это, в свою очередь, зависит от того, как настроен компьютер. Дополнительные сведения см. в статье Обработка необработанных исключений в CLR.
28 su_mai
 
21.11.15
17:39
(27) Там внизу мелким шрифтом:
29 su_mai
 
21.11.15
17:39
(28) C# также содержит оператор using, который предоставляет аналогичную функциональность для объектов IDisposable в удобном синтаксисе.
30 DES
 
21.11.15
20:02
(26) а разве try  не есть аналог транзакции ?
происходит откат всех действий, в том числе и удаление всех переменных ?
31 su_mai
 
21.11.15
21:39
(30) Тема управления памятью CLR объемная, но хорошо описана.
Если упрощенно, то среда CLR выделяет память в "управляемой куче" для новых объектов оператором new. Когда на объект нет ссылок он признается подлежащим удалению и помещается в очередь удаления. Со временем уборщик мусора удалит его.

Однако, если во время выполнения объект захватывал внешние неуправляемые CLR данные и не освободил их сам, то образуются утечки памяти. Например если создать временный файл на диске и не удалить его то объект удалиться, а файл останется. (Такое же поведение и в транзакциях.)

Есть правило, если класс оперирует неуправляемыми данными, то он должен наследовать интерфейс IDisposable и соответственно реализовать метод Dispose(), в котором освободить все "хвосты".

При использовании таких объектов необходимо учитывать, что выполнение программы может прерваться из-за исключения, по этому используют конструкцию

<Создание объекта>
try
{
<Использование объекта>
}
finally
{
<Вызов метода объекта Dispose>
}

Это громоздкая конструкция заменяется элегантной
using(<Создание объекта>)
{
<Использование объекта>
}
Компилятор преобразует её в конструкцию try finally.
32 Serginio1
 
21.11.15
22:50
(29) Я к (26), что finally ничего не маскирует
33 su_mai
 
22.11.15
14:49
(32) Да, оно просто не успевает, его процесс "убивают" раньше...
34 Serginio1
 
22.11.15
15:10
(33) Еще раз finally ничего не маскирует. Так же вызывается исключение. Просто если бы внутри finally  закрывались ресурсы смысл в нем был бы. Плюс если бы по стеку в  catch обрабатывалось исключение
35 Serginio1
 
22.11.15
15:13
(33) Кстати, а почему ты не используешь
http://catalog.mista.ru/profile/82159/public/
36 su_mai
 
22.11.15
15:23
(34) И я о том же, просто некорректно сказал про маскировку. Я хотел сказать, что создается видимость обработки исключения. Поэтому я и распинался про using.

(35) А должен? :)
37 Serginio1
 
22.11.15
16:00
(36) finally  не обрабатывает исключение

Так и расскажи почему тебя не заинтересовало.

Например с http://catalog.mista.ru/public/238584/  можно отказаться от создания ВК. Особенно с динамической компиляцией обертки событий . Можешь получить глобальный контекст для вызова функций 1С, а так же получения интерфейсов IAsyncEvent,IExtWndsSupport итд.
Кстати там обертка для глобального контекста, так как тип и объект при вызове

            if (args.Length == 1 && args[0].GetType() == typeof(System.Object[]))
                result = ТипГК.InvokeMember(binder.Name, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod, null, App1C, (System.Object[])args[0]);
            else
                result = ТипГК.InvokeMember(binder.Name, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod, null, App1C, args);

Это не есть сущности одного объекта

ТипГК = ГК1С.GetType();
App1C = ГК1С.AppDispatch;

Там куча вещей которые можно использовать.
38 Serginio1
 
22.11.15
16:07
Кстати про фильты искключений в C# 6
http://rsdn.ru/article/RSDN-2014-2/Docs/11-12-filters/11-12-filters.xml
39 su_mai
 
22.11.15
16:09
(37) (38) Спасибо посмотрю на досуге!
40 DES
 
23.11.15
14:24
раз вы уже тут, то поясните про GUID-ы
ИХ там 3 шт, Какой в них смысл.
Не один из них не нашел в реестре по-ходу после регистрации dll.
41 Serginio1
 
23.11.15
15:07
ProgID поставь.У тебя же есть примеры.

    [ComVisible(true)]
    [ProgId("NetObjectToIDispatch45")]
    [ClassInterface(ClassInterfaceType.AutoDispatch)]
    [Guid("DFDADA57-B22C-4276-928A-8B91C9891FF1")]
    public class NetObjectToIDispatch45

Тем более испорльзуя .NET(C#) для 1С. Динамическая компиляция класса обертки для использования .Net событий в 1С через ДобавитьОбработчик или ОбработкаВнешнегоСобытия
Писать COM объект не нужно
http://catalog.mista.ru/public/417830/
42 Serginio1
 
23.11.15
15:08
Или там можно сгенерировать как C# файлы, так и 1С методы событий
43 Serginio1
 
23.11.15
15:10
Если ты библиотеку подгружаешь через NetObjectToIDispatch45 то зачем тебе засорять регистр?
44 DES
 
23.11.15
16:47
нет, я гружу только свою ДЛЛ  и она работает. (без оберток)
примеры то я взял, но что к чему не разобрался до конца.
45 Serginio1
 
23.11.15
17:33
А как ты в 1С подключаешь?
46 Serginio1
 
23.11.15
17:40
А вообще тебе интерфейсы не нужны. Ты их не используешь.
Нужно только
[ComVisible(true)]
    [ProgId("ИмяТвоегоКласса")]
    [ClassInterface(ClassInterfaceType.AutoDispatch)]

    [Guid("AFDADA57-B22C-4276-928A-8B91C9891FF1")]
public class Utilites

Тогда вызов из 1С
объект=Новый ComОбъект("ИмяТвоегоКласса");
и все публичные методы и непомеченные как  [ComVisible(false)]

будут видны
47 xaozai
 
23.11.15
18:03
Об = Новый COMОбъект("ТвойОбъект");
//работа с объектом компонента
//...
Об.ЗавершитьРаботуКомпонента();//если предусмотрено (может и не быть)
Об = Неопределено;//таким образом связь с объектом разрывается и объект должен выгрузиться из памяти, если написан корректно
48 DES
 
23.11.15
20:41
(47) Об.ЗавершитьРаботуКомпонента()
а что в DDL в этой функуции ?
49 DES
 
23.11.15
20:42
(46) это [Guid("AFDADA57-B22C-4276-928A-8B91C9891FF1")]
от фонаря или строго определенный   ?
50 H A D G E H O G s
 
23.11.15
20:46
(48) Ничего.
Это еротические фантазии автора (47).
51 H A D G E H O G s
 
23.11.15
20:51
(49) Давай мы попробуем что-то придумать, если:

1) Ты можешь дать удаленный доступ
2) Есть сервер 1с в режиме отладки, который можно завалить.
52 DES
 
23.11.15
22:38
(51) правда что ли ?
53 DES
 
23.11.15
22:40
(51) организуем завтра
54 Serginio1
 
23.11.15
23:35
(49) Гуид должен быть уникальным. В VS сервис- создать GUID
55 DES
 
24.11.15
00:14
(46)
Не догоняю, какие строчки лишние?
У меня и так срабытывает
объект=Новый ComОбъект("ИмяТвоегоКласса");
в частности
объект=Новый ComОбъект("Online.Utilites");
56 DES
 
24.11.15
10:11
(51) переделал try на using
dll-ко стало вести себя прилично, т.е. не блокирует загрузку *.dt. Спасибо за желание помочь.
57 Serginio1
 
24.11.15
10:36
(55) Тогда класс у тебя должен описан как в (46)
И соответсвенно ты должен найти Guid этого класса.
В регистр записываются только GUID класса у которого определен ProgId.

Кстати в моих примерах есть обмен по TCP/IP
58 DES
 
24.11.15
10:48
(57) там у тебя асинхронный пример
59 Serginio1
 
24.11.15
11:05
(58) Нет отправка там синхронная

Функция СоздатьСерверTCP(Врап)
    
    Если не ЗначениеЗаполнено(ИмяФайлаСборки) Тогда
        //  предупреждение("Не выбрано Имя Файла Сборки");
        вызватьИсключение    "Не выбрано Имя Файла Сборки"  
    КонецЕсли;
    
    
    врап=новый COMОбъект("NetObjectToIDispatch45");
    ФайлСборки=ИмяФайлаСборки;//"d:\MyPrograms\Test\ОбменПоTCPIP\ОбменПоTCPIP\bin\Debug\ОбменПоTCPIP.dll";
    Сборка=врап.загрузитьСборку(ФайлСборки); //ПроектИспользованияДелегатов.dll
    тип=Сборка.GetType("TCPConnectTo1C.TCPConnector");
    
    рез=врап.СоздатьОбъект(Тип);
    возврат   рез
    
КонецФункции // СоздатьTCP()

Процедура ОтправитьКомандуНажатие(Элемент)
    перем Врап;
    
    КлиентTCP=СоздатьСерверTCP(Врап);
    
    ServerAdress="127.0.0.1";
    порт=6891;
    Команда="Тест Отправки Сообщения";
    ДанныеДляКоманды=XmlСтрока(ТекущаяДата());
    
    ЕстьОтвет=истина;
    ЗакрытьСоединение=истина;
    ОшибкаСоединения=false;
    
    резулт=КлиентTCP.ОтправитьКоманду(ServerAdress,порт,Команда,ДанныеДляКоманды,ЕстьОтвет,ЗакрытьСоединение);
    Сообщить(резулт.Данные);    
    Если резулт.ОшибкаСоединения Тогда
        СтрОшибки="ОшибкаСоединения
        |"+резулт.Данные;
        Предупреждение(СтрОшибки);
    КонецЕсли;
    
    
    
КонецПроцедуры
60 Serginio1
 
24.11.15
11:11
bool СоединитьсяССервером(string ServerAdress,int порт)
        {


            IPEndPoint ipEndpoint = new IPEndPoint(IPAddress.Parse(ServerAdress), порт); //6891
            try
            {
                if (клиент == null)
                {
                    клиент = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    клиент.Connect(ipEndpoint);
                }
                return true;
            }
            catch (SocketException ex)
            {
                MessageBox.Show("Ошибка соединения с сервером: " + ex.Message);
                клиент.Close();
                клиент = null;
                return false;
            }
        }
      public  ДанныеОтветаПоTCP ОтправитьКоманду(string ServerAdress,int порт,string Команда, string ДанныеДляКоманды, bool ЕстьОтвет, bool ЗакрытьСоединение)
        {



          bool ОшибкаСоединения=false;
            var result = "";
            
            try
            {

                if (!СоединитьсяССервером(ServerAdress,порт))
                {
                    ОшибкаСоединения = true;
                    return new ДанныеОтветаПоTCP("Ошибка", ОшибкаСоединения);
                }

                using (var strim = new NetworkStream(клиент))
                {
                    try
                    {
                        ДляОбменаПоТСП.ОтправитьКоманду(strim, Команда, ДанныеДляКоманды, ЕстьОтвет);
                        if (ЕстьОтвет) result = ДляОбменаПоТСП.ReadCompressedString(strim);
                    }
                    catch (SocketException ex)
                    {
                        MessageBox.Show("Ошибка  отправки данных: " + ex.Message);
                        result = "Ошибка";
                        ОшибкаСоединения = true;
                    }

                    if (ЗакрытьСоединение)
                    {
                        клиент.Close();
                        клиент = null;
                    }

                }
            }
            catch (SocketException ex)
            {
                MessageBox.Show("Ошибка соединения с сервером: " + ex.Message);
                result = "Ошибка";
                ОшибкаСоединения = true;
            }

            var res = new ДанныеОтветаПоTCP(result, ОшибкаСоединения);
            return res;
        }

     }
Независимо от того, куда вы едете — это в гору и против ветра!