سؤال

I am getting an error trying to arrange asynchronous loading and parsing of an XML document in VBA using a wrapper class.

Following the ideas described in this msdn article and this tutorial which have worked perfectly for asynchronous handling of MSXML2.XMLHTTP40.send method I attempted to do a similar thing for DOMDocument.loadXML.

Here is the code from the wrapper class DOMMonitor

Private domDoc As MSXML2.DOMDocument
Public Event onXmlLoadComplete(d As MSXML2.DOMDocument)

Public Sub loadXML(XmlFilePath As String)
    Set domDoc = CreateObject("MSXML2.DOMDocument")
    domDoc.async = True
    domDoc.onreadystatechange = Me ' error occurs here
    domDoc.Load XmlFilePath
End Sub

Public Sub onLoadComplete()
    If domDoc.readyState = "4" Then
        RaiseEvent onXmlLoadComplete(domDoc)
    End If
End Sub

I have made onLoadComplete the default method by setting VB_UserMemId = 0, so it is supposed to be invoked when domDoc fires onreadystatechange .

However when I invoke loadXML

Dim dm As DomMonitor
Set dm = New DomMonitor
dm.loadXML txtXMLData

i get the following runtime error in this line:

domDoc.onreadystatechange = Me

This object cannot sink the 'onreadystatechange' event. An error occurred marshalling the object's IDispatch interface

What am I doing wrong and is there a good workaround here?

Thanks in advance.

P.S. The reason I am republishing the event is that I do not necessarily want use the default method of the final subscriber for this purpose. However, as things stand now I do not even get to that stage.

هل كانت مفيدة؟

المحلول

The way I read that msdn article is that to assign a wrapper class to the readystatechange, the object has to be either an IXMLHTTPRequest or an IServerXMLHTTPRequest object (bullet 3). Since your object is a DOMDocument, readystatechange doesn't accept an object.

However, you can instantiate a DOMDocument WithEvents (bullet 2), making the other way redundant, I guess. I don't have an xml file large enough to test, but I think this should work. I assume that if the class loses scope, all bets are off, so I made it a global variable.

In a standard module

Public clsDOMMonitor As CDOMMonitor

Sub test()

    Set clsDOMMonitor = New CDOMMonitor
    clsDOMMonitor.loadXML "C:\Users\dkusleika\Downloads\wurfl-2.3.xml"

End Sub

In CDOMMonitor class

Private WithEvents mDoc As MSXML2.DOMDocument

Private Sub mDoc_onreadystatechange()

    If mDoc.readyState = 4 Then
        MsgBox "second"
    End If

End Sub

Public Sub loadXML(XmlFilePath As String)

    Set mDoc = New MSXML2.DOMDocument
    mDoc.async = True
    mDoc.Load XmlFilePath

    MsgBox "first"

End Sub

I assume that setting async to True is all that is needed for this to work properly. My 100k xml file is probably done so fast that that the event never gives up control. But if you had a sufficiently large xml file, I think you would get "first" before "second".

نصائح أخرى

Change the class' Instancing property from Private to PublicNotCreatable when late binding, whilst also applying the tweak which you have mentioned.

Use the above example when early binding (as in your case).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top