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,106 @@ 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+ var response = await browser . EvaluateScriptAsync ( "document.querySelector('[name=q]').value = 'CefSharp Was Here!'" ) ;
75+
76+ //Give the browser a little time to render
77+ await Task . Delay ( 500 ) ;
78+ // Wait for the screenshot to be taken.
79+ var bitmap = await browser . ScreenshotAsync ( ) ;
80+
81+ // File path to save our screenshot e.g. C:\Users\{username}\Desktop\CefSharp screenshot.png
82+ var screenshotPath = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . Desktop ) , "CefSharp screenshot.png" ) ;
83+
84+ Console . WriteLine ( ) ;
85+ Console . WriteLine ( "Screenshot ready. Saving to {0}" , screenshotPath ) ;
86+
87+ // Save the Bitmap to the path.
88+ // The image type is auto-detected via the ".png" extension.
89+ bitmap . Save ( screenshotPath ) ;
90+
91+ // We no longer need the Bitmap.
92+ // Dispose it to avoid keeping the memory alive. Especially important in 32-bit applications.
93+ bitmap . Dispose ( ) ;
94+
95+ Console . WriteLine ( "Screenshot saved. Launching your default image viewer..." ) ;
96+
97+ // Tell Windows to launch the saved image.
98+ Process . Start ( new ProcessStartInfo ( screenshotPath )
99+ {
100+ // UseShellExecute is false by default on .NET Core.
101+ UseShellExecute = true
102+ } ) ;
103+
104+ Console . WriteLine ( "Image viewer launched. Press any key to exit." ) ;
105+ }
106+
107+ // Wait for user to press a key before exit
108+ Console . ReadKey ( ) ;
109+
110+ // Clean up Chromium objects. You need to call this in your application otherwise
111+ // you will get a crash when closing.
112+ Cef . Shutdown ( ) ;
113+ } ) ;
114+
115+ return 0 ;
116+ }
117+
118+ /// <summary>
119+ /// Synchronous demo using CefSharp.OffScreen
120+ /// Loads google.com, uses javascript to fill out the search box then takes a screenshot which is opened
121+ /// in the default image viewer.
122+ /// For a asynchronous demo see <see cref="Main(string[])"/> above.
123+ /// To use this demo simply delete the <see cref="Main(string[])"/> method and rename this method to Main.
124+ /// </summary>
125+ /// <param name="args">args</param>
126+ /// <returns>exit code</returns>
127+ public static int MainSync ( string [ ] args )
128+ {
129+ #if ANYCPU
130+ //Only required for PlatformTarget of AnyCPU
131+ CefRuntime . SubscribeAnyCpuAssemblyResolver ( ) ;
132+ #endif
133+
134+ const string testUrl = "https://www.google.com/" ;
135+
136+ Console . WriteLine ( "This example application will load {0}, take a screenshot, and save it to your desktop." , testUrl ) ;
137+ Console . WriteLine ( "You may see Chromium debugging output, please wait..." ) ;
138+ Console . WriteLine ( ) ;
139+
31140 var settings = new CefSettings ( )
32141 {
33142 //By default CefSharp will use an in-memory cache, you need to specify a Cache Folder to persist data
@@ -38,69 +147,76 @@ public static int Main(string[] args)
38147 Cef . Initialize ( settings , performDependencyCheck : true , browserProcessHandler : null ) ;
39148
40149 // Create the offscreen Chromium browser.
41- browser = new ChromiumWebBrowser ( testUrl ) ;
150+ var browser = new ChromiumWebBrowser ( testUrl ) ;
42151
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 ;
152+ EventHandler < LoadingStateChangedEventArgs > handler = null ;
46153
47- // We have to wait for something, otherwise the process will exit too soon.
48- Console . ReadKey ( ) ;
154+ handler = ( s , e ) =>
155+ {
156+ // Check to see if loading is complete - this event is called twice, one when loading starts
157+ // second time when it's finished
158+ if ( ! e . IsLoading )
159+ {
160+ // Remove the load event handler, because we only want one snapshot of the page.
161+ browser . LoadingStateChanged -= handler ;
49162
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 ( ) ;
163+ var scriptTask = browser . EvaluateScriptAsync ( "document.querySelector('[name=q]').value = 'CefSharp Was Here!'" ) ;
53164
54- return 0 ;
55- }
165+ scriptTask . ContinueWith ( t =>
166+ {
167+ if ( ! t . Result . Success )
168+ {
169+ throw new Exception ( "EvaluateScriptAsync failed:" + t . Result . Message ) ;
170+ }
171+
172+ //Give the browser a little time to render
173+ Thread . Sleep ( 500 ) ;
174+ // Wait for the screenshot to be taken.
175+ var task = browser . ScreenshotAsync ( ) ;
176+ task . ContinueWith ( x =>
177+ {
178+ // File path to save our screenshot e.g. C:\Users\{username}\Desktop\CefSharp screenshot.png
179+ var screenshotPath = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . Desktop ) , "CefSharp screenshot.png" ) ;
56180
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 ;
181+ Console . WriteLine ( ) ;
182+ Console . WriteLine ( "Screenshot ready. Saving to {0}" , screenshotPath ) ;
66183
67- var scriptTask = browser . EvaluateScriptAsync ( "document.querySelector('[name=q]').value = 'CefSharp Was Here!'" ) ;
184+ // Save the Bitmap to the path.
185+ // The image type is auto-detected via the ".png" extension.
186+ task . Result . Save ( screenshotPath ) ;
68187
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" ) ;
188+ // We no longer need the Bitmap.
189+ // Dispose it to avoid keeping the memory alive. Especially important in 32-bit applications.
190+ task . Result . Dispose ( ) ;
79191
80- Console . WriteLine ( ) ;
81- Console . WriteLine ( "Screenshot ready. Saving to {0}" , screenshotPath ) ;
192+ Console . WriteLine ( "Screenshot saved. Launching your default image viewer..." ) ;
82193
83- // Save the Bitmap to the path.
84- // The image type is auto-detected via the ".png" extension.
85- task . Result . Save ( screenshotPath ) ;
194+ // Tell Windows to launch the saved image.
195+ Process . Start ( new ProcessStartInfo ( screenshotPath )
196+ {
197+ // UseShellExecute is false by default on .NET Core.
198+ UseShellExecute = true
199+ } ) ;
86200
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 ( ) ;
201+ Console . WriteLine ( "Image viewer launched. Press any key to exit." ) ;
202+ } , TaskScheduler . Default ) ;
203+ } ) ;
204+ }
205+ } ;
90206
91- Console . WriteLine ( "Screenshot saved. Launching your default image viewer..." ) ;
207+ // An event that is fired when the first page is finished loading.
208+ // This returns to us from another thread.
209+ browser . LoadingStateChanged += handler ;
92210
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- }
211+ // We have to wait for something, otherwise the process will exit too soon.
212+ Console . ReadKey ( ) ;
213+
214+ // Clean up Chromium objects. You need to call this in your application otherwise
215+ // you will get a crash when closing.
216+ //The ChromiumWebBrowser instance will be disposed
217+ Cef . Shutdown ( ) ;
218+
219+ return 0 ;
104220 }
105221 }
106222}
0 commit comments