Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions CefSharp.BrowserSubprocess.Core/BrowserSubprocessExecutable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// 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
{
/// <summary>
/// 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).
/// </summary>
public ref class BrowserSubprocessExecutable
{
public:
BrowserSubprocessExecutable()
{

}

/// <summary>
/// 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.
/// </summary>
/// <param name="args">command line args</param>
/// <returns>
/// 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.
/// </returns
int Main(IEnumerable<String^>^ args)
{
return Main(args, nullptr);
}

/// <summary>
/// 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.
/// </summary>
/// <param name="args">command line args</param>
/// <param name="handler">An option IRenderProcessHandler implementation, use null if no handler is required</param>
/// <returns>
/// 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.
/// </returns>
int Main(IEnumerable<String^>^ args, IRenderProcessHandler^ handler)
{
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
{
return subProcess->Run();
}
finally
{
delete subProcess;
}
}

return SubProcess::ExecuteProcess(args);
}

protected:
virtual SubProcess^ GetSubprocess(IEnumerable<String^>^ args, int parentProcessId, IRenderProcessHandler^ handler)
{
return gcnew SubProcess(handler, args);
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,15 @@
<ClInclude Include="..\CefSharp.Core\Internals\Serialization\Primitives.h" />
<ClInclude Include="..\CefSharp.Core\Internals\StringUtils.h" />
<ClInclude Include="BindObjectAsyncHandler.h" />
<ClCompile Include="BrowserSubprocessExecutable.h" />
<ClInclude Include="SubProcessApp.h" />
<ClInclude Include="Async\JavascriptAsyncMethodCallback.h" />
<ClInclude Include="Async\JavascriptAsyncMethodHandler.h" />
<ClInclude Include="Async\JavascriptAsyncMethodWrapper.h" />
<ClInclude Include="Async\JavascriptAsyncObjectWrapper.h" />
<ClInclude Include="CefAppUnmanagedWrapper.h" />
<ClInclude Include="JavascriptPostMessageHandler.h" />
<ClCompile Include="WcfBrowserSubprocessExecutable.h" />
<ClInclude Include="Wrapper\Frame.h" />
<ClInclude Include="Wrapper\Browser.h" />
<ClInclude Include="Wrapper\V8Context.h" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@
<ClCompile Include="Wrapper\Browser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="WcfBrowserSubprocessExecutable.h">
<Filter>Header Files</Filter>
</ClCompile>
<ClCompile Include="BrowserSubprocessExecutable.h">
<Filter>Header Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Resource.rc" />
Expand Down
46 changes: 46 additions & 0 deletions CefSharp.BrowserSubprocess.Core/WcfBrowserSubprocessExecutable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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
{
/// <summary>
/// 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 <see cref="CefSharpArguments.WcfEnabledArgument"/> command line argument is
/// present then the WcfEnabledSubProcess implementation is used.
/// </summary>
public ref class WcfBrowserSubprocessExecutable : BrowserSubprocessExecutable
{
public:
WcfBrowserSubprocessExecutable()
{

}
protected:
SubProcess^ GetSubprocess(IEnumerable<String^>^ 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);
}
};
}
}
67 changes: 11 additions & 56 deletions CefSharp.BrowserSubprocess/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,84 +2,39 @@
//
// 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;
using CefSharp.Internals;
using CefSharp.RenderProcess;

namespace CefSharp.BrowserSubprocess
{
/// <summary>
/// When implementing your own BrowserSubprocess
/// - For Full .Net use <see cref="WcfBrowserSubprocessExecutable"/>
/// - For .Net Core use <see cref="BrowserSubprocessExecutable"/> (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)
/// </summary>
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();

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);
}
//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);

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();
}
}
}
1 change: 1 addition & 0 deletions CefSharp/CefSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
<Compile Include="DefaultApp.cs" />
<Compile Include="Enums\SchemeOptions.cs" />
<Compile Include="Internals\CommandLineArgDictionary.cs" />
<Compile Include="Internals\ParentProcessMonitor.cs" />
<Compile Include="Internals\ReadOnlyNameValueCollection.cs" />
<Compile Include="Internals\TaskScheduler\LimitedConcurrencyLevelTaskScheduler.cs" />
<Compile Include="IUrlRequest.cs" />
Expand Down
49 changes: 49 additions & 0 deletions CefSharp/Internals/ParentProcessMonitor.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// 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.
/// </summary>
public static class ParentProcessMonitor
{
/// <summary>
/// Starts a long running task (spawns new thread) used to monitor the parent process
/// and calls <see cref="Process.Kill"/> if the parent exits unexpectedly (usually result of a crash).
/// </summary>
/// <param name="parentProcessId">process Id of the parent application</param>
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();
}
}
}