2010-11-08 17 views
6

Sto cercando di trovare un modo per il mio programma di sapere quando un web browser sta navigando e quando non lo è. Questo perché il programma interagirà con il documento caricato tramite JavaScript che verrà iniettato nel documento. Non ho alcun altro modo per sapere quando inizia la navigazione che gestire l'evento Navigating poiché non è il mio programma ma l'utente che naviga interagendo con il documento. Ma poi, quando si verifica DocumentCompleted, non significa necessariamente che ha terminato la navigazione. Sono stato googling molto e trovato due pseudo-soluzioni:È possibile sapere con certezza se un web browser sta navigando o no?

  1. Verificare la presenza di proprietà di WebBrowser ReadyState in caso DocumentCompleted. Il problema con questo è che se non viene caricato il documento ma viene caricato un frame nel documento, lo ReadyState sarà Completed anche se il documento principale non è completo.

  2. Per evitare questo, consigliano di vedere se il parametro Url passato al DocumentCompleted corrisponde al Url del WebBrowser. In questo modo vorrei sapere che DocumentCompleted non viene invocato da qualche altro frame nel documento.

Il problema 2 è che, come ho detto, l'unico modo che ho per sapere quando una pagina sta navigando è gestendo l'evento Navigating (o Navigated). Quindi, se, ad esempio, sono su Google Maps e faccio clic su Cerca, verrà chiamato il numero Navigating, ma solo una cornice sta navigando; non tutta la pagina (nello specifico caso di Google, potrei usare la proprietà TargetFrameName di WebBrowserNavigatingEventArgs per verificare se si tratta di un frame che sta navigando, ma i frame non hanno sempre nomi). Quindi, verrà chiamato DocumentCompleted, ma non con la stessa Url come la mia proprietà UrlUrl perché era solo una cornice quella che navigava, quindi il mio programma sarebbe la cosa che sta ancora navigando, per sempre.

L'aggiunta di chiamate a Navigating e la sottrazione di chiamate a DocumentCompleted non funziona. Non sono sempre gli stessi. Non ho trovato una soluzione a questo problema già da mesi; Ho utilizzato le soluzioni 1 e 2 e spero che funzionino nella maggior parte dei casi. Il mio piano era quello di utilizzare un timer nel caso in cui alcune pagine web avessero errori o qualcosa del genere, ma non credo che Google Maps abbia errori. Potrei ancora usarlo ma l'unica soluzione più brutta sarebbe quella di bruciare il mio PC.

Edit: Finora, questo è il più vicino che ho avuto modo di una soluzione:

partial class SafeWebBrowser 
{ 
    private class SafeNavigationManager : INotifyPropertyChanged 
    { 
     private SafeWebBrowser Parent; 
     private bool _IsSafeNavigating = false; 
     private int AccumulatedNavigations = 0; 
     private bool NavigatingCalled = false; 

     public event PropertyChangedEventHandler PropertyChanged; 

     public bool IsSafeNavigating 
     { 
      get { return _IsSafeNavigating; } 
      private set { SetIsSafeNavigating(value); } 
     } 

     public SafeNavigationManager(SafeWebBrowser parent) 
     { 
      Parent = parent; 
     } 

     private void SetIsSafeNavigating(bool value) 
     { 
      if (_IsSafeNavigating != value) 
      { 
       _IsSafeNavigating = value; 
       OnPropertyChanged(new PropertyChangedEventArgs("IsSafeNavigating")); 
      } 
     } 

     private void UpdateIsSafeNavigating() 
     { 
      IsSafeNavigating = (AccumulatedNavigations != 0) || (NavigatingCalled == true); 
     } 

     private bool IsMainFrameCompleted(WebBrowserDocumentCompletedEventArgs e) 
     { 
      return Parent.ReadyState == WebBrowserReadyState.Complete && e.Url == Parent.Url; 
     } 

     protected void OnPropertyChanged(PropertyChangedEventArgs e) 
     { 
      if (PropertyChanged != null) PropertyChanged(this, e); 
     } 

     public void OnNavigating(WebBrowserNavigatingEventArgs e) 
     { 
      if (!e.Cancel) NavigatingCalled = true; 
      UpdateIsSafeNavigating(); 
     } 

     public void OnNavigated(WebBrowserNavigatedEventArgs e) 
     { 
      NavigatingCalled = false; 
      AccumulatedNavigations++; 
      UpdateIsSafeNavigating(); 
     } 

     public void OnDocumentCompleted(WebBrowserDocumentCompletedEventArgs e) 
     { 
      NavigatingCalled = false; 
      AccumulatedNavigations--; 
      if (AccumulatedNavigations < 0) AccumulatedNavigations = 0; 
      if (IsMainFrameCompleted(e)) AccumulatedNavigations = 0; 
      UpdateIsSafeNavigating(); 
     } 
    } 
} 

SafeWebBrowser eredita WebBrowser. I metodi OnNavigating, OnNavigated e OnDocumentCompleted vengono richiamati nei corrispondenti metodi sovrascritti WebBrowser. La proprietà IsSafeNavigating è quella che mi consente di sapere se sta navigando o meno.

risposta

1

No, non esiste alcun metodo che funzioni per i siti Web tutti. Motivo: un Javascript può attivare improvvisamente una navigazione (pensa AJAX ...) e non c'è modo di prevedere se o quando ciò accada. A meno che tu non sviluppi per un sito web specifico, naturalmente.

Mi raccomando di fare una domanda diversa: cosa succede se la navigazione avviene mentre si vuole fare qualcosa? Una volta che sai che puoi prendere l'errore.

+0

Ma anche se JavaScript attiva una navigazione, verranno chiamati gli eventi 'Navigating' e' Navigated'. L'ho provato e ha funzionato in questo modo. Non capisco il tuo secondo paragrafo. Cosa intendi con "mentre vuoi fare qualcosa"? – Juan

+0

@jsoldi ho paura che FrankJK sia sbagliato, se c'è una volontà, c'è un modo. si arriva a un riposo di 5 secondi dopo che l'intero documento è stato caricato per attendere la reindirizzamento di JavaScript, o per controllarli dopo che il documento è stato completato (controllando gli eventi) se uno è stato attivato o meno. In questo momento inserirò un messaggio su come superare alcuni dei tuoi ostacoli. –

0

primo che ho convertito il documento in XML e poi usato il mio metodo magico:

nodeXML = HtmlToXml.ConvertToXmlDocument((IHTMLDocument2)htmlDoc.DomDocument); 
    if (ExitWait(false)) 
     return false; 

conversione:

public static XmlNode ConvertToXmlDocument(IHTMLDocument2 doc2) 
{ 
    XmlDocument xmlDoc = new XmlDocument(); 
    IHTMLDOMNode htmlNodeHTML = null; 
    XmlNode xmlNodeHTML = null; 

    try 
    { 
     htmlNodeHTML = (IHTMLDOMNode)((IHTMLDocument3)doc2).documentElement; 
     xmlDoc.AppendChild(xmlDoc.CreateXmlDeclaration("1.0", ""/*((IHTMLDocument2)htmlDoc.DomDocument).charset*/, "")); 
     xmlNodeHTML = xmlDoc.CreateElement("html"); // create root node 
     xmlDoc.AppendChild(xmlNodeHTML); 
     CopyNodes(xmlDoc, xmlNodeHTML, htmlNodeHTML); 
    } 
    catch (Exception err) 
    { 
     Utils.WriteLog(err, "Html2Xml.ConvertToXmlDocument"); 
    } 

metodo magico:

private bool ExitWait(bool bDelay) 
{ 
    if (m_bStopped) 
     return true; 
    if (bDelay) 
    { 
     DateTime now = DateTime.Now; 
     DateTime later = DateTime.Now; 
     TimeSpan difT = (later - now); 
     while (difT.TotalMilliseconds < MainDef.IE_PARSER_DELAY) 
     { 
      Application.DoEvents(); 
      System.Threading.Thread.Sleep(10); 
      later = DateTime.Now; 
      difT = later - now; 
      if (m_bStopped) 
       return true; 
     } 
    } 
    return m_bStopped; 
} 

dove m_bStopped è falsa per impostazione predefinita, IE_PARSER_DELAY è un valore di timeout. Spero che questo aiuti.

3

Attendere fino a quando il documento è stato caricato è un problema difficile, ma si desidera verificare continuamente per .ReadyState e .Busy (non dimenticarlo). Ti darò alcune informazioni generali di cui avrai bisogno, quindi alla fine risponderò alla tua domanda specifica.

BTW, NC = NavigateComplete e DC = DocumentComplete.

Inoltre, se la pagina che si sta aspettando ha dei frame, è necessario procurarsi un ref e controllarli anche .busy e .readystate, e se i frame sono nidificati, i frame nidificati .readystate e .busy così, quindi è necessario scrivere una funzione che recuperi in modo ricorsivo quei riferimenti.

Ora, indipendentemente dal numero di frame, l'evento NC attivato per primo è sempre il documento principale e l'ultimo evento DC attivato è sempre quello del documento superiore (padre).

Quindi è necessario verificare se è la prima chiamata e il pDisp Is WebBrowser1.object (letteralmente ciò che si digita nella propria istruzione if) quindi si conosce il NC per il documento di livello superiore, quindi si attende che questo stesso oggetto venga visualizzato in un evento DC, quindi salva il pDisp in una variabile globale e attendi finché non viene eseguito un DC e il pDisp di DC è uguale al pDisp globale che hai salvato durante il primo evento NC (come in, il pDisp che hai salvato globalmente in il primo evento NC che ha sparato). Quindi, una volta che sai che il pDisp è stato restituito in un DC, sai che l'intero documento è finito.

Questo migliorerà il metodo del currect, tuttavia, per renderlo più infallibile, è necessario effettuare anche il controllo dei frame, poiché anche se hai fatto tutto quanto sopra, è oltre il 90% buono ma non il 100% folle prova, bisogno di fare di più per questo.

Per eseguire con successo il conteggio NC/DC in modo significativo (è possibile, fidati) è necessario salvare il pDisp di ogni NC in un array o in una raccolta, se e solo se non lo è già esistono in quell'array/collezione. La chiave per fare questo lavoro è controllare il pDisp NC duplicato e non aggiungerlo se esiste. Perché ciò che accade è che NC genera un URL particolare, quindi si verifica un reindirizzamento sul lato server o una modifica dell'URL e quando ciò accade, l'NC viene nuovamente attivato, MA accade con lo stesso oggetto pDisp utilizzato per il vecchio URL. Quindi lo stesso oggetto pDisp viene inviato al secondo evento NC che ora si verifica per la seconda volta con un nuovo URL, ma viene comunque eseguito con lo stesso identico oggetto pDisp.

Ora, poiché si dispone del conteggio di tutti gli oggetti pDisp NC univoci, è possibile (uno per uno) rimuoverli ogni volta che si verifica un evento DC, eseguendo il confronto tipico If pDisp Is pDispArray(i) Then (questo è in VB) avvolto in un ciclo For e per ognuno di essi decollato, il numero di array si avvicina a 0. Questo è il modo accurato per farlo, tuttavia, questo da solo non è sufficiente, poiché un'altra coppia NC/DC può verificarsi dopo che il conteggio raggiunge 0. Inoltre, devi ricordarti di fare lo stesso identico pDisp del ciclo For durante l'evento NavigateError come fai nell'evento DC, perché quando si verifica un errore di navigazione, viene generato un evento NavigateError INSTEAD dell'evento DC.

So che questo è stato molto da prendere, ma mi ci sono voluti anni di dover gestire questo temuto controllo per capire queste cose, ho altri metodi di codifica & se necessario, ma alcune delle cose che ho menzionato qui in relazione al fatto che WB Navigation è veramente pronto, non è mai stato pubblicato online prima, quindi spero davvero che tu li trovi utili e fammi sapere come vai. Inoltre, se vuoi/hai bisogno di chiarimenti su alcuni di questo fammi sapere, sfortunatamente, quanto sopra non è tutto se vuoi essere sicuro al 100% che la pagina web sia stata caricata, evviva.

PS: Inoltre, ho dimenticato di menzionare, fare affidamento su URL per fare qualsiasi tipo di conteggio è impreciso e una pessima idea perché diversi fotogrammi possono avere lo stesso URL - ad esempio, il sito www.microsoft.com fa questo , ci sono come 3 frame o così chiamando il sito principale di MS che vedi nella barra degli indirizzi. Non utilizzare URL per alcun metodo di conteggio.

Problemi correlati