tl; dr
Вы запутали ад из IsMissing
, заставив его думать, что неинициализированный массив был искажен ParamArray
, и обнаружили ошибку в его реализации.
Согласно программе просмотра событий Windows, основное исключение, которое приводит к отключению Excel, является нарушением прав доступа в vbe7.dll:
Первый отчет, который я увидел (прокомментировал выше), был с ошибкой обратного вызова Rubberduckпотому что Excel был тостом.
Faulting application name: EXCEL.EXE, version: 15.0.5067.1000, time stamp: 0x5b76360d
Faulting module name: VBE7.DLL, version: 7.1.10.68, time stamp: 0x58def301
Exception code: 0xc0000005
Fault offset: 0x00000000001cd38c
Faulting process id: 0xac4
Faulting application start time: 0x01d45de81ef072ed
Faulting application path: C:\Program Files\Microsoft Office\Office15\EXCEL.EXE
Faulting module path: C:\PROGRA~1\COMMON~1\MICROS~1\VBA\VBA7.1\VBE7.DLL
Нарушение прав доступа связано с попыткой VB во время выполнения разыменовать нулевой указатель.Это то место, где отладчик приземляет вас, когда умирает:
00007FFF9CDDD383 mov rax,qword ptr [rsp+30h]
00007FFF9CDDD388 mov rax,qword ptr [rax+8]
00007FFF9CDDD38C movzx eax,word ptr [rax] <--fails, rax is 0.
00007FFF9CDDD38F cmp eax,1
00007FFF9CDDD392 jne 00007FFF9CD7B8A4
Итак, давайте посмотрим, что проверяет функция IsMissing
.VBA - это, по сути, зверь COM, и необязательный параметр, который не передается, принимается вызываемой функцией как Variant
с типом VT_ERROR для соглашения RPC.Он не передается как Null
, Empty
или любая другая конструкция VB.Следующий код демонстрирует:
Sub Foo()
Bar
End Sub
Sub Bar(Optional x)
Debug.Print VarPtr(x) 'This has a valid pointer.
Debug.Print IsError(x) 'True
Debug.Print VarType(x) '10 (vbError)
End Sub
Вы можете прочитать чуть более техническое объяснение в блоге Раймонда Чена .
IsMissing
предназначен для передачи входящего параметра, поэтому он также должен знать о ParamArray
с (под капотом ParamArray
- это просто Variant()
).Хотя, если вы не дадите ей никаких параметров, процедура все равно получит массив - он просто на UBound
ниже, чем LBound
:
Sub Calling()
Called
End Sub
Sub Called(ParamArray params())
Debug.Print LBound(params) '<-- 0
Debug.Print UBound(params) '<-- -1
End Sub
Вот где это становится интересным.Вы не можете иметь как ParamArray
, так и необязательный аргумент в одной и той же сигнатуре процедуры - это может быть один или другой.Когда вы посмотрите на сигнатуру функции, вы увидите, что она принимает указатель на Variant
(то есть ожидает параметр ByRef
):
[entry(0x60000007), helpcontext(0x000f69b5)]
VARIANT_BOOL _stdcall IsMissing([in] VARIANT* ArgName);
Но такой код не 't дает функции то, что она ожидает увидеть:
Sub DieExcelDie()
Dim x(1), y()
x(1) = y
x(1) = IsMissing(x(1))
End Sub
x(1)
необходимо упорядочить в ByRef Variant
для передачи в качестве параметра в IsMissing
.Итак, что он видит - это массив Variant
, который имеет значение LBound
и UBound
, равное нулю ( этот ответ идет немного вкак выглядит неинициализированный массив в памяти).Параметр еще не прошел механизм RPC, поэтому на данный момент он совершенно неотличим от ParamArray
, содержащего один переданный параметр.Он проверит, был ли введен вариант VT_ERROR, что это массив, и попытался бы обработать его таким образом.Поскольку он не соответствует спецификации для пустого списка параметров, он, скорее всего, проверяет, что было первым переданным параметром ... путем разыменования области данных.Структура SAFEARRAY
выглядит следующим образом 1 :
Возвращаясь к разборке и не спускаясь полностью вниз по кроличьей норедизассемблирования vbe7.dll, rax+8
в предыдущей инструкции, скорее всего, загружает указатель pvData
(это 8 байт в 64-битной версии Excel), который, как вы видите, равен нулю.
Это просто ошибка в реализации IsMissing
- она должна проверять нулевой указатель, а не разыменовывать его вслепую.Я подозреваю, хотя не могу проверить, что это было введено в VB5 вместе с изменениями, которые позволили как необязательные параметры, так и не Variant
и значения по умолчанию.
1 Изображение было взято как скриншот с Как Visual Basic 6 хранит данные в codeguru.