Сбой Excel при попытке проверить DispStaticNodeList - PullRequest
0 голосов
/ 25 апреля 2018

Ситуация:

Я пытаюсь проверить переменную a, отображаемую как объект DispStaticNodeList в окне локальных компьютеров;Excel вылетает каждый раз, когда я пытаюсь это сделать.

Вот переменная a, по-видимому, типа DispStaticNodeList, в окне localals:

Locals window

Воспроизведение сбоя Excel:

  1. Попытка развернуть элемент в окне Locals приводит к сбою Excel.
  2. Попытка зацикливанияFor Each TestFail

Основные результаты исследований:

  1. Я сделал несколькокопаться в поисках таких комбинаций, как Excel + Crash + DispStaticNodeList, - ноль результатов;По крайней мере, с условиями поиска Google, которые я использовал.Я уверен, что мой Google-Fu был слабым.
  2. Если я верю в эту статью Я имею дело с COM-объектом, который поддерживается MSHTML.

  3. И в соответствии с это :

Если имя DispStaticNodeList, мы можем быть уверены, что этомассив .. (или, по крайней мере, имеет семантику массива).

Основываясь на пункте 3, я написал код, TestPass ниже, который успешно зацикливается на нем, но я не полностьюпонимаю почему.Я установил объект, а затем зациклил его длина!

Я только что нашел this , в котором говорится:

Объекты NodeList представляют собой наборы узлов, такие как возвращаемые свойствами, такими как Node.childNodes и документ.Метод querySelectorAll ().

Таким образом, кажется, что объект может быть NodeList, что, учитывая описание в ближайшем окне, кажется правильным, и как список я могу зациклить его длину, ноне знаю, почему For Each не работает и почему происходит сбой Excel.Коллега предполагает, что это может произойти сбой из-за иерархической природы данных.Я также отмечаю, что существуют классы с именами IDOMNodeIterator и NodeIterator, но я не уверен, смогу ли я использовать их в соответствии с описаниями для NodeList методов здесь .

Вопрос:

Что такое a и почему происходит сбой Excel при попытке выполнить проверку или цикл с For Each?

кодом, которыйуспешно зацикливается:

Option Explicit

Public Sub TestPass()
    Dim html As HTMLDocument
    Set html = GetTestHTML
    Dim a As Object, b As Object

    Set a = html.querySelectorAll("div.intro p")

    Dim i As Long

    For i = 0 To Len(a) -1
        On Error Resume Next
        Debug.Print a(i).innerText    '<== HTMLParaElement
        On Error GoTo 0
    Next i
End Sub

Public Function GetTestHTML(Optional ByVal url As String = "https://www.w3schools.com/cssref/trysel.asp") As HTMLDocument
    Dim http As New XMLHTTP60
    Dim html As New HTMLDocument
    With http                                    'Set http = CreateObject("MSXML2.XMLHttp60")
        .Open "GET", url, False
        .send
        html.body.innerHTML = .responseText
        Set GetTestHTML = html
    End With
End Function

* TestFail Код, вызывающий сбой:

Public Sub TestFail()
    Dim html As HTMLDocument
    Set html = GetTestHTML
    Dim a As Object, b As Object

    Set a = html.querySelectorAll("div.intro p")

    For Each b In a

    Next b
End Sub

Примечания:

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

Проектные ссылки:

Project references

Пример HTML (ссылка также указана)

<div class="noSel">
<h1 style=""><span class="markup">&lt;h1&gt;</span>Welcome to My Homepage<span class="markup">&lt;/h1&gt;</span></h1>

<div id="helpIntro" style="">
<span class="markup">&lt;div class="intro"&gt;</span>
<div class="intro">
<p style="margin-top: 4px; border-color: rgb(255, 102, 102); background-color: rgb(255, 255, 153);"><span class="markup">&lt;p&gt;</span>My name is Donald <span id="Lastname" style=""><span class="markup">&lt;span id="Lastname"&gt;</span>Duck.<span class="markup">&lt;/span&gt;</span></span><span class="markup">&lt;/p&gt;</span></p>
<p id="my-Address" style="border-color: rgb(255, 102, 102); background-color: rgb(255, 255, 153);"><span class="markup">&lt;p id="my-Address"&gt;</span>I live in Duckburg<span class="markup">&lt;/p&gt;</span></p>
<p style="margin-bottom: 4px; border-color: rgb(255, 102, 102); background-color: rgb(255, 255, 153);"><span class="markup">&lt;p&gt;</span>I have many friends:<span class="markup">&lt;/p&gt;</span></p>
</div>
<span class="markup">&lt;/div&gt;</span>
</div>

<br>
<div class="helpUl">
<span class="markup">&lt;ul id="Listfriends&gt;</span>
<ul id="Listfriends" style="margin-top:0px;margin-bottom:0px;">

<li><span class="markup">&lt;li&gt;</span>Goofy<span class="markup">&lt;/li&gt;</span></li>
<li><span class="markup">&lt;li&gt;</span>Mickey<span class="markup">&lt;/li&gt;</span></li>
<li><span class="markup">&lt;li&gt;</span>Daisy<span class="markup">&lt;/li&gt;</span></li>
<li><span class="markup">&lt;li&gt;</span>Pluto<span class="markup">&lt;/li&gt;</span></li>
</ul>       
<span class="markup">&lt;/ul&gt;</span>
</div>

<ul style="display:none;"></ul>
<p style=""><span class="markup">&lt;p&gt;</span>All my friends are great!<span class="markup">&lt;br&gt;</span><br>But I really like Daisy!!<span class="markup">&lt;/p&gt;</span></p>

<p lang="it" title="Hello beautiful" style=""><span class="markup">&lt;p lang="it" title="Hello beautiful"&gt;</span>Ciao bella<span class="markup">&lt;/p&gt;</span></p>

Редактировать: я также смог сделать цикл следующим образом:

Public Sub Test()
    Dim html As MSHTML.HTMLDocument, i As Long
    Set html = GetTestHTML

    For i = 0 To html.querySelectorAll("div.intro p").Length - 1
       Debug.Print html.querySelectorAll("div.intro p")(i).innerText
    Next i

End Sub

1 Ответ

0 голосов
/ 25 апреля 2018

Если имя - DispStaticNodeList, мы можем быть уверены, что это массив .. (или, по крайней мере, имеет семантику массива ).

Массивы может обычно повторяться с циклом For Each, однако более эффективно итерировать их с использованием цикла For.Похоже, то, что вы получаете, не является точно массивом, и, хотя кажется, что оно поддерживает индексирование, оно, по-видимому, не поддерживает перечисление , что могло бы объяснить взрыв, когда выпопытка перечислить его с помощью цикла For Each.

Похоже, окно инструментов locals может использовать семантику For Each для перечисления элементов в коллекции.

Я не знаком с этой конкретной библиотекой, так что это немного (обученная) догадка, но довольно просто создать пользовательский тип коллекции COM, который не может быть повторен с циклом For Each в VBA.- обычно ошибка обнаруживается на стороне VBA ... Кажется, может быть ошибка в реализации перечислителя библиотеки (при условии, что для нее есть перечислитель), приводящая к тому, что она генерирует исключение, которое заканчивается необработанным и каким-то образом сводит все на нет... дело в том, что вы не можете исправить и перекомпилировать эту библиотеку ... поэтому единственное, что вы можете сделать, это избежать итерации этого типа с помощью цикла For Each,избегайте его расширения в окне инструментов locals (и так, ... часто сохраняйте свою работу!).

Эта статья дает хорошую идею из C # /Перспектива .NET, как работает перечисление COM.Конечно, эта библиотека не является управляемым кодом (.NET), но понятия COM в игре одинаковы.

TL; DR: это не потому, что вы можете For...Next, что вы можете For Each;соответствующий тип COM должен явно поддерживать перечисление.Если код VBA компилируется с циклом For Each, то это происходит, поэтому это должно быть ошибкой в ​​перечислителе типа.

...