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 bd3cc35392..aa36e21395 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 e3f2b07e73..69ee661fad 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(); + } + } +}