Вход | Регистрация

1С:Предприятие ::

Метки: 

v7: Внешняя компонента COM на C# не выгружается

Я
   Other
 
31.08.18 - 13:50
Пишу компоненту, она реализует IInitDone и ILanguageExtender.
public void Init([MarshalAs(UnmanagedType.IDispatch)] object pConnection)
{
    V7Data.V7Object = pConnection;
}

public void Done()
{
    V7Data.V7Object = null;
    GC.Collect();
    GC.WaitForPendingFinalizers();
}

Пока я не трогаю указатель на IDipspath - всё прекрасно. Объект создается, при закрытии 1с вызывается метод Done().
Однако, стоит потрогать указатель и компонента из памяти больше не выгружается. Например:

var obj1c = V7Data.V7Object.GetType().InvokeMember("AppDispatch", System.Reflection.BindingFlags.GetProperty, null, V7Data.V7Object, null);
var obj = Marshal.GetIDispatchForObject(obj1c);
int b = Marshal.Release(obj);

Так вот, сколько бы раз я не вызывал Release(), число ссылок в b остается неизменным, т.е. Release() не уменьшает счетчик ссылок.

Что я таки делаю не так?
 
 
   H A D G E H O G s
 
1 - 31.08.18 - 14:05
странный какой то у тебя done()
Вообще это функция, которая должна вернуть в 1С \
S_OK
   H A D G E H O G s
 
2 - 31.08.18 - 14:06
function AddInExtension.Done:HResult; stdcall;
// 1С вызывает эту функцию при завершении работы компоненты

begin
  vk_object.Destroy();
  iError:=nil;
  iEvent:=nil;
  Done:=S_OK;
end;
   Other
 
3 - 31.08.18 - 14:07
(1) Если из функций IIinitDone в .net что-то возвращать - всё вообще валится в exception.
   Other
 
4 - 31.08.18 - 14:08
За основу я брал код шаблона из RSDN.
   H A D G E H O G s
 
5 - 31.08.18 - 14:09
(3) stdcall есть?
   H A D G E H O G s
 
6 - 31.08.18 - 14:10
В done() хоть заходит?
   Other
 
7 - 31.08.18 - 14:13
(6) Только начал писать. )) Если просто подключить компоненту и закрыть 1с - Done() вызывается. Если что-то сделать с указателем на IDispatch - не вызывается.
   H A D G E H O G s
 
8 - 31.08.18 - 14:14
(7) А если так
var obj1c = V7Data.V7Object.GetType().InvokeMember("AppDispatch", System.Reflection.BindingFlags.GetProperty, null, V7Data.V7Object, null);
var obj = Marshal.GetIDispatchForObject(obj1c);

obj = null;
obj1c = null;

    GC.Collect();
    GC.WaitForPendingFinalizers();
   H A D G E H O G s
 
9 - 31.08.18 - 14:15
Вернее

obj = null;
obj1c = null;
V7Data.V7Object = null;
    GC.Collect();
    GC.WaitForPendingFinalizers();
   Other
 
10 - 31.08.18 - 14:25
(9) var obj = Marshal.GetIDispatchForObject(obj1c);
Тип значения obj будет IntPtr. Он не принимает null.
Тип obj1c будет COM callable wrapper и его обнуление ничего не дает, т.к. не уничтожается сам COM объект. Чтобы сборщик мусора освободил память, нужно счетчик ссылок именно на COM объект до нуля, если я правильно понял. Для этого есть метод Release() класса Marshal, но он почему-то не уменьшает счетчик ссылок.
 
 Рекламное место пустует
   Other
 
11 - 31.08.18 - 14:27
Т.е. я получаю указатель IntPtr на ком объект, передаю его в метод Release, результатом выполнения метода будет число оставшихся ссылок на этот объект. Например 2. Я вызываю Release еще раз - результат всё равно 2.
   Other
 
12 - 31.08.18 - 14:56
var obj = Marshal.GetIDispatchForObject(obj1c);
var obj1 = Marshal.GetIDispatchForObject(V7Data.V7Object);
int a = Marshal.Release(obj);
int b = Marshal.Release(obj1);
while (a > 0 || b > 0)
{
    a = Marshal.Release(obj1);
    b = Marshal.Release(obj1);
}

А вот так счетчики ссылок уменьшаются. Интересно...
   Other
 
13 - 31.08.18 - 18:32
В общем, заработало вот так:
public void Done()
{
  if (_obj1c != null)
  {
    var obj1cPtr = Marshal.GetIDispatchForObject(_obj1c);
    int rc = Marshal.Release(obj1cPtr);
    while (rc > 0)
    {
      rc = Marshal.Release(obj1cPtr);
    }
    _obj1c = null;
  }

  if (_obj1c != null)
  {
    var connectionPtr = 
    Marshal.GetIDispatchForObject(V7Data.V7Object);
    int rc = Marshal.Release(connectionPtr);
    while (rc > 0)
    {
      rc = Marshal.Release(connectionPtr);
    }
    V7Data.V7Object = null;
  }
  GC.Collect();
  GC.WaitForPendingFinalizers();
}

V7Object статическое свойство класса V7Data
_obj1c вынес в приватное поле основного класса, реализующего IInitDone.


Если нужно создавать объекты агрегатных типов - ссылки на  них приходится хранить в глобальной коллекции, а потом в цикле освобождать Marshal.Release().
Как-то так


Список тем форума
Рекламное место пустует Рекламное место пустует
Глупец, лишенный способности посмеяться над собой вместе с другими, не сможет долго выносить программирование.
Фредерик Брукс-младший
ВНИМАНИЕ! Если вы потеряли окно ввода сообщения, нажмите Ctrl-F5 или Ctrl-R или кнопку "Обновить" в браузере.
Рекламное место пустует