c# – EventHandle.WaitOne WebBrowser =等待DocumentComplete时
我在C#-Program中遇到了WebBrowsing自动化的问题.
我之前使用过代码来获取BHO,并且它正在运行. 但在纯粹的c#程序中,似乎存在某种僵局.我已经指示我的程序点击搜索按钮,然后等待(通过手动重新发送)文档完成信号.但是现在似乎在ManualResetEvent发出超时信号之前不会处理点击搜索按钮.然后单击,并且还会触发DocumentComplete-Event. 对于该程序的结构: 所以在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 解决方法
通过执行_webBrowser.Invoke(new delegatePlaceBet(searchTipp),new object [] {tipp}),可以在主UI线程上同步执行searchTipp方法.这是可以理解的,因为您无法从创建控件的原始线程以外的任何线程访问WebBrowser API.
但是,通过这样做,_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. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |