2010-11-08 11 views
6

Estoy tratando de encontrar una forma para que mi programa sepa cuándo navega WebBrowser y cuándo no. Esto se debe a que el programa interactuará con el documento cargado a través de JavaScript que se inyectará en el documento. No tengo otra forma de saber cuándo comienza a navegar que manejar el evento Navigating ya que no es mi programa sino el usuario que navegará al interactuar con el documento. Pero luego, cuando ocurre DocumentCompleted, no necesariamente significa que ha terminado de navegar. He estado buscando en Google mucho y encontré dos pseudo-soluciones:¿Es posible saber con certeza si un WebBrowser está navegando o no?

  1. Comprobar si ReadyState propiedad de WebBrowser en caso DocumentCompleted. El problema con esto es que si no se carga el documento sino un marco en el documento, el ReadyState será Completed incluso si el documento principal no se completa.

  2. Para evitar esto, se aconsejan para ver si el parámetro pasado a UrlDocumentCompleted coincide con el Url del WebBrowser. De esta manera sabría que DocumentCompleted no está siendo invocado por algún otro marco en el documento.

El problema con 2 es que, como ya he dicho, la única forma que tengo de saber cuando una página está navegando es por el manejo del evento Navigating (o Navigated). Entonces, si, por ejemplo, estoy en Google Maps y hago clic en Buscar, se llamará al Navigating, pero solo está navegando un marco; no toda la página (en el caso específico de Google, podría usar la propiedad TargetFrameName de WebBrowserNavigatingEventArgs para verificar si es un marco el que está navegando, pero los marcos no siempre tienen nombres). Así que después de eso, se llamará DocumentCompleted, pero no con la misma Url como mi propiedad WebBrowser s Url porque era solo un marco el que navegaba, por lo que mi programa querría que siga navegando, para siempre.

Agregar llamadas a Navigating y restar llamadas a DocumentCompleted tampoco funcionará. No son siempre lo mismo. No he encontrado una solución a este problema desde hace meses; He estado usando las soluciones 1 y 2 y espero que funcionen en la mayoría de los casos. Mi plan era usar un temporizador en caso de que alguna página web tenga errores o algo así, pero no creo que Google Maps tenga ningún error. Todavía podría usarlo, pero la única solución más fea sería quemar mi PC.

Editar: Hasta ahora, esto es lo más cercano que tengo a una solución:

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 hereda WebBrowser. Se invocan los métodos OnNavigating, OnNavigated y OnDocumentCompleted en los métodos reemplazados WebBrowser correspondientes. La propiedad IsSafeNavigating es la que me avisaría si está navegando o no.

Respuesta

1

No, no hay ningún método que funcione para todos los sitios web. Motivo: un Javascript podría desencadenar una navegación repentina (piense en AJAX ...) y no hay manera de predecir si esto sucede o cuándo. A menos que se desarrolle para un sitio web específico, por supuesto.

Te recomiendo que te hagas una pregunta diferente: ¿Qué sucede si la navegación se lleva a cabo mientras quieres hacer algo? Una vez que sepa que puede detectar el error.

+0

Pero incluso si javascript desencadena una navegación, '' Navigating' y eventos Navigated' se llamará. Lo probé y funcionó de esta manera. No entiendo tu segundo párrafo. ¿A qué te refieres con "mientras quieres hacer algo"? – Juan

+0

@jsoldi tengo miedo de que FrankJK esté equivocado, si hay voluntad, hay una manera. obtiene un descanso de 5 segundos después de que todo el documento se haya cargado para esperar a que se redirija el código de JavaScript o lo busca después de completar el documento (mediante la comprobación de eventos) si uno se activa o no. Voy a (ahora mismo) publicar un mensaje sobre cómo puede superar algunos de sus obstáculos. –

0

primera vez que lo convierte el documento a XML y luego usó mi método mágico:

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

conversión:

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"); 
    } 

método mágico:

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; 
} 

donde m_bStopped es falsa de forma predeterminada, IE_PARSER_DELAY es un valor de tiempo de espera excedido. Espero que esto ayude.

3

Esperar hasta que el documento se haya cargado es un problema difícil, pero desea comprobar continuamente si se encuentra .ReadyState y .Busy (no lo olvide). Le daré información general que necesitará, luego responderé su pregunta específica al final.

BTW, NC = NavigateComplete y DC = DocumentComplete.

Además, si la página que está esperando tiene marcos, necesita obtener una referencia para ellos y verificar también su .busy y .readystate, y si los marcos están anidados, los marcos anidados .readystate y .busy también, por lo que necesita escribir una función que recursivamente recupere esas referencias.

Ahora, independientemente de la cantidad de marcos que tenga, el evento NC disparado primero es siempre el documento superior, y el último evento DC disparado es siempre el del documento superior (principal) también.

lo que debe comprobar para ver si es la primera llamada y el pDisp Is WebBrowser1.object (literalmente, eso es lo que escribe en su sentencia if), entonces usted sabe su la NC para el documento de nivel superior, entonces se espera para este mismo objeto que aparezca en un evento DC, así que guarde el pDisp en una variable global, y espere hasta que se ejecute un DC y que el pDisp de DC sea igual al Global pDisp que haya guardado durante el primer evento NC (como en, el pDisp que guardó globalmente en el primer evento NC que se disparó). Entonces, una vez que sepa que pDisp fue devuelto en un DC, sabrá que el documento completo se terminó de cargar.

Esto mejorará su método correcto, sin embargo, para hacerlo más infalible, también debe hacer las comprobaciones de marcos, ya que incluso si hizo todo lo anterior, es más del 90% bueno pero no 100% tonto prueba, necesitamos hacer más por esto.

Para hacer un conteo exitoso de NC/DC de una manera significativa (es posible, créame) necesita guardar el pDisp de cada NC en una matriz o una colección, si y solo si no lo hace ya existir en esa matriz/colección. La clave para hacer esto es verificar el NC pDisp duplicado y no agregarlo si existe. Porque lo que sucede es que NC dispara con una URL particular, luego ocurre un redireccionamiento del lado del servidor o un cambio de URL y cuando esto sucede, la NC se activa nuevamente, PERO sucede con el mismo objeto pDisp que se usó para la antigua URL. Por lo tanto, el mismo objeto pDisp se envía al segundo evento NC que ahora se está produciendo por segunda vez con una nueva URL, pero que todavía se está haciendo con el mismo objeto pDisp.

Ahora, como tiene un recuento de todos los objetos NC pDisp exclusivos, puede (uno por uno) eliminarlos a medida que ocurre cada evento de DC, haciendo la típica comparación If pDisp Is pDispArray(i) Then (esto está en VB) envuelta en un bucle For , y para cada uno despegado, el recuento de matriz se acercará más a 0. esta es la forma exacta de hacerlo, sin embargo, esto por sí solo no es suficiente, ya que otro par NC/DC puede ocurrir después de que su cuenta llega a 0. Además, usted tiene que recordar que hacer exactamente el mismo bucle For pDisp comprobación en caso NavigateError como lo hace en el caso de CC, ya que cuando se produce un error de navegación, un evento NavigateError se dispara en lugar del evento DC.

Sé que esto era mucho para tomar, pero me tomó años tener que lidiar con este temido control para resolver estas cosas, tengo otros métodos de código & si es necesario, pero algunas de las cosas que mencionado aquí en relación con que WB Navigation esté verdaderamente preparado, no se haya publicado en línea antes, por lo que espero que los encuentres útiles y me digas cómo te va. Además, si quiere/necesita aclaración sobre algo de esto, avísenme, lamentablemente, lo anterior no es todo si quiere estar 100% seguro de que la página web está lista para cargarse, aplausos.

PD: Además, se olvidó de mencionar que depender de las URL para hacer cualquier tipo de recuento es incorrecto y una muy mala idea porque varios marcos pueden tener la misma URL. Por ejemplo, el sitio web www.microsoft.com hace esto , hay como 3 cuadros más o menos llamando al sitio principal de MS que ve en la barra de direcciones. No use las URL para ningún método de conteo.

Cuestiones relacionadas