1- // Copyright © 2010-2015 The CefSharp Authors. All rights reserved.
1+ // Copyright © 2010-2021 The CefSharp Authors. All rights reserved.
22//
33// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
44
1111
1212namespace CefSharp . MinimalExample . OffScreen
1313{
14+ /// <summary>
15+ /// CefSharp.OffScreen Minimal Example
16+ /// </summary>
1417 public class Program
1518 {
16- private static ChromiumWebBrowser browser ;
17-
19+ /// <summary>
20+ /// Asynchronous demo using CefSharp.OffScreen
21+ /// Loads google.com, uses javascript to fill out the search box then takes a screenshot which is opened
22+ /// in the default image viewer.
23+ /// For a synchronous demo see <see cref="MainSync(string[])"/> below.
24+ /// </summary>
25+ /// <param name="args">args</param>
26+ /// <returns>exit code</returns>
1827 public static int Main ( string [ ] args )
1928 {
2029#if ANYCPU
@@ -28,6 +37,108 @@ public static int Main(string[] args)
2837 Console . WriteLine ( "You may see Chromium debugging output, please wait..." ) ;
2938 Console . WriteLine ( ) ;
3039
40+ //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
41+ //https://devblogs.microsoft.com/pfxteam/await-synchronizationcontext-and-console-apps/
42+ //Continuations will happen on the main thread. Cef.Initialize/Cef.Shutdown must be called on the same Thread.
43+ //The Nito.AsyncEx.Context Nuget package has a more advanced implementation
44+ //should you wish to use a pre-build implementation.
45+ //https://github.com/StephenCleary/AsyncEx/blob/8a73d0467d40ca41f9f9cf827c7a35702243abb8/doc/AsyncContext.md#console-example-using-asynccontext
46+ //NOTE: This is only required if you use await
47+
48+ AsyncContext . Run ( async delegate
49+ {
50+ var settings = new CefSettings ( )
51+ {
52+ //By default CefSharp will use an in-memory cache, you need to specify a Cache Folder to persist data
53+ CachePath = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) , "CefSharp\\ Cache" )
54+ } ;
55+
56+ //Perform dependency check to make sure all relevant resources are in our output directory.
57+ var success = await Cef . InitializeAsync ( settings , performDependencyCheck : true , browserProcessHandler : null ) ;
58+
59+ if ( ! success )
60+ {
61+ throw new Exception ( "Unable to initialize CEF, check the log file." ) ;
62+ }
63+
64+ // Create the CefSharp.OffScreen.ChromiumWebBrowser instance
65+ using ( var browser = new ChromiumWebBrowser ( testUrl ) )
66+ {
67+ var initialLoadResponse = await browser . WaitForInitialLoadAsync ( ) ;
68+
69+ if ( ! initialLoadResponse . Success )
70+ {
71+ throw new Exception ( string . Format ( "Page load failed with ErrorCode:{0}, HttpStatusCode:{1}" , initialLoadResponse . ErrorCode , initialLoadResponse . HttpStatusCode ) ) ;
72+ }
73+
74+ browser . ShowDevTools ( ) ;
75+
76+ var response = await browser . EvaluateScriptAsync ( "document.querySelector('[name=q]').value = 'CefSharp Was Here!'" ) ;
77+
78+ //Give the browser a little time to render
79+ await Task . Delay ( 500 ) ;
80+ // Wait for the screenshot to be taken.
81+ var bitmap = await browser . ScreenshotAsync ( ) ;
82+
83+ // File path to save our screenshot e.g. C:\Users\{username}\Desktop\CefSharp screenshot.png
84+ var screenshotPath = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . Desktop ) , "CefSharp screenshot.png" ) ;
85+
86+ Console . WriteLine ( ) ;
87+ Console . WriteLine ( "Screenshot ready. Saving to {0}" , screenshotPath ) ;
88+
89+ // Save the Bitmap to the path.
90+ // The image type is auto-detected via the ".png" extension.
91+ bitmap . Save ( screenshotPath ) ;
92+
93+ // We no longer need the Bitmap.
94+ // Dispose it to avoid keeping the memory alive. Especially important in 32-bit applications.
95+ bitmap . Dispose ( ) ;
96+
97+ Console . WriteLine ( "Screenshot saved. Launching your default image viewer..." ) ;
98+
99+ // Tell Windows to launch the saved image.
100+ Process . Start ( new ProcessStartInfo ( screenshotPath )
101+ {
102+ // UseShellExecute is false by default on .NET Core.
103+ UseShellExecute = true
104+ } ) ;
105+
106+ Console . WriteLine ( "Image viewer launched. Press any key to exit." ) ;
107+ }
108+
109+ // Wait for user to press a key before exit
110+ Console . ReadKey ( ) ;
111+
112+ // Clean up Chromium objects. You need to call this in your application otherwise
113+ // you will get a crash when closing.
114+ Cef . Shutdown ( ) ;
115+ } ) ;
116+
117+ return 0 ;
118+ }
119+
120+ /// <summary>
121+ /// Synchronous demo using CefSharp.OffScreen
122+ /// Loads google.com, uses javascript to fill out the search box then takes a screenshot which is opened
123+ /// in the default image viewer.
124+ /// For a asynchronous demo see <see cref="Main(string[])"/> above.
125+ /// To use this demo simply delete the <see cref="Main(string[])"/> method and rename this method to Main.
126+ /// </summary>
127+ /// <param name="args">args</param>
128+ /// <returns>exit code</returns>
129+ public static int MainSync ( string [ ] args )
130+ {
131+ #if ANYCPU
132+ //Only required for PlatformTarget of AnyCPU
133+ CefRuntime . SubscribeAnyCpuAssemblyResolver ( ) ;
134+ #endif
135+
136+ const string testUrl = "https://www.google.com/" ;
137+
138+ Console . WriteLine ( "This example application will load {0}, take a screenshot, and save it to your desktop." , testUrl ) ;
139+ Console . WriteLine ( "You may see Chromium debugging output, please wait..." ) ;
140+ Console . WriteLine ( ) ;
141+
31142 var settings = new CefSettings ( )
32143 {
33144 //By default CefSharp will use an in-memory cache, you need to specify a Cache Folder to persist data
@@ -38,69 +149,76 @@ public static int Main(string[] args)
38149 Cef . Initialize ( settings , performDependencyCheck : true , browserProcessHandler : null ) ;
39150
40151 // Create the offscreen Chromium browser.
41- browser = new ChromiumWebBrowser ( testUrl ) ;
152+ var browser = new ChromiumWebBrowser ( testUrl ) ;
42153
43- // An event that is fired when the first page is finished loading.
44- // This returns to us from another thread.
45- browser . LoadingStateChanged += BrowserLoadingStateChanged ;
154+ EventHandler < LoadingStateChangedEventArgs > handler = null ;
46155
47- // We have to wait for something, otherwise the process will exit too soon.
48- Console . ReadKey ( ) ;
156+ handler = ( s , e ) =>
157+ {
158+ // Check to see if loading is complete - this event is called twice, one when loading starts
159+ // second time when it's finished
160+ if ( ! e . IsLoading )
161+ {
162+ // Remove the load event handler, because we only want one snapshot of the page.
163+ browser . LoadingStateChanged -= handler ;
49164
50- // Clean up Chromium objects. You need to call this in your application otherwise
51- // you will get a crash when closing.
52- Cef . Shutdown ( ) ;
165+ var scriptTask = browser . EvaluateScriptAsync ( "document.querySelector('[name=q]').value = 'CefSharp Was Here!'" ) ;
53166
54- return 0 ;
55- }
167+ scriptTask . ContinueWith ( t =>
168+ {
169+ if ( ! t . Result . Success )
170+ {
171+ throw new Exception ( "EvaluateScriptAsync failed:" + t . Result . Message ) ;
172+ }
173+
174+ //Give the browser a little time to render
175+ Thread . Sleep ( 500 ) ;
176+ // Wait for the screenshot to be taken.
177+ var task = browser . ScreenshotAsync ( ) ;
178+ task . ContinueWith ( x =>
179+ {
180+ // File path to save our screenshot e.g. C:\Users\{username}\Desktop\CefSharp screenshot.png
181+ var screenshotPath = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . Desktop ) , "CefSharp screenshot.png" ) ;
56182
57- private static void BrowserLoadingStateChanged ( object sender , LoadingStateChangedEventArgs e )
58- {
59- // Check to see if loading is complete - this event is called twice, one when loading starts
60- // second time when it's finished
61- // (rather than an iframe within the main frame).
62- if ( ! e . IsLoading )
63- {
64- // Remove the load event handler, because we only want one snapshot of the initial page.
65- browser . LoadingStateChanged -= BrowserLoadingStateChanged ;
183+ Console . WriteLine ( ) ;
184+ Console . WriteLine ( "Screenshot ready. Saving to {0}" , screenshotPath ) ;
66185
67- var scriptTask = browser . EvaluateScriptAsync ( "document.querySelector('[name=q]').value = 'CefSharp Was Here!'" ) ;
186+ // Save the Bitmap to the path.
187+ // The image type is auto-detected via the ".png" extension.
188+ task . Result . Save ( screenshotPath ) ;
68189
69- scriptTask . ContinueWith ( t =>
70- {
71- //Give the browser a little time to render
72- Thread . Sleep ( 500 ) ;
73- // Wait for the screenshot to be taken.
74- var task = browser . ScreenshotAsync ( ) ;
75- task . ContinueWith ( x =>
76- {
77- // Make a file to save it to (e.g. C:\Users\jan\Desktop\CefSharp screenshot.png)
78- var screenshotPath = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . Desktop ) , "CefSharp screenshot.png" ) ;
190+ // We no longer need the Bitmap.
191+ // Dispose it to avoid keeping the memory alive. Especially important in 32-bit applications.
192+ task . Result . Dispose ( ) ;
193+
194+ Console . WriteLine ( "Screenshot saved. Launching your default image viewer..." ) ;
195+
196+ // Tell Windows to launch the saved image.
197+ Process . Start ( new ProcessStartInfo ( screenshotPath )
198+ {
199+ // UseShellExecute is false by default on .NET Core.
200+ UseShellExecute = true
201+ } ) ;
79202
80- Console . WriteLine ( ) ;
81- Console . WriteLine ( "Screenshot ready. Saving to {0}" , screenshotPath ) ;
203+ Console . WriteLine ( "Image viewer launched. Press any key to exit." ) ;
204+ } , TaskScheduler . Default ) ;
205+ } ) ;
206+ }
207+ } ;
82208
83- // Save the Bitmap to the path .
84- // The image type is auto-detected via the ".png" extension .
85- task . Result . Save ( screenshotPath ) ;
209+ // An event that is fired when the first page is finished loading .
210+ // This returns to us from another thread .
211+ browser . LoadingStateChanged += handler ;
86212
87- // We no longer need the Bitmap.
88- // Dispose it to avoid keeping the memory alive. Especially important in 32-bit applications.
89- task . Result . Dispose ( ) ;
213+ // We have to wait for something, otherwise the process will exit too soon.
214+ Console . ReadKey ( ) ;
90215
91- Console . WriteLine ( "Screenshot saved. Launching your default image viewer..." ) ;
216+ // Clean up Chromium objects. You need to call this in your application otherwise
217+ // you will get a crash when closing.
218+ //The ChromiumWebBrowser instance will be disposed
219+ Cef . Shutdown ( ) ;
92220
93- // Tell Windows to launch the saved image.
94- Process . Start ( new ProcessStartInfo ( screenshotPath )
95- {
96- // UseShellExecute is false by default on .NET Core.
97- UseShellExecute = true
98- } ) ;
99-
100- Console . WriteLine ( "Image viewer launched. Press any key to exit." ) ;
101- } , TaskScheduler . Default ) ;
102- } ) ;
103- }
221+ return 0 ;
104222 }
105223 }
106224}
0 commit comments