Situation:
I am attempting to inspect a variable a
, showing as a DispStaticNodeList
object in the locals window; Excel crashes every time I try to do this.
Here is the variable a
, apparently of type DispStaticNodeList
, in the locals window:
Reproducing the Excel crash:
For Each
also causes a crash.*TestFail
Research highlights:
Excel
+ Crash
+ DispStaticNodeList
yielded zero results; At least with the Google search terms I used. Pretty sure my Google-Fu was weak.If I believe this article I am dealing with a COM object that is supported by MSHTML
.
And according to this:
If the name is DispStaticNodeList, we can be pretty sure it's an array..(or at least has array semantics).
Based on point 3 I wrote the code, TestPass
, below, which does loop over it successfully, but I don't fully understand why. I have set an object and then looped its len!
NodeList objects are collections of nodes such as those returned by properties such as Node.childNodes and the document.querySelectorAll() method.
So it seems the object may be a NodeList
, which given the description in the immediate window seems about right, and as a list I can loop over its length, but not sure why For Each
won't work and why Excel crashes. Colleague suggests it may crash due to hierarchical nature of data. I further note that there are classes called IDOMNodeIterator
and NodeIterator
, but I am not sure if I can use those in line with the descriptions for NodeList
methods here.
Question:
What is a
and why does it cause Excel to crash when attempting to inspect or loop with a For Each
?
Code that loops successfully:
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 Code that causes crash:
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
Notes:
I sent a test workbook to a colleague who was also able to reproduce this behaviour with the example given.
Project References:
Sample of HTML (link was also provided)
<div class="noSel">
<h1 style=""><span class="markup"><h1></span>Welcome to My Homepage<span class="markup"></h1></span></h1>
<div id="helpIntro" style="">
<span class="markup"><div class="intro"></span>
<div class="intro">
<p style="margin-top: 4px; border-color: rgb(255, 102, 102); background-color: rgb(255, 255, 153);"><span class="markup"><p></span>My name is Donald <span id="Lastname" style=""><span class="markup"><span id="Lastname"></span>Duck.<span class="markup"></span></span></span><span class="markup"></p></span></p>
<p id="my-Address" style="border-color: rgb(255, 102, 102); background-color: rgb(255, 255, 153);"><span class="markup"><p id="my-Address"></span>I live in Duckburg<span class="markup"></p></span></p>
<p style="margin-bottom: 4px; border-color: rgb(255, 102, 102); background-color: rgb(255, 255, 153);"><span class="markup"><p></span>I have many friends:<span class="markup"></p></span></p>
</div>
<span class="markup"></div></span>
</div>
<br>
<div class="helpUl">
<span class="markup"><ul id="Listfriends></span>
<ul id="Listfriends" style="margin-top:0px;margin-bottom:0px;">
<li><span class="markup"><li></span>Goofy<span class="markup"></li></span></li>
<li><span class="markup"><li></span>Mickey<span class="markup"></li></span></li>
<li><span class="markup"><li></span>Daisy<span class="markup"></li></span></li>
<li><span class="markup"><li></span>Pluto<span class="markup"></li></span></li>
</ul>
<span class="markup"></ul></span>
</div>
<ul style="display:none;"></ul>
<p style=""><span class="markup"><p></span>All my friends are great!<span class="markup"><br></span><br>But I really like Daisy!!<span class="markup"></p></span></p>
<p lang="it" title="Hello beautiful" style=""><span class="markup"><p lang="it" title="Hello beautiful"></span>Ciao bella<span class="markup"></p></span></p>
Edit: I have also been able to loop in the following fashion:
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
If the name is DispStaticNodeList, we can be pretty sure it's an array..(or at least has array semantics).
Arrays can normally be iterated with a For Each
loop, however it's more efficient to iterate them using a For
loop. Looks like what you're getting isn't exactly an array, and while it appears to support indexing, it apparently doesn't support enumeration, which would explain the blowing up when you attempt to enumerate it with a For Each
loop.
Looks like the locals toolwindow might be using For Each
semantics to list the items in the collection.
I'm not familiar with that particular library so this is a bit of (educated) guesswork, but it's pretty easy to make a custom COM collection type that can't be iterated with a For Each
loop in VBA - normally the error is caught on the VBA side though... Seems there might be a bug in the library's enumerator implementation (assuming there's an enumerator for it) causing it to throw an exception that ends up unhandled and somehow takes everything down with it... thing is, you can't fix & recompile that library... so the only thing you can do is to avoid iterating that type with a For Each
loop, and avoid expanding it in the locals toolwindow (and so, ...save your work often!).
This article gives a good idea from a C#/.NET perspective, of how COM enumeration works. Of course that library isn't managed code (.NET), but the COM concepts at play are the same.
TL;DR: it's not because you can For...Next
that you can For Each
; the COM type involved must explicitly support enumeration. If the VBA code compiles with a For Each
loop, then it does, so it must be a bug in the type's enumerator.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With