我之前使用过代码来获取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();
>使用跑步者方法的处理
b.placeTipp(workStructure);
> 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)); } } }
>发生死锁的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}",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",tipp.BookieMatchName)); throw new BookieTippNotFoundExcpetion(String.Format("Bookie {0}: Tip {1} nicht gefunden",tipp.BookieMatchName)); } if (total_ocs_count <= 0) { ConsoleWriter.writeToConsole(String.Format("Bookie {0}: Tip {1} nicht gefunden",tipp.BookieMatchName)); } /* else if (total_ocs_count > 1) { throw new BookieMoreThanOneFoundExcpetion(String.Format("Bookie {0}: Tipp {1} nicht eindeutig",tipp.BookieMatchName)); } */ ConsoleWriter.writeToConsole(String.Format("Bookie {0}: Tip {1} gefunden",tipp.BookieMatchName)); _status = BookieStatusType.GETMARKET; } } } _mreWaitForAction.Set(); } }
有人知道这里发生了什么?
谢谢
lichtbringer
解决方法
但是,通过这样做,_mreWaitForAction.WaitOne(10000)调用将在主UI线程上执行,从而有效地阻止消息循环(由Application.Run启动). WebBrowser需要一个功能性的消息循环,它不断地传递Windows消息,否则DocumentCompleted不会被触发,并且您将陷入僵局.
我的理解是,您只需在此处创建另一个线程来组织自动化方案的工作流程.你真的不需要另一个线程. Here’s an example如何在主UI线程上异步完成,使用async / await和here’s an example在控制台应用程序中使用WebBrowser.
或者,作为一种变通方法,您可以使用here中的WaitWithDoEvents,如下所示:_mreWaitForAction.WaitWithDoEvents(10000).它在等待处理消息时等待处理.您应该了解使用Application.DoEvents()创建嵌套消息循环的potential implications.
注意,如果使用嵌套消息循环,则仍然不需要单独的线程.它将为您提供主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); }
虽然正如我上面提到的,你可以更自然地实现同样的目的,没有嵌套的消息循环,使用async / await,这是这样做的首选方法,IMO.