Действительно ли возможно прервать (или знать) Подсчет ссылок COM на объектах CLR, выставленных COM

Я перефразировал этот вопрос.

Когда объекты .NET выставляются COM-клиентам через COM iterop, CCW (Обертка для COM-вызовов) создается, это находится между COM-клиентом и Управляемым объектом .NET.

В мире COM объекты проводят подсчет количества ссылок, которые другие объекты имеют к нему. Объекты удалены/освобождены/взяты себя в руки, когда тот подсчет ссылок переходит к Нулю. Это означает, что завершение COM-объекта детерминировано (мы используем Using/IDispose в .NET для детерминированного завершения, объектные финализаторы не детерминированы).

Каждый CCW является COM-объектом, и это - ссылка, считаемая как любой другой COM-объект. Когда CCW умирает (подсчет ссылок переходит к Нулю), GC не сможет найти, что CLR возражает CCW, перенесенному, и объект CLR имеет право на набор. Счастливые дни, все хорошо с миром.

То, что я хотел бы сделать, поймать, когда CCW умирает (т.е. когда его подсчет ссылок переходит к нулю), и так или иначе предупредите об этом объект CLR (например, Путем вызова Расположить метода на управляемом объекте).

Так, действительно ли возможно знать, когда подсчет ссылок Обертки для COM-вызовов для класса CLR переходит к Нулю?
и/или
Действительно ли возможно обеспечить мою реализацию AddRef & ReleaseRef для CCWs в .NET?

Если не альтернатива должна реализовать эти DLLs в ATL (я не нуждаюсь ни в какой помощи с ATL, спасибо). Это не были бы аэрокосмические исследования, но я отказываюсь сделать это, поскольку я - единственный разработчик, внутренний с любым реальным миром C++ или любым ATL.

Фон
Я переписываю некоторый старый VB6 ActiveX DLLs в .NET (C#, чтобы быть точным, но это - больше .NET / взаимодействующая с COM проблема, а не проблема C#). Некоторые старые объекты VB6 зависят от подсчета ссылок для выполнения действий, когда объект завершается (см. explaination подсчета ссылок выше). Они DLL не содержит важную бизнес-логику, они - утилиты и функции помощника, которые мы предоставляем клиентам, которые интегрируются с нами использующий VBScript.

Что я не пытаюсь сделать

  • Подсчет ссылок .NET возражает вместо использования Сборщика "мусора". Я довольно доволен GC, моя проблема не с GC.
  • Используйте объектные финализаторы. Финализаторы не детерминированы в этом экземпляре, мне нужно детерминированное завершение (как идиома Using/IDispose в .NET)
  • Реализуйте IUnknown в неуправляемом C++
    Если я должен пойти путем C++, я буду использовать ATL, спасибо.
  • Решите это использование Vb6 или многократное использование объектов VB6. Весь смысл этого осуществления должен удалить нашу зависимость сборки от Vb6.

Спасибо
BW

Принятый ответ
Народная тысяча благодаря Steve Steiner, который придумал единственное (возможно осуществимый) основанный на.NET ответ и Earwicker, который предложил очень простое решение ATL.

Однако принятый ответ переходит к Bigtoe, который предлагает перенести объекты .NET в объекты VbScript (который я не считал честным), эффективно предоставляя простое решение VbScript проблемы VbScript.

Благодаря всем.

17
задан 13 revs 23 May 2017 в 12:26
поделиться

8 ответов

Хорошо, ребята, вот еще одна попытка. Фактически вы можете использовать «Компоненты сценария Windows», чтобы обернуть свои COM-объекты .NET и таким образом получить финализацию. Вот полный пример использования простого .NET-калькулятора, который может добавлять значения. Я уверен, что вы получите концепцию оттуда, это полностью позволяет избежать проблем с VB-Runtime, ATL и использует Windows Scripting Host, который доступен на всех основных платформах WIN32 / WIN64.

Я создал простой класс COM .NET под названием Calculator в пространстве имен DemoLib. Обратите внимание, что это реализует IDisposable, где в демонстрационных целях я помещаю что-то на экран, чтобы показать, что он завершился. Я полностью придерживаюсь vb здесь, в .NET, и script, чтобы все было просто, но часть .NET может быть на C # и т. Д.Когда вы сохраните этот файл, вам нужно будет зарегистрировать его в regsvr32, его нужно будет сохранить как что-то вроде CalculatorLib.wsc.

<ComClass(Calculator.ClassId, Calculator.InterfaceId, Calculator.EventsId)> _
Public Class Calculator
    Implements IDisposable
#Region "COM GUIDs"
    ' These  GUIDs provide the COM identity for this class 
    ' and its COM interfaces. If you change them, existing 
    ' clients will no longer be able to access the class.
    Public Const ClassId As String = "68b420b3-3aa2-404a-a2d5-fa7497ad0ebc"
    Public Const InterfaceId As String = "0da9ab1a-176f-49c4-9334-286a3ad54353"
    Public Const EventsId As String = "ce93112f-d45e-41ba-86a0-c7d5a915a2c9"
#End Region
    ' A creatable COM class must have a Public Sub New() 
    ' with no parameters, otherwise, the class will not be 
    ' registered in the COM registry and cannot be created 
    ' via CreateObject.
    Public Sub New()
        MyBase.New()
    End Sub
    Public Function Add(ByVal x As Double, ByVal y As Double) As Double
        Return x + y
    End Function
    Private disposedValue As Boolean = False        ' To detect redundant calls
    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                MsgBox("Disposed called on .NET COM Calculator.")
            End If
        End If
        Me.disposedValue = True
    End Sub
#Region " IDisposable Support "
    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region
End Class

Затем я создаю компонент сценария Windows под названием Calculator.Lib, который имеет единственный метод, который возвращает обратно COM-класс VB-Script, который предоставляет математическую библиотеку .NET. Здесь я что-то всплываю на экране во время Construction and Destruction, обратите внимание, что в Destruction мы вызываем метод Dispose в библиотеке .NET, чтобы освободить ресурсы. Обратите внимание на использование функции Lib () для возврата вызывающему калькулятору .NET Com.

<?xml version="1.0"?>
<component>
<?component error="true" debug="true"?>
<registration
    description="Demo Math Library Script"
    progid="Calculator.Lib"
    version="1.00"
    classid="{0df54960-4639-496a-a5dd-a9abf1154772}"
>
</registration>
<public>
  <method name="GetMathLibrary">
  </method>
</public>
<script language="VBScript">
<![CDATA[
Option Explicit
'-----------------------------------------------------------------------------------------------------
' public Function to return back a logger.
'-----------------------------------------------------------------------------------------------------
function GetMathLibrary()
    Set GetMathLibrary = New MathLibrary
end function
Class MathLibrary
    private dotNetMatFunctionLib
  private sub class_initialize()
    MsgBox "Created."
    Set dotNetMatFunctionLib = CreateObject("DemoLib.Calculator")
  end sub
  private sub class_terminate()
        dotNetMatFunctionLib.Dispose()
        Set dotNetMatFunctionLib = nothing
    MsgBox "Terminated."
  end sub
  public function Lib()
    Set Lib = dotNetMatFunctionLib
  End function
end class
]]>
</script>
</component>

Наконец, чтобы связать все это воедино, вот пример сценария VB, в котором вы получаете диалоги, показывающие создание, расчет, dispose, вызываемый в библиотеке .NET, и, наконец, завершение в компоненте COM, раскрывающем компонент .NET.

dim comWrapper
dim vbsCalculator
set comWrapper = CreateObject("Calculator.Lib")
set vbsCalculator = comWrapper.GetMathLibrary()
msgbox "10 + 10 = " & vbsCalculator.lib.Add(10, 10)
msgbox "20 + 20 = " & vbsCalculator.lib.Add(20, 20)
set vbsCalculator = nothing
MsgBox("Dispose & Terminate should have been called before here.")
5
ответ дан 30 November 2019 в 13:53
поделиться

Помните, что тэги должны быть семантическими, а не презентационными. В английском языке есть такое понятие, как «fine print». Это то, что представляет маленький тэг. Аналогичного понятия «большой принт» нет, кроме заголовка, который уже покрыт семью другими тэгами.

-121--768203-

Поток может быть запущен путем ожидания WaitObject и вызова метода Set другим потоком. Просмотрите метод WaitHandle.WaitOne .

Вот статья, которая также может помочь.

-121--4903824-

Я не проверил это, но вот что я бы попытался:

Во-первых, вот CBrumme блог статьи о clr по умолчанию реализации IMarshal. Если ваши утилиты используются в апартаментах COM, вы не сможете получить надлежащее поведение от прямого порта VB6 к CLR. Com-объекты, реализованные CLR, действуют так, как если бы они агрегировали свободнонарезной маршаллер, а не квартирную резьбовую модель, которая VB6 открыта.

Можно реализовать IMarshal (в clr-классе, который вы выставляете как com-объект). Насколько я понимаю, это позволит вам контролировать создание COM-прокси (а не прокси взаимодействия). Я думаю, что это позволит вам перехватить вызовы Release в объекте, который вы вернули из UnmarshalInterface, и передать сигнал обратно вашему исходному объекту. Я бы обернул стандартный маршаллер (например, pinvoke CoGetStandardMarshaler ) и переадресовал на него все вызовы. Я полагаю, что объект будет иметь срок службы, привязанный к сроку службы КНО.

снова... это то, что я бы попытался, если бы мне пришлось решить это в C #.

С другой стороны, действительно ли такое решение было бы проще, чем внедрение в ATL? Просто потому, что волшебная часть написана на C # не делает решение простым. Если то, что я предлагаю выше, решает проблему, вам нужно написать действительно большой комментарий, объясняющий, что происходит.

4
ответ дан 30 November 2019 в 13:53
поделиться

1) article_has_author (только моя «приватная» конвенция) m или ArticleAuthor 2) в отношениях n: m это обычно не имеет значения.

-121--4320938-

Я сделал это довольно мало в PyQt и это работает очень хорошо. Qt имеет широкую поддержку изображений, шрифтов, стилей и т.д., и все они могут быть записаны в PDF-документы.

-121--665924-

.Net Framework работает по-разному, см.
.NET Framework предоставляет методы управления памятью, отличающиеся от путей управления памятью в мире на базе COM. Управление памятью в COM выполнялось посредством подсчета ссылок. .NET предоставляет метод автоматического управления памятью, который включает трассировку ссылок. В этой статье мы рассмотрим метод сбора мусора, используемый CLR среды CLR Common Language Runtime.

ничего не делать

[ОТРЕДАКТИРОВАНО] более одного раунда...

Посмотрите на эту альтернативу Импорт библиотеки типов в виде сборки
Как вы сами сказали, используя CCW, вы можете получить доступ к счетчику ссылок традиционным COM-способом .

[ОТРЕДАКТИРОВАНО] Стойкость - это добродетель
Вы знаете WinAPIOverride32 ? С его помощью можно запечатлеть и изучить, как это работает. Другой инструмент, который может помочь, - Консоль-шпион Deviare COM .
Это будет непросто.
Удачи.

2
ответ дан 30 November 2019 в 13:53
поделиться

Я предполагаю, что это невозможно, потому что счетчик ссылок 0 не означает, что объект не используется, потому что у вас может быть вызов граф вроде

VB_Object
   |
   V
   |
Managed1 -<- Managed2

, и в этом случае объект Managed1 все еще используется, даже если объект VB отбрасывает ссылку на него, и его refcount, следовательно, равен 0.

Если вам действительно нужно делать то, что вы говорите, я думаю, вы могли бы создать классы-оболочки в неуправляемом C ++, который вызывает метод Dispose, когда refcount падает до 0. Эти классы, вероятно, можно было бы создать в коде из метаданных, но у меня нет никакого опыта в том, как реализовать такие вещи.

0
ответ дан 30 November 2019 в 13:53
поделиться

Почему бы не изменить парадигму. Как насчет того, чтобы создать собственный агрегат вокруг открытых и расширенных методов уведомления. Это можно сделать даже в .Net не только с помощью ATL.

ИЗМЕНИТЬ : Вот ссылка, которая может описывать как-то иначе ( http://msdn.microsoft.com/en-us/library/aa719740 (VS.71) .aspx ). Но следующие шаги объясняют мою идею выше.

Создайте новый класс .Net, который реализует ваш устаревший интерфейс (ILegacy) и новый интерфейс (ISendNotify) с помощью одного метода:

interface ISendNotify 
{
     void SetOnDestroy(IMyListener );
}

class MyWrapper : ILegacy, ISendNotify, IDisposable{ ...

Внутри MyClass создайте экземпляр вашего реального устаревшего объекта и делегируйте ему все вызовы из MyClass. пример. Это агрегирование.Итак, время жизни агрегата теперь зависит от MyClass. Поскольку MyClass является IDisposable, теперь вы можете перехватить его при удалении экземпляра, поэтому вы можете отправить уведомление с помощью IMyListener

EDIT2 : Взято там ( http://vb.mvps.org/hardcore/html/countingreferences .htm ) простейшая импликация IUnknown с отправкой события

Class MyRewritten
    ...
    Implements IUnknown
    Implements ILegacy
    ...
    Sub IUnknown_AddRef()
        c = c + 1
    End Sub

    Sub IUnknown_Release()
        c = c - 1
        If c = 0 Then
            RaiseEvent Me.Class_Terminate
            Erase Me
        End If
    End Sub
-1
ответ дан 30 November 2019 в 13:53
поделиться

Насколько мне известно, GC уже обеспечивает поддержку того, что вы пытаетесь сделать. Это называется доработка. В чисто управляемом мире лучше всего избегать финализации, поскольку она имеет некоторые побочные эффекты, которые могут негативно повлиять на производительность и работу сборщика мусора. Интерфейс IDisposable обеспечивает чистый управляемый способ обхода финализации объекта и обеспечения очистки как управляемых, так и неуправляемых ресурсов из управляемого кода.

В вашем случае вам необходимо инициировать очистку управляемого ресурса после того, как будут освобождены все неуправляемые ссылки. Доработка должна помочь решить вашу проблему здесь. GC будет финализировать объект всегда, если присутствует финализатор, независимо от того, как были освобождены последние ссылки на финализируемый объект. Если вы реализуете финализатор для своего типа .NET (просто реализуете деструктор), то GC поместит его в очередь финализации. После завершения цикла сбора GC он обработает очередь финализации. Любая работа по очистке, которую вы выполняете в своем деструкторе, произойдет после обработки очереди финализации.

Следует отметить, что если ваш финализируемый тип .NET содержит ссылки на другие объекты .NET, которые, в свою очередь, требуют завершения, вы можете вызвать длинную коллекцию GC, или некоторые из объектов могут существовать дольше, чем они были бы без завершения ( что будет означать, что они выживут в коллекции и дойдут до следующего поколения, которое собирается реже.) Однако, если работа по очистке вашего.Объекты .NET, которые используют CCW, никоим образом не чувствительны ко времени, и использование памяти не является большой проблемой, некоторое дополнительное время жизни не должно иметь значения. Следует отметить, что финализируемые объекты следует создавать с осторожностью, а минимизация или устранение любых ссылок уровня экземпляра класса на другие объекты может улучшить общее управление памятью через сборщик мусора.

Подробнее о финализации можно прочитать в этой статье: http://msdn.microsoft.com/en-us/magazine/bb985010.aspx . Хотя это довольно старая статья, написанная еще в то время, когда .NET 1.0 был впервые выпущен, фундаментальная архитектура GC пока не изменилась (первые значительные изменения в GC появятся с .NET 4.0, однако они больше связаны с одновременное выполнение GC без остановки потоков приложения, чем изменение его основной операции.)

-1
ответ дан 30 November 2019 в 13:53
поделиться

GroupReader буферизует в 4kB порциях, если я помню, когда смотрел на это пару лет назад. Можно добавить входящие данные в 4kB (ick!) или использовать лучший синтаксический анализатор. Я исправил это, портировав XP Джеймса Кларка (Java) на C # как часть Jabber-Net, здесь:

http://code.google.com/p/jabber-net/source/browse/ # svn/trunk/xpnet

Это LGPL, только обрабатывает UTF8, не упакован для использования и почти не имеет документации, поэтому я бы не рекомендовал использовать его.:)

-121--3977939-

% d печатает целое число, поэтому ваша переменная a интерпретируется как int.

С помощью gcc 3.4.6 я получаю 0 для обоих значений с вашим кодом (после снятия F с инициализации, как это привело к ошибке компилятора). Я подозреваю, что это имеет отношение к части переменной, которая интерпретируется как int равно 0, а затем компилятор путается... (вероятно, есть лучшее объяснение, но я не могу его придумать).

Использование% f для обеих переменных печати 1.000000 для обеих переменных для меня.

Вы можете найти список символов преобразования в нижней части этой страницы:

http://www.exforsys.com/tutorials/c-language/managing-input-and-output-operations-in-c.html

-121--3153467-

С помощью .NET запросите IUnknown для объекта. Вызовите AddRef (), затем Release (). Затем возьмите возвращаемое значение AddRef () и выполните с ним.

0
ответ дан 30 November 2019 в 13:53
поделиться

Насколько мне известно, лучше всего эта тема освещена в книге The .NET and COM Interoperability Handbook Алана Гордона, и эта ссылка должна вести на соответствующую страницу в Google Книгах. . (К сожалению, у меня его нет, вместо этого я обратился к книге Трольсена .)

Там говорится, что не существует четко определенного способа подключения к Release / подсчет ссылок в CCW. Вместо этого предлагается сделать свой класс C # одноразовым и поощрять своих клиентов COM (в вашем случае авторов VBScript) вызывать Dispose , когда они хотят, чтобы произошла детерминированная финализация.

Но, к счастью, для вас есть лазейка , потому что ваши клиенты являются COM-клиентами с поздним связыванием, потому что VBScript использует IDispatch для выполнения всех вызовов объектов.

Предположим, ваши классы C # доступны через COM. Сначала сделайте так, чтобы это работало.

Теперь в ATL / C ++ создайте класс-оболочку, используя мастер простых объектов ATL, и на странице параметров выберите Интерфейс: Пользовательский вместо Двойного. Это останавливает установку мастером собственной поддержки IDispatch .

В конструкторе класса используйте CoCreateInstance для волшебного создания экземпляра вашего класса C #. Запросите у него IDispatch и удерживайте этот указатель в члене.

Добавьте IDispatch в список наследования класса-оболочки и направьте все четыре метода IDispatch прямо на указатель, который вы спрятали в конструкторе.

В FinalRelease оболочки используйте технику позднего связывания ( Invoke ) для вызова метода Dispose объекта C #, как описано в Книга Алана Гордона (на страницах, на которые я ссылался выше).

Итак, теперь ваши клиенты VBScript общаются через CCW с классом C #, но вы можете перехватить окончательный выпуск и передать его методу Dispose .

Сделайте так, чтобы ваша библиотека ATL открывала отдельную оболочку для каждого «реального» класса C #. Вы, вероятно, захотите использовать наследование или шаблоны, чтобы получить хорошее повторное использование кода здесь. Каждый поддерживаемый вами класс C # должен содержать всего пару строк в коде оболочки ATL.

2
ответ дан 30 November 2019 в 13:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: