Имя: Пароль:
1C
1C 7.7
v7: Внешняя компонента COM на C# не выгружается
,
0 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() не уменьшает счетчик ссылок.

Что я таки делаю не так?
1 H A D G E H O G s
 
31.08.18
14:05
странный какой то у тебя done()
Вообще это функция, которая должна вернуть в 1С \
S_OK
2 H A D G E H O G s
 
31.08.18
14:06
function AddInExtension.Done:HResult; stdcall;
// 1С вызывает эту функцию при завершении работы компоненты
begin
  vk_object.Destroy();
  iError:=nil;
  iEvent:=nil;
  Done:=S_OK;
end;
3 Other
 
31.08.18
14:07
(1) Если из функций IIinitDone в .net что-то возвращать - всё вообще валится в exception.
4 Other
 
31.08.18
14:08
За основу я брал код шаблона из RSDN.
5 H A D G E H O G s
 
31.08.18
14:09
(3) stdcall есть?
6 H A D G E H O G s
 
31.08.18
14:10
В done() хоть заходит?
7 Other
 
31.08.18
14:13
(6) Только начал писать. )) Если просто подключить компоненту и закрыть 1с - Done() вызывается. Если что-то сделать с указателем на IDispatch - не вызывается.
8 H A D G E H O G s
 
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();
9 H A D G E H O G s
 
31.08.18
14:15
Вернее

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

А вот так счетчики ссылок уменьшаются. Интересно...
13 Other
 
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().

Как-то так