Re: UCOMIStream, MSHTML and WebBrowser control Persistence Problem

From: Ken Hanson (khanson_at_capstonetechnology.com)
Date: 03/17/04


Date: Tue, 16 Mar 2004 20:00:18 -0800

Charles,

I can recreate the problem very easily:

Create a simple vb winform app with two forms, the first form will
showdialog the second from from a command button. The second form has the
followng load event

AxWebBrowser1.Navigate2("C:\Capstone\Applications\TestApp\WindowsApplication
1\test.htm")

Do While AxWebBrowser1.ReadyState <>
SHDocVw.tagREADYSTATE.READYSTATE_COMPLETE

Application.DoEvents()

Loop

Call AxWebBrowser1.Document.Script.evaluate(0.5)

--------------------------------

The test.htm file contains the following:

<HTML>
  <HEAD>
    <TITLE>Evaluate</TITLE>

    <SCRIPT>
      function evaluate(x)
      {
         alert("hello")
         return eval(x)
      }
   </SCRIPT>
  </HEAD>

  <BODY>
  </BODY>
</HTML>

The dialog form will work once, Close and re-open, the second time it is
instantiated the call to evaluate fails. I will be calling Microsoft
tommorow.

- Ken

"Ken Hanson" <khanson@capstonetechnology.com> wrote in message
news:O41u5N8CEHA.1812@TK2MSFTNGP12.phx.gbl...
> Charles,
>
> Actually I had chopped off the top of the Form Load script which correctly
> did the navigate. As it turns out the problem does not seem to have
> anything to do with loading from a stream. If I navigate to a file
> containing the html directly instead of loading from a stream I get the
same
> behaivour.
>
> Basically I can call into the DHTML only after the first navigate, if the
> web control is disposed and the form is released (but the app still runs
of
> course) and the the form and webbrowser is re-instantiated and navigates
to
> the same html I cannot call into the same dhtml script function.
Something
> must be left hanging around but I can't figure out what it is.
>
> If you have any thoughts please let me know. I may play around with a
> simpler dhtml sample to see if it has something to do with my script,
> however in vb6 calling the same dhtml function gives me no problems. I am
> beginning to suspect there is something broken in the interop regarding
> support for this type script calling.
>
> Thanks for all your help.
>
> - Ken
>
> Call WebBrowser1.Script.PopupDrawXYPlot(strXMLData, lngCurveFitStyles)
>
> Works once on first instance, after second time the WebBrowser is
> instantiated it fails, saying it is unable to find PopupDrawXYPlot, even
> though the innerHTML of the document shows it has loaded correctly.
>
>
>
>
> "Charles Law" <blank@nowhere.com> wrote in message
> news:utl18A7CEHA.2888@TK2MSFTNGP09.phx.gbl...
> > Ken
> >
> > Although probably not the problem, it looks like you might be caching
the
> > document object. It is best to retrieve it from the control each time
you
> > need a reference.
> >
> > When you talk about 'dialog form unload', do you mean that you close the
> > form that hosts the browser control and then re-open it? The browser
> control
> > needs to be disposed explicitly, so you should have a call to Dispose
when
> > you close the form.
> >
> > You say that objects are not available on subsequent occasions. Do you
get
> > an error? When you load the form, I do not see a
Navigate("about:blank").
> > This is required to initialise the control before you do anything else,
> and
> > then you must wait for DocumentComplete (readyState = "complete") before
> you
> > continue.
> >
> > HTH
> >
> > Charles
> >
> >
> > "Ken Hanson" <khanson@capstonetechnology.com> wrote in message
> > news:eTyO2t6CEHA.2800@tk2msftngp13.phx.gbl...
> > > Charles,
> > >
> > > Thanks for the advice. Per your suggestion I moved the load from
stream
> > > logic to the form load event and it worked great. After looking a
> little
> > > closer I saw that the C++ example that Microsoft shows avoids the
infite
> > > event firing loop by checking the interface pointer of the document to
> > make
> > > sure it is the one you want, this is difficult to do in .NET and not
> worth
> > > the effort since it works as seen below.
> > >
> > > Unfortantely I Now I have a new problem. It appears that I can only
> load
> > > once from the stream. If I let the dialog form unload and release
> object
> > > accordingly a second attempt at loading the document seems to succeed
> but
> > > objects in the document are not available as they were the first time.
> > >
> > > Are you familiar with any thing I need to explicity clean up to avoid
> > > confusing COM/WebBrowser interface?
> > >
> > > Thanks much for your help.
> > >
> > > - Ken
> > >
> > > In Form load:
> > >
> > > objDocument = WebBrowser1.Document
> > >
> > > strHTML = objSR.ReadToEnd()
> > >
> > > hGlobal = Marshal.StringToHGlobalAnsi(strHTML)
> > >
> > > CreateStreamOnHGlobal(hGlobal, True, strm)
> > >
> > > objPersistStreamInit = DirectCast(objDocument, IPersistStreamInit)
> > >
> > > objPersistStreamInit.InitNew()
> > >
> > > objPersistStreamInit.Load(strm)
> > >
> > > Do While WebBrowser1.ReadyState <> tagREADYSTATE.READYSTATE_COMPLETE
> > >
> > > Application.DoEvents()
> > >
> > > Loop
> > >
> > > objDocument = WebBrowser1.Document
> > >
> > > ...
> > >
> > > Subsequent call to vbscript function in document, works once, does not
> > work
> > > on subsequent doucment loads?
> > >
> > > Call objDocument.Script.PopupDrawXYPlot(strXMLData, lngCurveFitStyles)
> > >
> > >
> > > "Charles Law" <blank@nowhere.com> wrote in message
> > > news:OWLSze6CEHA.2628@TK2MSFTNGP11.phx.gbl...
> > > > Hi Ken
> > > >
> > > > It looks to me that your technique is correct. The problem, I
suspect,
> > is
> > > > where you load the document from the stream. Since documents are
> loaded
> > > > asynchronously, the logic of the load process is
> > > >
> > > > 1. Put html into stream
> > > > 2. Load document from stream
> > > > 3. Wait for operation to complete
> > > > 4. Continue ...
> > > >
> > > > Step 3 means waiting for the readyState to become 'complete', and
when
> > > that
> > > > happens the DocumentComplete event fires. As you are loading the
> > document
> > > in
> > > > the DocumentComplete event, you will get into a continuous loop.
> > > >
> > > > I would suggest that you either load the document outside the
> > > > DocumentComplete event (preferred), or set a flag in the event,
before
> > you
> > > > load, to say that you are currently loading. This way you can avoid
> > > > reloading the document.
> > > >
> > > > The preferred way would be to wait for the browser initialisation to
> > > > complete (that is, it finishes navigating to about:blank, which you
> must
> > > > instruct it to do), and then load the document, waiting for
readyState
> > > > before continuing
> > > >
> > > > HTH
> > > >
> > > > Charles
> > > >
> > > >
> > > > "Ken Hanson" <khanson@capstonetechnology.com> wrote in message
> > > > news:%23cYb7l4CEHA.1604@TK2MSFTNGP11.phx.gbl...
> > > > > Charels,
> > > > >
> > > > > I have seen several threads where it appears you have been
sucesfull
> > > > loading
> > > > > the WebBrowser control from a stream in the DocumentComplete event
> > from
> > > > > vb.net. I have been struggeling with this myself and was hoping
you
> > > might
> > > > > illuminate me.
> > > > >
> > > > > Bottom line is that when I try to load from a stream, no error is
> > thrown
> > > > but
> > > > > I seem to go into a infinite event loop where DocumentComplete
keeps
> > > being
> > > > > fired.
> > > > >
> > > > > Any help would be appreciated.
> > > > >
> > > > > Thanks,
> > > > >
> > > > > Ken
> > > > >
> > > > > Here are the bits that appear to be important:
> > > > >
> > > > > Dim TrendHTML As Stream =
> > > > >
> > > >
> > >
> >
>
System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(
> > > > > "CTCTrend.TrendGraphics.htm")
> > > > >
> > > > > ...
> > > > >
> > > > > Private Sub WebBrowser1_DocumentComplete(ByVal eventSender As
> > > > System.Object,
> > > > > ByVal eventArgs As
> AxSHDocVw.DWebBrowserEvents2_DocumentCompleteEvent)
> > > > > Handles WebBrowser1.DocumentComplete
> > > > >
> > > > > Dim bResult As Boolean
> > > > >
> > > > > Dim strm As UCOMIStream
> > > > >
> > > > > Dim objPersistStreamInit As IPersistStreamInit
> > > > >
> > > > > Dim hGlobal As IntPtr
> > > > >
> > > > > Dim strHTML As String
> > > > >
> > > > > Dim objSR As New StreamReader(TrendHTML)
> > > > >
> > > > > Try
> > > > >
> > > > > objDocument = WebBrowser1.Document
> > > > >
> > > > > strHTML = objSR.ReadToEnd() 'objSR is a stream reader initialized
> > > durring
> > > > > form load
> > > > >
> > > > > hGlobal = Marshal.StringToHGlobalAnsi(strHTML)
> > > > >
> > > > > CreateStreamOnHGlobal(hGlobal, True, strm)
> > > > >
> > > > > objPersistStreamInit = DirectCast(objDocument, IPersistStreamInit)
> > > > >
> > > > > objPersistStreamInit.InitNew()
> > > > >
> > > > > objPersistStreamInit.Load(strm)
> > > > >
> > > > > Catch ex As Exception
> > > > >
> > > > > msgbox ex.Mesage
> > > > >
> > > > > End Try
> > > > >
> > > > > End Sub
> > > > >
> > > > > "Charles Law" <blank@nowhere.com> wrote in message
> > > > > news:efMqFaW%23DHA.3812@TK2MSFTNGP09.phx.gbl...
> > > > > > Sorry for the cross post, but I'm not sure who is best placed to
> > > answer
> > > > > this
> > > > > > one.
> > > > > >
> > > > > > This is the most bizarre behaviour of MSHTML and streams.
> > > > > >
> > > > > > I have a WebBrowser control that contains nothing but some
default
> > > HTML.
> > > > I
> > > > > > want to copy the document and modify it before saving it to
disk.
> > > > > >
> > > > > > So, I clone the document like this:
> > > > > >
> > > > > > <code>
> > > > > > Private Function CloneDocument(ByVal doc As
> > mshtml.IHTMLDocument2)
> > > > As
> > > > > > mshtml.IHTMLDocument2
> > > > > >
> > > > > > Dim newdoc2 As mshtml.IHTMLDocument2
> > > > > > Dim ips As IPersistStreamInit
> > > > > >
> > > > > > Dim strm As UCOMIStream
> > > > > >
> > > > > > Dim source As String
> > > > > >
> > > > > > ' Create and initialise a new document object
> > > > > > newdoc2 = New mshtml.HTMLDocument
> > > > > >
> > > > > > ips = DirectCast(newdoc2, IPersistStreamInit)
> > > > > >
> > > > > > ips.InitNew()
> > > > > >
> > > > > > Do Until newdoc2.readyState = "complete"
> > > > > > Application.DoEvents()
> > > > > > Loop
> > > > > >
> > > > > > ' Get the current document HTML as a stream
> > > > > > source = GetDocumentSource(AxWebBrowser1.Document) ' see
> > below
> > > > > > strm = GetStream(source) ' see below
> > > > > >
> > > > > > ' Load the new document from the stream
> > > > > > ips.Load(strm)
> > > > > >
> > > > > > ' Wait until the new document has settled
> > > > > > Do Until newdoc2.readyState = "complete"
> > > > > > Application.DoEvents()
> > > > > > Loop
> > > > > >
> > > > > > Return newdoc2
> > > > > >
> > > > > > End Function
> > > > > >
> > > > > > Private Function GetStream(ByVal s As String) As UCOMIStream
> > > > > >
> > > > > > Dim iptr As IntPtr
> > > > > > Dim istrm As UCOMIStream
> > > > > >
> > > > > > ' Get a pointer to the string
> > > > > > iptr = Marshal.StringToHGlobalAuto(s)
> > > > > >
> > > > > > ' Create the stream from the pointer
> > > > > > CreateStreamOnHGlobal(iptr, True, istrm)
> > > > > >
> > > > > > Return istrm
> > > > > >
> > > > > > End Function
> > > > > > Private Function GetStream(ByVal size As Integer) As
> UCOMIStream
> > > > > >
> > > > > > Dim iptr As IntPtr
> > > > > > Dim strm As UCOMIStream
> > > > > >
> > > > > > ' Create a pointer to a block of the required size
> > > > > > iptr = Marshal.AllocHGlobal(size)
> > > > > >
> > > > > > ' Create the stream from the pointer
> > > > > > CreateStreamOnHGlobal(iptr, True, strm)
> > > > > >
> > > > > > Return strm
> > > > > >
> > > > > > End Function
> > > > > >
> > > > > > Private Function GetDocumentSource(ByVal doc As
> > > > mshtml.IHTMLDocument2,
> > > > > > ByVal resetIsDirty As Boolean) As String
> > > > > >
> > > > > > Dim stream As UCOMIStream
> > > > > >
> > > > > > Dim ips As IPersistStreamInit
> > > > > >
> > > > > > Dim s As String
> > > > > >
> > > > > > ips = DirectCast(doc, IPersistStreamInit)
> > > > > >
> > > > > > If ips Is Nothing Then
> > > > > > s = Nothing
> > > > > > Else
> > > > > > stream = GetStream(2048)
> > > > > >
> > > > > > ' Save the document into the comstream, without
> clearing
> > > the
> > > > > > IsDirty flag
> > > > > > ips.Save(stream, resetIsDirty)
> > > > > >
> > > > > > s = StreamToString(stream)
> > > > > > End If
> > > > > >
> > > > > > Return s
> > > > > >
> > > > > > End Function
> > > > > >
> > > > > > Private Function StreamToString(ByVal strm As UCOMIStream)
As
> > > String
> > > > > >
> > > > > > Dim iptr As IntPtr
> > > > > >
> > > > > > Dim s As String
> > > > > >
> > > > > > GetHGlobalFromStream(strm, iptr)
> > > > > >
> > > > > > ' *** THIS IS ODD TOO ***
> > > > > > ' If the source is the WebBrowser control then Ansi must
> be
> > > used
> > > > > ***
> > > > > > s = Marshal.PtrToStringAnsi(iptr)
> > > > > >
> > > > > > ' If the source is the cloned and modified document then
> > Auto
> > > > must
> > > > > > be used ***
> > > > > > s = Marshal.PtrToStringAuto(iptr)
> > > > > >
> > > > > > ' ***
> > > > > >
> > > > > > Return s
> > > > > >
> > > > > > End Function
> > > > > > </code>
> > > > > >
> > > > > > Having cloned the document, I modify it by inserting some tags
> into
> > > the
> > > > > body
> > > > > > element. I then call GetDocumentSource() to get the HTML from
the
> > > cloned
> > > > > > document.
> > > > > >
> > > > > > However, the HTML that is returned is the original HTML, from
the
> > > > > WebBrowser
> > > > > > control, and not from the cloned document. I know that the
cloned
> > > > document
> > > > > > contains the correct HTML because if I execute the following for
> the
> > > > > cloned
> > > > > > document
> > > > > >
> > > > > > ?doc.all.tags("html").Item(0).outerhtml
> > > > > >
> > > > > > in the command window, I get what I expect in the body element.
> > > > > >
> > > > > > Can anyone suggest why this is happening?
> > > > > >
> > > > > > I have also highlighted an oddity in function StreamToString(),
> > which
> > > I
> > > > do
> > > > > > not understand. Why would the encoding of the HTML change
between
> > the
> > > > > > browser and a cloned document?
> > > > > >
> > > > > > TIA
> > > > > >
> > > > > > Charles
> > > > > >
> > > > > >
> > > > >
> > > > >
> > > >
> > > >
> > >
> > >
> >
> >
>
>