From 2e70c016d74280901f5ae7bf18c995c549334010 Mon Sep 17 00:00:00 2001 From: amaitland Date: Fri, 6 Sep 2019 15:24:14 +1000 Subject: [PATCH 1/2] BrowserSubprocess - Refactor to support .Net Core - Added BrowserSubprocessExecutable for use with .Net Core (no WCF) - Added WcfBrowserSubprocessExecutable for use with the existing CefSharp.BrowserSubprocess.exe which supports WCF (conditionally) --- .../BrowserSubprocessExecutable.h | 79 +++++++++++++++++++ .../CefSharp.BrowserSubprocess.Core.vcxproj | 2 + ...arp.BrowserSubprocess.Core.vcxproj.filters | 6 ++ .../WcfBrowserSubprocessExecutable.h | 39 +++++++++ CefSharp.BrowserSubprocess/Program.cs | 58 +------------- CefSharp/CefSharp.csproj | 1 + CefSharp/Internals/ParentProcessMonitor.cs | 49 ++++++++++++ 7 files changed, 180 insertions(+), 54 deletions(-) create mode 100644 CefSharp.BrowserSubprocess.Core/BrowserSubprocessExecutable.h create mode 100644 CefSharp.BrowserSubprocess.Core/WcfBrowserSubprocessExecutable.h create mode 100644 CefSharp/Internals/ParentProcessMonitor.cs diff --git a/CefSharp.BrowserSubprocess.Core/BrowserSubprocessExecutable.h b/CefSharp.BrowserSubprocess.Core/BrowserSubprocessExecutable.h new file mode 100644 index 0000000000..aa98156724 --- /dev/null +++ b/CefSharp.BrowserSubprocess.Core/BrowserSubprocessExecutable.h @@ -0,0 +1,79 @@ +// Copyright © 2019 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +#pragma once + +#include "Stdafx.h" + +#include "SubProcess.h" +#include "WcfEnabledSubProcess.h" + +using namespace System; +using namespace CefSharp::Internals; + +namespace CefSharp +{ + namespace BrowserSubprocess + { + /// + /// BrowserSubprocessExecutable provides the fundimental browser process handling for + /// CefSharp.BrowserSubprocess.exe and can be used to self host the BrowserSubProcess in your + /// existing application (preferred approach for .Net Core). + /// + public ref class BrowserSubprocessExecutable + { + public: + BrowserSubprocessExecutable() + { + + } + + int Main(IEnumerable^ args, IRenderProcessHandler^ handler) + { + int result; + auto type = CommandLineArgsParser::GetArgumentValue(args, CefSharpArguments::SubProcessTypeArgument); + + auto parentProcessId = -1; + + // The Crashpad Handler doesn't have any HostProcessIdArgument, so we must not try to + // parse it lest we want an ArgumentNullException. + if (type != "crashpad-handler") + { + parentProcessId = int::Parse(CommandLineArgsParser::GetArgumentValue(args, CefSharpArguments::HostProcessIdArgument)); + if (CommandLineArgsParser::HasArgument(args, CefSharpArguments::ExitIfParentProcessClosed)) + { + ParentProcessMonitor::StartMonitorTask(parentProcessId); + } + } + + // Use our custom subProcess provides features like EvaluateJavascript + if (type == "renderer") + { + auto subProcess = GetSubprocess(args, parentProcessId, handler); + + try + { + result = subProcess->Run(); + } + finally + { + delete subProcess; + } + } + else + { + result = SubProcess::ExecuteProcess(args); + } + + return result; + } + + protected: + virtual SubProcess^ GetSubprocess(IEnumerable^ args, int parentProcessId, IRenderProcessHandler^ handler) + { + return gcnew SubProcess(handler, args); + } + }; + } +} diff --git a/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj b/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj index 71eec8ab14..63585f5d91 100644 --- a/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj +++ b/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj @@ -173,6 +173,7 @@ + @@ -180,6 +181,7 @@ + diff --git a/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj.filters b/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj.filters index 4c402a4914..d8aee10e1e 100644 --- a/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj.filters +++ b/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj.filters @@ -181,6 +181,12 @@ Source Files + + Header Files + + + Header Files + diff --git a/CefSharp.BrowserSubprocess.Core/WcfBrowserSubprocessExecutable.h b/CefSharp.BrowserSubprocess.Core/WcfBrowserSubprocessExecutable.h new file mode 100644 index 0000000000..4fcaf246fb --- /dev/null +++ b/CefSharp.BrowserSubprocess.Core/WcfBrowserSubprocessExecutable.h @@ -0,0 +1,39 @@ +// Copyright © 2019 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +#pragma once + +#include "Stdafx.h" + +#include "SubProcess.h" +#include "WcfEnabledSubProcess.h" +#include "BrowserSubprocessExecutable.h" + +using namespace System; +using namespace CefSharp::Internals; + +namespace CefSharp +{ + namespace BrowserSubprocess + { + public ref class WcfBrowserSubprocessExecutable : BrowserSubprocessExecutable + { + public: + WcfBrowserSubprocessExecutable() + { + + } + protected: + SubProcess^ GetSubprocess(IEnumerable^ args, int parentProcessId, IRenderProcessHandler^ handler) override + { + auto wcfEnabled = CommandLineArgsParser::HasArgument(args, CefSharpArguments::WcfEnabledArgument); + if (wcfEnabled) + { + return gcnew WcfEnabledSubProcess(parentProcessId, handler, args); + } + return gcnew SubProcess(handler, args); + } + }; + } +} diff --git a/CefSharp.BrowserSubprocess/Program.cs b/CefSharp.BrowserSubprocess/Program.cs index a8975d3448..30351fbbca 100644 --- a/CefSharp.BrowserSubprocess/Program.cs +++ b/CefSharp.BrowserSubprocess/Program.cs @@ -4,8 +4,6 @@ using System; using System.Diagnostics; -using System.Threading.Tasks; -using CefSharp.Internals; using CefSharp.RenderProcess; namespace CefSharp.BrowserSubprocess @@ -23,63 +21,15 @@ public static int Main(string[] args) SubProcess.EnableHighDPISupport(); - int result; - var type = args.GetArgumentValue(CefSharpArguments.SubProcessTypeArgument); + //Add your own custom implementation of IRenderProcessHandler here + IRenderProcessHandler handler = null; - var parentProcessId = -1; - - // The Crashpad Handler doesn't have any HostProcessIdArgument, so we must not try to - // parse it lest we want an ArgumentNullException. - if (type != "crashpad-handler") - { - parentProcessId = int.Parse(args.GetArgumentValue(CefSharpArguments.HostProcessIdArgument)); - if (args.HasArgument(CefSharpArguments.ExitIfParentProcessClosed)) - { - Task.Factory.StartNew(() => AwaitParentProcessExit(parentProcessId), TaskCreationOptions.LongRunning); - } - } - - // Use our custom subProcess provides features like EvaluateJavascript - if (type == "renderer") - { - //Add your own custom implementation of IRenderProcessHandler here - IRenderProcessHandler handler = null; - var wcfEnabled = args.HasArgument(CefSharpArguments.WcfEnabledArgument); - var subProcess = wcfEnabled ? new WcfEnabledSubProcess(parentProcessId, handler, args) : new SubProcess(handler, args); - - using (subProcess) - { - result = subProcess.Run(); - } - } - else - { - result = SubProcess.ExecuteProcess(args); - } + var browserProcessExe = new WcfBrowserSubprocessExecutable(); + var result = browserProcessExe.Main(args, handler); Debug.WriteLine("BrowserSubprocess shutting down."); return result; } - - private static async void AwaitParentProcessExit(int parentProcessId) - { - try - { - var parentProcess = Process.GetProcessById(parentProcessId); - parentProcess.WaitForExit(); - } - catch (Exception e) - { - //main process probably died already - Debug.WriteLine(e); - } - - await Task.Delay(1000); //wait a bit before exiting - - Debug.WriteLine("BrowserSubprocess shutting down forcibly."); - - Process.GetCurrentProcess().Kill(); - } } } diff --git a/CefSharp/CefSharp.csproj b/CefSharp/CefSharp.csproj index 1cab744a5f..f4d9fc0cd5 100644 --- a/CefSharp/CefSharp.csproj +++ b/CefSharp/CefSharp.csproj @@ -103,6 +103,7 @@ + diff --git a/CefSharp/Internals/ParentProcessMonitor.cs b/CefSharp/Internals/ParentProcessMonitor.cs new file mode 100644 index 0000000000..61d5def837 --- /dev/null +++ b/CefSharp/Internals/ParentProcessMonitor.cs @@ -0,0 +1,49 @@ +// Copyright © 2019 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace CefSharp.Internals +{ + /// + /// Monitor the parent process and exit if the parent process closes + /// before the subprocess. This class is used by the CefSharp.BrowserSubprocess to + /// self terminate if the parent dies without notifying it to exit. + /// See https://github.com/cefsharp/CefSharp/issues/2359 for more information. + /// + public static class ParentProcessMonitor + { + /// + /// Starts a long running task (spawns new thread) used to monitor the parent process + /// and calls if the parent exits unexpectedly (usually result of a crash). + /// + /// process Id of the parent application + public static void StartMonitorTask(int parentProcessId) + { + Task.Factory.StartNew(() => AwaitParentProcessExit(parentProcessId), TaskCreationOptions.LongRunning); + } + + private static async void AwaitParentProcessExit(int parentProcessId) + { + try + { + var parentProcess = Process.GetProcessById(parentProcessId); + parentProcess.WaitForExit(); + } + catch (Exception e) + { + //main process probably died already + Debug.WriteLine(e); + } + + await Task.Delay(1000); //wait a bit before exiting + + Debug.WriteLine("BrowserSubprocess shutting down forcibly."); + + Process.GetCurrentProcess().Kill(); + } + } +} From a406dc23dcb1b5ea84b77052d993c65fd191a0cb Mon Sep 17 00:00:00 2001 From: amaitland Date: Wed, 2 Oct 2019 12:17:33 +1000 Subject: [PATCH 2/2] Add additional comments and minor code simplification --- .../BrowserSubprocessExecutable.h | 43 ++++++++++++++++--- .../WcfBrowserSubprocessExecutable.h | 7 +++ CefSharp.BrowserSubprocess/Program.cs | 9 +++- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/CefSharp.BrowserSubprocess.Core/BrowserSubprocessExecutable.h b/CefSharp.BrowserSubprocess.Core/BrowserSubprocessExecutable.h index aa98156724..2eb318e515 100644 --- a/CefSharp.BrowserSubprocess.Core/BrowserSubprocessExecutable.h +++ b/CefSharp.BrowserSubprocess.Core/BrowserSubprocessExecutable.h @@ -29,9 +29,42 @@ namespace CefSharp } + /// + /// This function should be called from the application entry point function (typically Program.Main) + /// to execute a secondary process e.g. gpu, plugin, renderer, utility + /// It can be used to run secondary processes (BrowserSubProcess) from your main applications executable + /// or from a separate executable specified by the CefSettings.BrowserSubprocessPath value. + /// CefSharp defaults to using the latter approach, a default implementation (CefSharp.BrowserSubProcess.exe) is + /// supplied, see https://github.com/cefsharp/CefSharp/wiki/General-Usage#processes for more details. + /// + /// command line args + /// + /// If called for the browser process (identified by no "type" command-line value) it will return immediately + /// with a value of -1. If called for a recognized secondary process it will block until the process should exit + /// and then return the process exit code. + /// ^ args) + { + return Main(args, nullptr); + } + + /// + /// This function should be called from the application entry point function (typically Program.Main) + /// to execute a secondary process e.g. gpu, plugin, renderer, utility + /// It can be used to run secondary processes (BrowserSubProcess) from your main applications executable + /// or from a separate executable specified by the CefSettings.BrowserSubprocessPath value. + /// CefSharp defaults to using the latter approach, a default implementation (CefSharp.BrowserSubProcess.exe) is + /// supplied, see https://github.com/cefsharp/CefSharp/wiki/General-Usage#processes for more details. + /// + /// command line args + /// An option IRenderProcessHandler implementation, use null if no handler is required + /// + /// If called for the browser process (identified by no "type" command-line value) it will return immediately + /// with a value of -1. If called for a recognized secondary process it will block until the process should exit + /// and then return the process exit code. + /// int Main(IEnumerable^ args, IRenderProcessHandler^ handler) { - int result; auto type = CommandLineArgsParser::GetArgumentValue(args, CefSharpArguments::SubProcessTypeArgument); auto parentProcessId = -1; @@ -54,19 +87,15 @@ namespace CefSharp try { - result = subProcess->Run(); + return subProcess->Run(); } finally { delete subProcess; } } - else - { - result = SubProcess::ExecuteProcess(args); - } - return result; + return SubProcess::ExecuteProcess(args); } protected: diff --git a/CefSharp.BrowserSubprocess.Core/WcfBrowserSubprocessExecutable.h b/CefSharp.BrowserSubprocess.Core/WcfBrowserSubprocessExecutable.h index 4fcaf246fb..611064842f 100644 --- a/CefSharp.BrowserSubprocess.Core/WcfBrowserSubprocessExecutable.h +++ b/CefSharp.BrowserSubprocess.Core/WcfBrowserSubprocessExecutable.h @@ -17,6 +17,13 @@ namespace CefSharp { namespace BrowserSubprocess { + /// + /// WcfBrowserSubprocessExecutable provides the fundimental browser process handling for + /// CefSharp.BrowserSubprocess.exe and can be used to self host the BrowserSubProcess in your + /// existing application (preferred approach for .Net Core). + /// If the command line argument is + /// present then the WcfEnabledSubProcess implementation is used. + /// public ref class WcfBrowserSubprocessExecutable : BrowserSubprocessExecutable { public: diff --git a/CefSharp.BrowserSubprocess/Program.cs b/CefSharp.BrowserSubprocess/Program.cs index 30351fbbca..d0ae6a14ff 100644 --- a/CefSharp.BrowserSubprocess/Program.cs +++ b/CefSharp.BrowserSubprocess/Program.cs @@ -2,7 +2,6 @@ // // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. -using System; using System.Diagnostics; using CefSharp.RenderProcess; @@ -10,6 +9,8 @@ namespace CefSharp.BrowserSubprocess { /// /// When implementing your own BrowserSubprocess + /// - For Full .Net use + /// - For .Net Core use (No WCF Support) /// - Include an app.manifest with the dpi/compatability sections, this is required (this project contains the relevant). /// - If you are targeting x86/Win32 then you should set /LargeAddressAware (https://docs.microsoft.com/en-us/cpp/build/reference/largeaddressaware?view=vs-2017) /// @@ -17,13 +18,17 @@ public class Program { public static int Main(string[] args) { - Debug.WriteLine("BrowserSubprocess starting up with command line: " + String.Join("\n", args)); + Debug.WriteLine("BrowserSubprocess starting up with command line: " + string.Join("\n", args)); SubProcess.EnableHighDPISupport(); //Add your own custom implementation of IRenderProcessHandler here IRenderProcessHandler handler = null; + //The WcfBrowserSubprocessExecutable provides BrowserSubProcess functionality + //specific to CefSharp, WCF support (required for Sync JSB) will optionally be + //enabled if the CefSharpArguments.WcfEnabledArgument command line arg is present + //For .Net Core use BrowserSubprocessExecutable as there is no WCF support var browserProcessExe = new WcfBrowserSubprocessExecutable(); var result = browserProcessExe.Main(args, handler);