Могу ли я безопасно проверить, выпущен ли COM-объект в VBA? - PullRequest
1 голос
/ 07 августа 2020

Я просматривал код для создания слабых ссылок в VBA путем ручного разыменования указателей объектов без вызова IUnknown::AddRef, и обнаружил ошибку, которую не могу объяснить. Я мог бы придумать минимально воспроизводимый пример с использованием чистых вызовов API, но я думаю, что проще просто продемонстрировать использование класса WeakReference из этого обзора. Вот как я могу sh Excel:

Dim strongRef As Range
Set strongRef = [A1]

Dim weakRef As New WeakReference
weakRef.Object = strongRef

Debug.Assert strongRef.address = weakRef.Object.address 'fine
    
Set strongRef = Nothing
Debug.Assert weakRef.Object Is Nothing       'fine if step through, crashes if F5

Это ошибочное поведение; класс WeakReference разработан таким образом, что после уничтожения родительской ссылки слабая ссылка должна возвращать Nothing, а не пытаться вслепую разыменовать родительский ObjPtr, который теперь указывает на недопустимый экземпляр объекта. То, как он это делает, подробно объясняется в связанном вопросе, но по сути он кэширует указатель VTable родительского объекта, а затем использует его для проверки того, что указатель VTable все еще действителен перед каждым разыменованием. В основном класс полагается на тот факт, что, когда родительский объект выходит за пределы области видимости, его память освобождается, и поэтому указатель VTable перезаписывается чем-то другим.

Этот должен остановить этот вид ошибка. Однако это не так, и мне интересно, почему ...

Насколько я понимаю,

Set strongRef = Nothing
  • Вызовы IUnknown::Release
  • Это устанавливает счетчик ссылок на ноль, объект выходит за пределы области видимости
  • Объект отвечает за освобождение своей собственной памяти экземпляра, поэтому он использует указатель this (первый аргумент на IUnknown::Release), чтобы обнулить память экземпляра (включая указатель VTable) и освободить ее для использования снова распределитель памяти VBA
  • Наконец, значение в VarPtr(strongRef) устанавливается в ноль, чтобы указать, что это ссылка на нулевой объект

Однако я думаю, что ошибка возникает из-за того, что память экземпляра не сбрасывается, как только счетчик ссылок достигает нуля, поэтому, возможно, реализация VBA IUnknown::Release помечает память как «грязную», которая должна быть очищена позже асинхронным сборщиком мусора? Я здесь просто догадываюсь. Дело в том, что если я прохожу код построчно, он работает нормально, или если вы держите WeakReference в дочернем классе, тогда он работает нормально (см. Примеры в связанной публикации).

ОБНОВЛЕНИЕ

Я только что попробовал, с настраиваемым классом VBA для strongRef, например

Class1

Option Explicit

Static Property Get address() As Double
    Dim value As Double
    If value = 0 Then value = [RandBetween(1,1e10)]
    address = value
End Property

... тогда я не получаю cra sh! Так что это определенно как-то связано с конкретными c реализациями IUnknown::Release, и, вероятно, именно поэтому автор этого кода никогда не замечал ошибки.

...