2 votes

Récupérer la source HTML d'un contrôle WPF-WebBrowser en utilisant IPersistStreamInit

J'essaie d'obtenir la source HTML d'une page Web qui a été chargée dans un fichier de type WPF Contrôle du navigateur Web. La seule façon de le faire semble être de couler l'instance de WebBrowser.Document vers IPersistStreamInit (que je devrai définir moi-même, car il s'agit d'une interface COM) et d'appeler la méthode IPersistStreamInit.Save, en passant une une implémentation d'un IStream (encore une fois, une interface COM), qui persistera le document dans le flux. Enfin, en quelque sorte : Je reçois toujours les 4 premiers kilo-octets du flux, et non le document entier, et je ne sais pas pourquoi.

Voici le code de IPersistStreamInit :

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Security;

namespace PayPal.SkyNet.BpiTool.Interop
{
    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), 
        SuppressUnmanagedCodeSecurity, 
        Guid("7FD52380-4E07-101B-AE2D-08002B2EC713")]
    public interface IPersistStreamInit
    {
        void GetClassID(out Guid pClassID);
        [PreserveSig]
        int IsDirty();
        void Load([In, MarshalAs(UnmanagedType.Interface)] IStream pstm);
        void Save([In, MarshalAs(UnmanagedType.Interface)] IStream pstm, [In, MarshalAs(UnmanagedType.Bool)] bool fClearDirty);
        void GetSizeMax([Out, MarshalAs(UnmanagedType.LPArray)] long pcbSize);
        void InitNew();
    }
}

Voici le code de l'implémentation de l'IStream :

using System;
using System.IO;
using System.Runtime.InteropServices.ComTypes;

namespace PayPal.SkyNet.BpiTool.Interop
{
    public class ComStream : IStream
    {
        private Stream _stream;

        public ComStream(Stream stream)
        {
            this._stream = stream;
        }

        public void Commit(int grfCommitFlags)
        {
        }

        public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
        {
        }

        public void LockRegion(long libOffset, long cb, int dwLockType)
        {
        }

        public void Read(byte[] pv, int cb, IntPtr pcbRead)
        {
            this._stream.Read(pv, (int)this._stream.Position, cb);
        }

        public void Revert()
        {
        }

        public void SetSize(long libNewSize)
        {
            this._stream.SetLength(libNewSize);
        }

        public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag)
        {
            pstatstg = new System.Runtime.InteropServices.ComTypes.STATSTG();
        }

        public void UnlockRegion(long libOffset, long cb, int dwLockType)
        {
        }

        public void Write(byte[] pv, int cb, IntPtr pcbWritten)
        {
            this._stream.Write(pv, 0, cb);
        }

        public void Clone(out IStream outputStream)
        {
            outputStream = null;
        }

        public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
        {
            this._stream.Seek(dlibMove, (SeekOrigin)dwOrigin);
        }
    }
}

Maintenant, j'ai un cours pour conclure tout ça. Comme je ne veux pas redistribuer l'assemblage mshtml-interop-assembly, j'ai choisi la liaison tardive - et comme la liaison tardive est plus facile en VB, je l'ai fait en VB. Voici le code :

Option Strict Off
Option Explicit Off

Imports System.IO

Public Class HtmlDocumentWrapper : Implements IDisposable

    Private htmlDoc As Object

    Public Sub New(ByVal htmlDoc As Object)
        Me.htmlDoc = htmlDoc
    End Sub

    Public Property Document As Object
        Get
            Return Me.htmlDoc
        End Get
        Set(value As Object)
            Me.htmlDoc = Nothing
            Me.htmlDoc = value
        End Set
    End Property

    Public ReadOnly Property DocumentStream As Stream
        Get
            Dim str As Stream = Nothing
            Dim psi As IPersistStreamInit = CType(Me.htmlDoc, IPersistStreamInit)
            If psi IsNot Nothing Then
                str = New MemoryStream
                Dim cStream As New ComStream(str)
                psi.Save(cStream, False)
                str.Position = 0
            End If
            Return str
        End Get
    End Property
End Class

Maintenant, je devrais être capable d'utiliser tout ça :

private void Browser_Navigated(object sender, NavigationEventArgs e)
{
    HtmlDocumentWrapper doc = new HtmlDocumentWrapper();
    doc.Document = Browser.Document;
    using (StreamReader sr = new StreamReader(doc.DocumentStream))
    {
        using (StreamWriter sw = new StreamWriter("test.txt"))
        {
            //BOOM! Only 4kb of HTML source
            sw.WriteLine(sr.ReadToEnd());
            sw.Flush();
        }
    }
}

Quelqu'un sait-il pourquoi je n'obtiens pas l'intégralité de la source HTML ? Toute aide est la bienvenue.

Salutations

Arne

2voto

Déplacez votre code de Browser.Navigated à Browser.LoadCompleted comme Sheng Jiang l'a correctement noté ci-dessus et cela fonctionne.

0voto

aquaherd Points 434

C'est juste une supposition :

Le flux n'a pas une longueur connue, car il peut être en cours de téléchargement. Vous devrez le lire jusqu'à ce qu'il indique EOF.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X