lic*_*ger 5 c# deadlock webbrowser-control manualresetevent
我在C#-Program中遇到了WebBrowsing自动化的问题.我之前使用过代码来获取BHO,并且它正在运行.但在纯粹的c#程序中,似乎存在某种僵局.我已经指示我的程序点击搜索按钮,然后等待(通过手动重新发送)文档完成信号.但是现在似乎在ManualResetEvent发出超时信号之前不会处理点击搜索按钮.然后单击,并且还会触发DocumentComplete-Event.
对于该程序的结构:WebBrowser-Control是WindowsForm的一部分.WebBrowser控件传递给运行Thread的Class.该类再次将控件传递给另一个类,其中根据加载的webbrowser编程具体行为.
所以在Code中这样看起来:
线程的设置
_runner = new Thread(runner);
_runner.SetApartmentState(ApartmentState.STA);
_runner.IsBackground = true;
_runner.Start();
Run Code Online (Sandbox Code Playgroud)用流道法加工
b.placeTipp(workStructure);
Run Code Online (Sandbox Code Playgroud)PlaceTipp方法
public void placeTipp(ref OverallTippStructure tipp)
{
_expectedUrl = String.Empty;
_betUrl = String.Empty;
_status = BookieStatusType.CHECKLOGIN;
while (true)
{
_mreWaitForAction.Reset();
checkIETab();
switch (_status)
{
case BookieStatusType.REQUESTWEBSITE:
ConsoleWriter.writeToConsole(String.Format("Bookie {0}: Keine IE-Tab vorhanden. Fordere eine an", this.BookieName));
//if (RequestIETabEvent != null)
// RequestIETabEvent(this, new EventArgs());
_status = BookieStatusType.NAVIGATETOWEBSITE;
_mreWaitForAction.Set();
break;
case BookieStatusType.NAVIGATETOWEBSITE:
_webBrowser.Navigate(@"http://www.nordicbet.com");
break;
case BookieStatusType.CHECKLOGIN:
checkLogin();
break;
case BookieStatusType.LOGINNEEDED:
doLogin();
break;
case BookieStatusType.LOGGEDIN:
_status = BookieStatusType.SEARCHTIPP;
_mreWaitForAction.Set();
break;
case BookieStatusType.SEARCHTIPP:
searchTipp(tipp);
break;
case BookieStatusType.NAVTOSB:
NavToSB();
break;
case BookieStatusType.GETMARKET:
getMarket(tipp);
break;
case BookieStatusType.PLACEBET:
placeBet(tipp);
break;
case BookieStatusType.CONFIRMBET:
confirmBet();
break;
case BookieStatusType.EXTRACTBETDATA:
extractBetId(ref tipp);
break;
case BookieStatusType.FINISHED:
return;
}
if (!_mreWaitForAction.WaitOne(60000))
{
//Sonderüberpüfung be LoginNeeded
if (_status == BookieStatusType.LOGINNEEDED)
{
//checkLogin();
throw new BookieLoginFailedExcpetion();
}
//TIMEOUT!
ConsoleWriter.writeToConsole(String.Format("Bookie {0}: Timeout bei warten auf nächsten Schritt. Status war {1}", this.BookieName, this._status.ToString()));
throw new BookieTimeoutExcpetion(String.Format("Bookie {0}: Timeout bei dem Warten auf Ereignis", this.BookieName));
}
}
}
Run Code Online (Sandbox Code Playgroud)正在发生死锁的SearchTipp-Method:
private void searchTipp(OverallTippStructure tipp)
{
if (_webBrowser.InvokeRequired)
{
_webBrowser.Invoke(new delegatePlaceBet(searchTipp), new object[] { tipp });
}
else
{
ConsoleWriter.writeToConsole(String.Format("Bookie {0}: Suche Tipp {1}", this.BookieName, tipp.BookieMatchName));
_expectedUrl = String.Empty;
if (!_webBrowser.Url.ToString().StartsWith("https://www.nordicbet.com/eng/sportsbook"))
{
ConsoleWriter.writeToConsole(String.Format("Bookie {0}: Nicht auf Sportsbookseite. Navigiere", this.BookieName));
_status = BookieStatusType.NAVTOSB;
_mreWaitForAction.Set();
return;
}
_searchCompleted = false;
HtmlDocument doc = _webBrowser.Document;
if (doc != null)
{
mshtml.IHTMLInputElement elemSearch = (mshtml.IHTMLInputElement)doc.GetElementById("query").DomElement;
if (elemSearch != null)
{
Thread.Sleep(Delayer.delay(2000, 10000));
elemSearch.value = tipp.BookieMatchName;
mshtml.IHTMLElement elemSearchButton = (mshtml.IHTMLElement)doc.GetElementById("search_button").DomElement;
if (elemSearchButton != null)
{
Thread.Sleep(Delayer.delay(900, 4000));
elemSearchButton.click();
//elemSearchButton.InvokeMember("click");
if (!_mreWaitForAction.WaitOne(10000)) //Here The Deadlock happens
{
//Now the click event and therefor the search will be executed
ConsoleWriter.writeToConsole(String.Format("Bookie {0}: Suche ist auf Timeout gelaufen", this.BookieName));
throw new BookieTimeoutExcpetion(String.Format("Bookie {0}: Suche ist auf Timeout gelaufen", this.BookieName));
}
_mreWaitForAction.Reset();
HtmlElement spanResult = doc.GetElementById("total_ocs_count");
while (spanResult == null)
{
Thread.Sleep(500);
spanResult = doc.GetElementById("total_ocs_count");
}
int total_ocs_count = 0;
if (!Int32.TryParse(spanResult.InnerHtml, out total_ocs_count))
{
ConsoleWriter.writeToConsole(String.Format("Bookie {0}: Tip {1} nicht gefunden", this.BookieName, tipp.BookieMatchName));
throw new BookieTippNotFoundExcpetion(String.Format("Bookie {0}: Tip {1} nicht gefunden", this.BookieName, tipp.BookieMatchName));
}
if (total_ocs_count <= 0)
{
ConsoleWriter.writeToConsole(String.Format("Bookie {0}: Tip {1} nicht gefunden", this.BookieName, tipp.BookieMatchName));
throw new BookieTippNotFoundExcpetion(String.Format("Bookie {0}: Tip {1} nicht gefunden", this.BookieName, tipp.BookieMatchName));
}
/*
else if (total_ocs_count > 1)
{
throw new BookieMoreThanOneFoundExcpetion(String.Format("Bookie {0}: Tipp {1} nicht eindeutig", this.BookieName, tipp.BookieMatchName));
}
*/
ConsoleWriter.writeToConsole(String.Format("Bookie {0}: Tip {1} gefunden", this.BookieName, tipp.BookieMatchName));
_status = BookieStatusType.GETMARKET;
}
}
}
_mreWaitForAction.Set();
}
}
Run Code Online (Sandbox Code Playgroud)有人知道这里发生了什么?
谢谢
lichtbringer
通过这样做_webBrowser.Invoke(new delegatePlaceBet(searchTipp), new object[] { tipp }),您可以使该searchTipp方法在主 UI 线程上同步执行。WebBrowser这是可以理解的,因为除了创建控件的原始线程之外,您无法从任何线程访问API。
然而,这样做的话,_mreWaitForAction.WaitOne(10000)调用也会在主 UI 线程上执行,从而有效地阻塞消息循环(由 启动Application.Run)。WebBrowser需要一个功能性的消息循环,它不断地发送 Windows 消息,否则DocumentCompleted不会被触发,并且您将陷入死锁。
我的理解是,您只需在此处创建另一个线程来组织自动化场景的工作流程。你真的不需要另一个线程。 下面是如何使用/在主 UI 线程上异步完成此操作的示例,下面是在控制台应用程序中使用的示例。asyncawaitWebBrowser
或者,作为解决方法,您可以WaitWithDoEvents从此处使用,如下所示:_mreWaitForAction.WaitWithDoEvents(10000)。它等待句柄,同时仍然发送消息。您应该意识到使用 . 创建嵌套消息循环的潜在影响Application.DoEvents()。
请注意,如果您使用嵌套消息循环,您仍然不需要单独的线程。它将为您提供主 UI 线程上的线性代码工作流程。例子:
private void buttonStart_Click(object sender, EventArgs e)
{
using (var mre = new ManualResetEvent(false))
{
WebBrowserDocumentCompletedEventHandler handler = (s, args) =>
mre.Set();
this.webBrowser.DocumentCompleted += handler;
try
{
this.webBrowser.Navigate("http://www.example.com");
mre.WaitWithDoEvents(10000);
}
finally
{
this.webBrowser.DocumentCompleted -= handler;
}
}
MessageBox.Show(this.webBrowser.Document.Body.OuterHtml);
}
Run Code Online (Sandbox Code Playgroud)
尽管正如我上面提到的,您可以更自然地实现相同的目的,并且无需嵌套消息循环,使用async/ await,这是执行此操作的首选方式,IMO。
| 归档时间: |
|
| 查看次数: |
640 次 |
| 最近记录: |