Arhn - архитектура программирования

Можно ли точно знать, работает ли веб-браузер или нет?

Я пытаюсь найти способ, чтобы моя программа знала, когда веб-браузер выполняет навигацию, а когда нет. Это связано с тем, что программа будет взаимодействовать с загруженным документом через JavaScript, который будет внедрен в документ. У меня нет другого способа узнать, когда начинается навигация, кроме обработки Навигация, так как это не моя программа, а пользователь, который будет перемещаться, взаимодействуя с документом. Но тогда, когда происходит DocumentCompleted, это не обязательно означает, что навигация завершена. Я много гуглил и нашел два псевдорешения:

  1. Проверьте свойство WebBrowser ReadyState в DocumentCompleted событие. Проблема в том, что если загружается не документ, а фрейм в документе, ReadyState будет Completed, даже если основной документ не завершен.

  2. Чтобы предотвратить это, они советуют проверить, соответствует ли URL параметр, переданный в DocumentCompleted, соответствует URL из WebBrowser. Таким образом, я бы знал, что DocumentCompleted не вызывается каким-либо другим фреймом в документе.

Проблема с 2 заключается в том, что, как я уже сказал, единственный способ узнать, когда страница находится в процессе навигации, — это обработать событие Navigating (или Navigated). Так что, если, например, я нахожусь в Google Maps и нажимаю «Поиск», будет вызываться Navigating, но навигация происходит только по фрейму; не вся страница (в конкретном случае с Google я мог бы использовать свойство TargetFrameName WebBrowserNavigatingEventArgs, чтобы проверить, является ли фрейм тем, по которому осуществляется навигация, но у фреймов не всегда есть имена). Итак, после этого будет вызываться DocumentCompleted, но не с тем же Url, что и мое свойство WebBrowsers Url, потому что это был просто фрейм, который перемещался, поэтому моя программа думала, что она все еще перемещается, навсегда.

Добавление вызовов к Navigating и вычитание вызовов к DocumentCompleted также не сработает. Они не всегда одинаковы. Я не могу найти решение этой проблемы уже несколько месяцев; Я использовал решения 1 и 2 и надеюсь, что они будут работать в большинстве случаев. Мой план состоял в том, чтобы использовать таймер на случай, если на какой-то веб-странице есть ошибки или что-то в этом роде, но я не думаю, что у Google Maps есть какие-либо ошибки. Я все еще мог бы использовать его, но единственным более уродливым решением было бы сжечь мой компьютер.

Редактировать: На данный момент это самое близкое решение, которое у меня есть:

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 наследует WebBrowser. Методы OnNavigating, OnNavigated и OnDocumentCompleted вызываются для соответствующих WebBrowser переопределенных методов. Свойство IsSafeNavigating — это то, что даст мне знать, перемещается оно или нет.


Ответы:


1

Ожидание загрузки документа — сложная проблема, но вы хотите постоянно проверять .ReadyState и .Busy (не забывайте об этом). Я дам вам некоторую общую информацию, которая вам понадобится, а затем отвечу на ваш конкретный вопрос в конце.

Кстати, NC = NavigateComplete и DC = DocumentComplete.

Кроме того, если на странице, которую вы ожидаете, есть фреймы, вам нужно получить ссылку на них и проверить их .busy и .readystate, а если фреймы вложенные, то вложенные фреймы .readystate и .busy, а также, поэтому вам нужно написать функцию, которая рекурсивно извлекает эти ссылки.

Теперь, независимо от того, сколько у него кадров, первое инициированное событие NC всегда является верхним документом, а последнее инициированное событие DC всегда также является событием верхнего (родительского) документа.

Таким образом, вы должны проверить, является ли это первым вызовом и pDisp Is WebBrowser1.object (буквально это то, что вы вводите в своем операторе if), тогда вы знаете, что это NC для документа верхнего уровня, затем вы ждете, пока этот же объект появится в событии DC, поэтому сохраните pDisp в глобальной переменной и подождите, пока не запустится контроллер домена и pDisp контроллера домена не станет равным глобальному pDisp, который вы сохранили во время первого события NC (например, pDisp, который вы сохранили глобально в первом событии NC что стрелял). Таким образом, как только вы узнаете, что pDisp был возвращен в контроллер домена, вы знаете, что загрузка всего документа завершена.

Это улучшит ваш текущий метод, однако, чтобы сделать его более надежным, вам также необходимо выполнить проверку фреймов, поскольку даже если вы сделали все вышеперечисленное, это более чем на 90% хорошо, но не на 100%. сделать больше для этого.

Для успешного подсчета NC/DC осмысленным образом (это возможно, поверьте мне) вам необходимо сохранить pDisp каждого NC в массиве или коллекции, если и только если он еще не существует в этом массиве. /коллекция. Ключом к выполнению этой работы является проверка дублирующегося NC pDisp, а не добавление его, если он существует. Потому что происходит следующее: NC запускается с определенным URL-адресом, затем происходит перенаправление на стороне сервера или изменение URL-адреса, и когда это происходит, NC запускается снова, НО это происходит с тем же объектом pDisp, который использовался для старого URL-адреса. Таким образом, один и тот же объект pDisp отправляется второму событию NC, которое теперь происходит во второй раз с новым URL-адресом, но все еще выполняется с одним и тем же объектом pDisp.

Теперь, поскольку у вас есть подсчет всех уникальных объектов NC pDisp, вы можете (один за другим) удалять их по мере возникновения каждого события DC, выполняя типичное сравнение If pDisp Is pDispArray(i) Then (это в VB), завернутое в цикл For, и для каждого один снят, ваш счетчик массива приблизится к 0. Это точный способ сделать это, однако одного этого недостаточно, так как другая пара NC/DC может появиться после того, как ваш счетчик достигнет 0. Кроме того, вы должны помнить для выполнения той же проверки For Loop pDisp в событии NavigateError, что и в событии DC, поскольку при возникновении ошибки навигации запускается событие NavigateError ВМЕСТО события DC.

Я знаю, что это было сложно, но мне потребовались годы, чтобы разобраться с этим ужасным контролем, чтобы понять эти вещи, у меня есть другой код и методы, если вам нужно, но некоторые из вещей, которые я упомянул здесь в отношении к WB Navigation действительно готовы, ранее не публиковались в Интернете, поэтому я очень надеюсь, что вы найдете их полезными и сообщите мне, как вы идете. Кроме того, если вам нужны пояснения по некоторым из этих вопросов, дайте мне знать, к сожалению, это еще не все, если вы хотите быть на 100% уверены, что веб-страница загружается, ура.

PS: Кроме того, забыл упомянуть, полагаться на URL-адреса для какого-либо подсчета неточно и очень плохая идея, потому что несколько фреймов могут иметь один и тот же URL-адрес — например, веб-сайт www.microsoft.com делает это, есть такие 3 кадра или около того, вызывающие основной сайт MS, которые вы видите в адресной строке. Не используйте URL для любого метода подсчета.

11.02.2012

2

Сначала я преобразовал документ в XML, а затем использовал свой волшебный метод:

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

преобразование:

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

магический метод:

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

где m_bStopped по умолчанию имеет значение false, IE_PARSER_DELAY — значение тайм-аута. Надеюсь, это поможет.

08.11.2010
Новые материалы

Коллекции публикаций по глубокому обучению
Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

Представляем: Pepita
Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

Советы по коду Laravel #2
1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

3 способа решить квадратное уравнение (3-й мой любимый) -
1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

Создание VR-миров с A-Frame
Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

Демистификация рекурсии
КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..