gpt4 book ai didi

c# - EventHandle.WaitOne + WebBrowser = 等待 DocumentComplete 时死锁

转载 作者:太空狗 更新时间:2023-10-29 23:31:35 27 4
gpt4 key购买 nike

我在 C# 程序中遇到 WebBrowsing 自动化的问题。我之前已经将代码用于 BHO,并且可以正常工作。但是在纯 c# 程序中似乎存在某种死锁。我已指示我的程序单击搜索按钮,然后等待(通过手动重置事件)文档完成发出信号。但现在看来,在 ManualResetEvent 发出超时信号之前,不会处理对搜索按钮的点击。然后单击并触发 DocumentComplete-Event。

对于该程序的结构:WebBrowser-Control 是 WindowsForm 的一部分。 WebBrowser 控件被传递给运行线程的类。该类再次将控制权传递给另一个类,根据加载的网络浏览器对具体行为进行编程。

所以在代码中这看起来是这样的:

  1. 线程的设置

    _runner = new Thread(runner);
    _runner.SetApartmentState(ApartmentState.STA);
    _runner.IsBackground = true;
    _runner.Start();
  2. 使用Runner-Method的处理

    b.placeTipp(workStructure);
  3. 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));
    }
    }
    }
  4. 发生死锁的 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();
    }
    }

有人知道这里发生了什么吗?

谢谢

光明使者

最佳答案

通过执行 _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/awaithere's an example在控制台应用程序中使用 WebBrowser

或者,作为解决方法,您可以使用 here 中的 WaitWithDoEvents ,像这样:_mreWaitForAction.WaitWithDoEvents(10000)。它在等待句柄的同时仍在发送消息。你应该知道 potential implications使用 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);
}

尽管正如我上面提到的,您可以使用 async/await 更自然地实现相同的效果,而无需嵌套消息循环,这是一种首选的方式这个,海事组织。

关于c# - EventHandle.WaitOne + WebBrowser = 等待 DocumentComplete 时死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20454050/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com