1

Exception: Cef.Shutdown must be called on the same thread that Cef.Initialize was called - typically your UI thread. If you called Cef.Initialize on a Thread other than the UI thread then you will need to call Cef.Shutdown on the same thread. Cef.Initialize was called on ManagedThreadId: 1where Cef.Shutdown is being called on ManagedThreadId: 4

class Program
{
    static void Main(string[] args)
    {
        MainAsync().Wait();
    }

    private static async Task MainAsync()
    {
        List<string> urls = new List<string>();
        urls.Add("https://google.com");

        CefSharpWrapper wrapper = new CefSharpWrapper();

        wrapper.InitializeBrowser();

        foreach (string url in urls)
        {
            await wrapper.GetResultAfterPageLoad(url);
        }
        wrapper.ShutdownBrowser();
    }
}

public sealed class CefSharpWrapper
{
    private ChromiumWebBrowser _browser;

    public void InitializeBrowser()
    {
        Cef.Initialize(new CefSettings());

        _browser = new ChromiumWebBrowser();

        AutoResetEvent waitHandleOnBrowserInitialized = new AutoResetEvent(false);

        EventHandler onBrowserInitialized = null;

        onBrowserInitialized = async (sender, e) =>
        {
            _browser.BrowserInitialized -= onBrowserInitialized;

            waitHandleOnBrowserInitialized.Set();
        };

        _browser.BrowserInitialized += onBrowserInitialized;

        waitHandleOnBrowserInitialized.WaitOne();

    }

    public Task<bool> GetResultAfterPageLoad(string pageUrl)
    {

        TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
        EventHandler<LoadingStateChangedEventArgs> onPageLoaded = null;

        // An event that is fired when the first page is finished loading.
        // This returns to us from another thread.
        onPageLoaded = async (sender, args) =>
        {
            // Check to see if loading is complete - this event is called twice, one when loading starts
            // second time when it's finished
            // (rather than an iframe within the main frame).
            if (!args.IsLoading)
            {
                // Remove the load event handler, because we only want one snapshot of the initial page.
                _browser.LoadingStateChanged -= onPageLoaded;

                tcs.SetResult(true);
            }
        };

        _browser.LoadingStateChanged += onPageLoaded;

        _browser.Load(pageUrl);

        return tcs.Task;

    }

    public void ShutdownBrowser()
    {
        // Clean up Chromium objects.  You need to call this in your application otherwise
        // you will get a crash when closing.
        Cef.Shutdown();
    }
}
Elgin Maksim
  • 31
  • 1
  • 10
  • What's unclear about the exception exactly? Move your Cef.Initialize and Cef.Shutdown calls to your static void main method either side of your MainAsync call. – amaitland May 27 '21 at 01:59
  • good question! I've never found a solution to this, either. Following. Best of Luck! – MC9000 May 27 '21 at 04:10
  • The reason you end up on different threads is detailed in https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/ LoadingStateChange is called on the CEF UI thread, your call to TaskCompletionSource.SetResult ends with the continuation running on a different thread. Move the Cef.Initialize and Cef.Shutdown calls as per my first comment. – amaitland May 27 '21 at 10:25
  • thanks amaitland, then how to use multiple ChromiumWebBrowser – Elgin Maksim May 27 '21 at 15:21

1 Answers1

0

Amaitland, thanks for the reply.

class Program
{
  static void Main(string[] args)
  {
      CefSharpWrapper.InitializeBrowser();
      MainAsync().Wait();
      CefSharpWrapper.ShutdownBrowser();
  }

  private static async Task MainAsync()
  {
      List<string> urls = new List<string>();
      urls.Add("https://google.com");

      CefSharpWrapper wrapper = new CefSharpWrapper();

      foreach (string url in urls)
      {
        await wrapper.GetResultAfterPageLoad(url);
      }
  }
}

public sealed class CefSharpWrapper
{
  private static ChromiumWebBrowser _browser;

  public static void InitializeBrowser()
  {
      Cef.Initialize(new CefSettings());

      _browser = new ChromiumWebBrowser();

      AutoResetEvent waitHandleOnBrowserInitialized = new AutoResetEvent(false);

      EventHandler onBrowserInitialized = null;

      onBrowserInitialized = async (sender, e) =>
      {
          _browser.BrowserInitialized -= onBrowserInitialized;

          waitHandleOnBrowserInitialized.Set();
      };

      _browser.BrowserInitialized += onBrowserInitialized;

      waitHandleOnBrowserInitialized.WaitOne();

  }

  public Task<bool> GetResultAfterPageLoad(string pageUrl)
  {

      TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
      EventHandler<LoadingStateChangedEventArgs> onPageLoaded = null;

    // An event that is fired when the first page is finished loading.
    // This returns to us from another thread.
      onPageLoaded = async (sender, args) =>
      {
        // Check to see if loading is complete - this event is called twice, one when loading starts
        // second time when it's finished
        // (rather than an iframe within the main frame).
          if (!args.IsLoading)
          {
            // Remove the load event handler, because we only want one snapshot of the initial page.
              _browser.LoadingStateChanged -= onPageLoaded;

              tcs.SetResult(true);
          }
      };

      _browser.LoadingStateChanged += onPageLoaded;

      _browser.Load(pageUrl);

      return tcs.Task;

  }

  public static void ShutdownBrowser()
  {
    // Clean up Chromium objects.  You need to call this in your application otherwise
    // you will get a crash when closing.
      Cef.Shutdown();
  }
}
Elgin Maksim
  • 31
  • 1
  • 10
  • Is this for scraping data from the url? or just verifying it loaded? is there a way, with this method to get the html source from a dynamic page? – MC9000 May 27 '21 at 16:58
  • dumb question: how can you call initializebrowser without creating an instance of CefSharpWrapper? I see it, somehow works, but not sure why it would work – MC9000 May 27 '21 at 17:09
  • 1
    here Cef.Initialize and Cef.Shutdown calls static void main method. working with a dynamic page in the MainAsync method. but I'm not sure if Cef will work from multiple applications at the same time – Elgin Maksim May 27 '21 at 18:46