Крис,
Я прохожу вас здесь возможную реализацию, которая решает эту проблему, но, пожалуйста, взглянуть на комментарии здесь, под которые мне пришлось столкнуться и исправить, прежде чем все работало, как я ожидал. Вот пример способа делать некоторые действия на странице в WebBrowser (обратите внимание, что WebBrowser является частью формы в моем случае):
internal ActionResponse CheckMessages() //Action Response is a custom class of mine to store some data coming from pages
{
//go to messages
HtmlDocument doc = WbLink.Document; //wbLink is a referring link to a webBrowser istance
HtmlElement ele = doc.GetElementById("message_alert_box");
if (ele == null)
return new ActionResponse(false);
object obj = ele.DomElement;
System.Reflection.MethodInfo mi = obj.GetType().GetMethod("click");
mi.Invoke(obj, new object[0]);
semaphoreForDocCompletedEvent = WaitForDocumentCompleted(); //This is a simil-waitOne statement (1)
if (!semaphoreForDocCompletedEvent)
throw new Exception("sequencing of Document Completed events is failed.");
//get the list
doc = WbLink.Document;
ele = doc.GetElementById("mailz");
if (!ele.WaitForAvailability("mailz", Program.BrowsingSystem.Document, 10000)) //This is a simil-waitOne statement (2)
ele = doc.GetElementById("mailz");
ele = doc.GetElementById("mailz");
//this contains a tbody
HtmlElement tbody = ele.FirstChild;
//count how many elemetns are espionage reports, these elements are inline then counting double with their wrappers on top of them.
int spioCases = 0;
foreach (HtmlElement trs in tbody.Children)
{
if (trs.GetAttribute("id").ToLower().Contains("spio"))
spioCases++;
}
int nMessages = tbody.Children.Count - 2 - spioCases;
//create an array of messages to store data
GameMessage[] archive = new GameMessage[nMessages];
for (int counterOfOpenMessages = 0; counterOfOpenMessages < nMessages; counterOfOpenMessages++)
{
//open first element
WbLink.ScriptErrorsSuppressed = true;
ele = doc.GetElementById("mailz");
//this contains a tbody
tbody = ele.FirstChild;
HtmlElement mess1 = tbody.Children[1];
int idMess1 = int.Parse(mess1.GetAttribute("id").Substring(0, mess1.GetAttribute("id").Length - 2));
//check if subsequent element is not a spio report, in case it is then the element has not to be opened.
HtmlElement mess1Sibling = mess1.NextSibling;
if (mess1Sibling.GetAttribute("id").ToLower().Contains("spio"))
{
//this is a wrapper for spio report
ReadSpioEntry(archive, counterOfOpenMessages, mess1, mess1Sibling);
//delete first in line
DeleteFirstMessageItem(doc, ref ele, ref obj, ref mi, ref tbody);
semaphoreForDocCompletedEvent = WaitForDocumentCompleted(6); //This is a simil-waitOne statement (3)
}
else
{
//It' s anormal message
OpenMessageEntry(ref obj, ref mi, tbody, idMess1); //This opens a modal dialog over the page, and it is not generating a DocumentCompleted Event in the webBrowser
//actually opening a message generates 2 documetn completed events without any navigating event issued
//Application.DoEvents();
semaphoreForDocCompletedEvent = WaitForDocumentCompleted(6);
//read element
ReadMessageEntry(archive, counterOfOpenMessages);
//close current message
CloseMessageEntry(ref ele, ref obj, ref mi); //this closes a modal dialog therefore is not generating a documentCompleted after!
semaphoreForDocCompletedEvent = WaitForDocumentCompleted(6);
//delete first in line
DeleteFirstMessageItem(doc, ref ele, ref obj, ref mi, ref tbody); //this closes a modal dialog therefore is not generating a documentCompleted after!
semaphoreForDocCompletedEvent = WaitForDocumentCompleted(6);
}
}
return new ActionResponse(true, archive);
}
На практике этот метод принимает страницу из ММОРПГА и читает сообщения, отправленные в учетную запись другими игроками и сохраняющие их в классе ActionResponse с помощью метода ReadMessageEntry.
Помимо реализации и логики кода, которые действительно зависят от случая (и не полезны для вас), есть несколько интересных элементов, которые могут быть приятными для вас. я поставил некоторые комментарии в коде и выделены 3 важные моменты [с символами (1)
, (2)
и (3)
]
алго является:
1) Прибытие на страницу
2) получить базовый документ от WebBrowser
3) найти элемент, чтобы нажать, чтобы попасть на страницу сообщений [сделано с: HtmlElement ele = doc.GetElementById("message_alert_box");
]
4) Запустите событие щелчка по нему с помощью экземпляра MethodInfo и обратного вызова [это вызывает другую страницу, так что документ будет завершен рано или поздно]
5) Подождите, пока документ будет называться, а затем протекают [сделано с: semaphoreForDocCompletedEvent = WaitForDocumentCompleted();
в точке (1)]
6) Выборка нового документа из веб-браузера после того, как страница изменена
7) найти конкретный якорь на страницу, которая является определяющим, где сообщение я хотите прочитать:
8) Убедитесь, что такая TAG присутствует в (возможно, некоторые AJAX задерживают то, что я хочу читать, чтобы быть готовыми) [сделано с помощью: ele.WaitForAvailability("mailz", Program.BrowsingSystem.Document, 10000)
, что является точкой (2)]
9) Проведите весь цикл для чтения каждого сообщения, что подразумевает открытие модальный диалог, который находится на одной странице, поэтому не генерирует DocumentCompleted, читает его, когда он готов, затем закрывает его и reloop. Для этого конкретного случая я использую перегрузку (1) называется semaphoreForDocCompletedEvent = WaitForDocumentCompleted(6);
в точке (3)
Теперь три метода я использую, чтобы сделать паузу, проверить и прочитать:
(1) Для остановки во время DocumentCompleted поднимается без перезаряда метод DocumentCompleted, который может быть использован для более чем одной цели (как в вашем случае)
private bool WaitForDocumentCompleted()
{
Thread.SpinWait(1000); //This is dirty but working
while (Program.BrowsingSystem.IsBusy) //BrowsingSystem is another link to Browser that is made public in my Form and IsBusy is just a bool put to TRUE when Navigating event is raised and but to False when the DocumentCOmpleted is fired.
{
Application.DoEvents();
Thread.SpinWait(1000);
}
if (Program.BrowsingSystem.IsInfoAvailable) //IsInfoAvailable is just a get property to cover webBroweser.Document inside a lock statement to protect from concurrent accesses.
{
return true;
}
else
return false;
}
(2) подождите конкретного тега будет доступен на странице:
public static bool WaitForAvailability(this HtmlElement tag, string id, HtmlDocument documentToExtractFrom, long maxCycles)
{
bool cond = true;
long counter = 0;
while (cond)
{
Application.DoEvents(); //VERIFY trovare un modo per rimuovere questa porcheria
tag = documentToExtractFrom.GetElementById(id);
if (tag != null)
cond = false;
Thread.Yield();
Thread.SpinWait(100000);
counter++;
if (counter > maxCycles)
return false;
}
return true;
}
(3) Грязная трюка, ожидающая документального завершения, который когда-либо появится, потому что никакие кадры не нуждаются в перезагрузке на странице!
private bool WaitForDocumentCompleted(int seconds)
{
int counter = 0;
while (Program.BrowsingSystem.IsBusy)
{
Application.DoEvents();
Thread.Sleep(1000);
if (counter == seconds)
{
return true;
}
counter++;
}
return true;
}
проходишь вы также DocumentCompleted метода и Навигационный, чтобы дать вам полную картину о том, как я использовал их.
private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (Program.BrowsingSystem.BrowserLink.ReadyState == WebBrowserReadyState.Complete)
{
lock (Program.BrowsingSystem.BrowserLocker)
{
Program.BrowsingSystem.ActualPosition = Program.BrowsingSystem.UpdatePosition(Program.BrowsingSystem.Document);
Program.BrowsingSystem.CheckContentAvailability();
Program.BrowsingSystem.IsBusy = false;
}
}
}
private void webBrowser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
lock (Program.BrowsingSystem.BrowserLocker)
{
Program.BrowsingSystem.ActualPosition.PageName = OgamePages.OnChange;
Program.BrowsingSystem.IsBusy = true;
}
}
Пожалуйста, дайте посмотреть here знать бардак за DoEvents(), если вы уже в курсе о деталях, которые лежат за реализацией представленного здесь (надеюсь, что это не является проблемой для ссылок на другие сайты из S. Переполнение).
Небольшая заключительная записка о том, что вам нужно поместить вызов метода Navigate в Invoke, когда вы используете его из экземпляра формы: это кристально ясно, что вам нужно вызвать, потому что методы, которые необходимо использовать webBrowser (или даже имеющий его в качестве реферируемой переменной) должен быть запущен в той же самой теме самого веб-браузера!
Кроме того, если WB является дочерним элементом какого-либо контейнера формы, ему также необходимо, чтобы поток из того, где он был создан, является тем же самым из создания Формы, и для транзитивности все методы, которые необходимо использовать для WB необходимо вызвать в потоке формы (в этом случае вызов переводит ваши вызовы в собственный поток Form). Надеюсь, это полезно для вас (я просто оставил комментарий // VERIFY в коде на моем родном языке, чтобы вы знали, что я думаю о Application.DoEvents()).
С наилучшими пожеланиями, Alex
Это в основном то, что у меня есть, за исключением того, что я делаю прямой вызов autoResetEvent.Set() (autoResetEvent.Reset() будет продолжать блокирование, не разблокирует) вместо использования промежуточного triggerFunction. Однако этот код не работает (опять же, из того, что я понимаю, это потому, что EventHandler выполняется в том же потоке, что и начальный вызов, поэтому, если поток блокируется бесконечно, событие никогда не будет срабатывать). – Chris
Что-то кажется неправильным в вашей главной теме страницы, которая мешает ему завершить выполнение. Если вы приостановите свой поток, страница должна просто сохранить рендеринг (куры - это поток). Может быть, я просто показал неправильные вызовы? Тем не менее, если что-то вы хотите опубликовать в своем триггере событий, потому что он гарантирует, что страница закончила рендеринг (по крайней мере, как полагает сервер) –
Я отредактировал свое сообщение, чтобы сделать обработку событий DocumentCompleted более понятной. Ясно, что что-то не так с тем, как работает поток, но я не могу понять, что это такое. – Chris