Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OffScreen - ChromeRuntime doesn't work with LoadURL #4832

Closed
1 task done
GrabzIt opened this issue Jun 11, 2024 · 5 comments
Closed
1 task done

OffScreen - ChromeRuntime doesn't work with LoadURL #4832

GrabzIt opened this issue Jun 11, 2024 · 5 comments

Comments

@GrabzIt
Copy link
Contributor

GrabzIt commented Jun 11, 2024

Is there an existing issue for this?

  • I have searched both open/closed issues, no issue already exists.

CefSharp Version

125.0.210

Operating System

Windows 10

Architecture

x64

.Net Version

.NET 6.0

Implementation

OffScreen

Reproduction Steps

        public static int Main(string[] args)
        {
            const string testUrl = "https://grabz.it/";

            Console.WriteLine("This example application will load {0}, take a screenshot, and save it to your desktop.", testUrl);
            Console.WriteLine("You may see Chromium debugging output, please wait...");
            Console.WriteLine();

            //Console apps don't have a SynchronizationContext, so to ensure our await calls continue on the main thread we use a super simple implementation from
            //https://devblogs.microsoft.com/pfxteam/await-synchronizationcontext-and-console-apps/
            //Continuations will happen on the main thread. Cef.Initialize/Cef.Shutdown must be called on the same Thread.
            //The Nito.AsyncEx.Context Nuget package has a more advanced implementation
            //should you wish to use a pre-build implementation.
            //https://github.com/StephenCleary/AsyncEx/blob/8a73d0467d40ca41f9f9cf827c7a35702243abb8/doc/AsyncContext.md#console-example-using-asynccontext
            //NOTE: This is only required if you use await

                var settings = new CefSettings()
                {
                    ChromeRuntime = true,
                    //By default CefSharp will use an in-memory cache, you need to specify a Cache Folder to persist data
                    CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache")
                };

                //Perform dependency check to make sure all relevant resources are in our output directory.
                var success = Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);

                if (!success)
                {
                    throw new Exception("Unable to initialize CEF, check the log file.");
                }

                // Create the CefSharp.OffScreen.ChromiumWebBrowser instance
                using (var browser = new ChromiumWebBrowser(string.Empty))
                {
                    browser.FrameLoadEnd += Browser_FrameLoadEnd;
                    browser.LoadUrl(testUrl);
                    // Wait for user to press a key before exit
                    Thread.Sleep(20000);
                }

                // Clean up Chromium objects. You need to call this in your application otherwise
                // you will get a crash when closing.
                Cef.Shutdown();

            return 0;
        }

        private async static void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e)
        {
            if (e.Frame.IsValid && e.Frame.IsMain)
            {
                var bitmapAsByteArray = await ((ChromiumWebBrowser)sender).CaptureScreenshotAsync();

                // File path to save our screenshot e.g. C:\Users\{username}\Desktop\CefSharp screenshot.png
                var screenshotPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "CefSharp screenshot.png");

                Console.WriteLine();
                Console.WriteLine("Screenshot ready. Saving to {0}", screenshotPath);

                File.WriteAllBytes(screenshotPath, bitmapAsByteArray);

                Console.WriteLine("Screenshot saved. Launching your default image viewer...");

                // Tell Windows to launch the saved image.
                Process.Start(new ProcessStartInfo(screenshotPath)
                {
                    // UseShellExecute is false by default on .NET Core.
                    UseShellExecute = true
                });
                Console.WriteLine("Image viewer launched. Press any key to exit.");
            }
        }

Expected behavior

This should load the url and display a screenshot but when ChromeRuntime = true it never works. However, it does if you remove ChromeRuntime = true.

Actual behavior

It never fires the browser loaded event.

Regression?

No response

Known Workarounds

No response

Does this problem also occur in the CEF Sample Application

Yes using WPF/OffScreen command line args

Other information

No response

@amaitland
Copy link
Member

However, it does if you remove ChromeRuntime = true.

Given it works with the alloy runtime this likely needs to be fixed in CEF.

@amaitland
Copy link
Member

@GrabzIt Looking at the code in more detail, can you defer creation of the browser until after you've subscribed to the event? e.g.

// Create the CefSharp.OffScreen.ChromiumWebBrowser instance
using (var browser = new ChromiumWebBrowser(testUrl, automaticallyCreateBrowser:false))
{
  browser.FrameLoadEnd += Browser_FrameLoadEnd;
  browser.CreateBrowser();
  // Wait 
  Thread.Sleep(20000);
}

@GrabzIt
Copy link
Contributor Author

GrabzIt commented Jun 12, 2024

Thanks, this approach works and seems to work with further calls to LoadURL and adding and removing the event: e.g.

              using (var browser = new ChromiumWebBrowser(testUrl, automaticallyCreateBrowser: false))
              {
                    browser.FrameLoadEnd += Browser_FrameLoadEnd;
                    browser.CreateBrowser();
                    Thread.Sleep(20000);
                    browser.FrameLoadEnd += Browser_FrameLoadEnd;
                    browser.LoadUrl("https://grabz.it/about/");
                    Thread.Sleep(20000);
              }

        private async static void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e)
        {
            if (e.Frame.IsValid && e.Frame.IsMain)
            {
                Console.WriteLine(e.Url);
                ((ChromiumWebBrowser)sender).FrameLoadEnd -= Browser_FrameLoadEnd;
            }
        }

Is this the way it should be used from now or are the CEF changes to the chrome runtime going to fix the issue?

@amaitland
Copy link
Member

Is this the way it should be used from now

That would be my recommendation. Pretty sure I modified most of the examples/tests a while back to use this approach. If there are any outstanding then happy to update them.

are the CEF changes to the chrome runtime going to fix the issue?

From a CefSharp point of view it probably makes sense to defer browser creation automatically if you pass in string.Empty, create the browser on the next load call.

@GrabzIt
Copy link
Contributor Author

GrabzIt commented Jun 16, 2024

That sounds great thanks.

@amaitland amaitland changed the title ChromeRuntime doesn't work with LoadURL OffScreen - ChromeRuntime doesn't work with LoadURL Jun 21, 2024
@amaitland amaitland added this to the 126.2.x milestone Jun 21, 2024
@amaitland amaitland self-assigned this Jun 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants