Skip to content

Commit cc23591

Browse files
authored
BrowserSubprocess - Refactor to support .Net Core (#2891)
* 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) * Add additional comments and minor code simplification
1 parent 21d272f commit cc23591

File tree

7 files changed

+223
-56
lines changed

7 files changed

+223
-56
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright © 2019 The CefSharp Authors. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4+
5+
#pragma once
6+
7+
#include "Stdafx.h"
8+
9+
#include "SubProcess.h"
10+
#include "WcfEnabledSubProcess.h"
11+
12+
using namespace System;
13+
using namespace CefSharp::Internals;
14+
15+
namespace CefSharp
16+
{
17+
namespace BrowserSubprocess
18+
{
19+
/// <summary>
20+
/// BrowserSubprocessExecutable provides the fundimental browser process handling for
21+
/// CefSharp.BrowserSubprocess.exe and can be used to self host the BrowserSubProcess in your
22+
/// existing application (preferred approach for .Net Core).
23+
/// </summary>
24+
public ref class BrowserSubprocessExecutable
25+
{
26+
public:
27+
BrowserSubprocessExecutable()
28+
{
29+
30+
}
31+
32+
/// <summary>
33+
/// This function should be called from the application entry point function (typically Program.Main)
34+
/// to execute a secondary process e.g. gpu, plugin, renderer, utility
35+
/// It can be used to run secondary processes (BrowserSubProcess) from your main applications executable
36+
/// or from a separate executable specified by the CefSettings.BrowserSubprocessPath value.
37+
/// CefSharp defaults to using the latter approach, a default implementation (CefSharp.BrowserSubProcess.exe) is
38+
/// supplied, see https://github.com/cefsharp/CefSharp/wiki/General-Usage#processes for more details.
39+
/// </summary>
40+
/// <param name="args">command line args</param>
41+
/// <returns>
42+
/// If called for the browser process (identified by no "type" command-line value) it will return immediately
43+
/// with a value of -1. If called for a recognized secondary process it will block until the process should exit
44+
/// and then return the process exit code.
45+
/// </returns
46+
int Main(IEnumerable<String^>^ args)
47+
{
48+
return Main(args, nullptr);
49+
}
50+
51+
/// <summary>
52+
/// This function should be called from the application entry point function (typically Program.Main)
53+
/// to execute a secondary process e.g. gpu, plugin, renderer, utility
54+
/// It can be used to run secondary processes (BrowserSubProcess) from your main applications executable
55+
/// or from a separate executable specified by the CefSettings.BrowserSubprocessPath value.
56+
/// CefSharp defaults to using the latter approach, a default implementation (CefSharp.BrowserSubProcess.exe) is
57+
/// supplied, see https://github.com/cefsharp/CefSharp/wiki/General-Usage#processes for more details.
58+
/// </summary>
59+
/// <param name="args">command line args</param>
60+
/// <param name="handler">An option IRenderProcessHandler implementation, use null if no handler is required</param>
61+
/// <returns>
62+
/// If called for the browser process (identified by no "type" command-line value) it will return immediately
63+
/// with a value of -1. If called for a recognized secondary process it will block until the process should exit
64+
/// and then return the process exit code.
65+
/// </returns>
66+
int Main(IEnumerable<String^>^ args, IRenderProcessHandler^ handler)
67+
{
68+
auto type = CommandLineArgsParser::GetArgumentValue(args, CefSharpArguments::SubProcessTypeArgument);
69+
70+
auto parentProcessId = -1;
71+
72+
// The Crashpad Handler doesn't have any HostProcessIdArgument, so we must not try to
73+
// parse it lest we want an ArgumentNullException.
74+
if (type != "crashpad-handler")
75+
{
76+
parentProcessId = int::Parse(CommandLineArgsParser::GetArgumentValue(args, CefSharpArguments::HostProcessIdArgument));
77+
if (CommandLineArgsParser::HasArgument(args, CefSharpArguments::ExitIfParentProcessClosed))
78+
{
79+
ParentProcessMonitor::StartMonitorTask(parentProcessId);
80+
}
81+
}
82+
83+
// Use our custom subProcess provides features like EvaluateJavascript
84+
if (type == "renderer")
85+
{
86+
auto subProcess = GetSubprocess(args, parentProcessId, handler);
87+
88+
try
89+
{
90+
return subProcess->Run();
91+
}
92+
finally
93+
{
94+
delete subProcess;
95+
}
96+
}
97+
98+
return SubProcess::ExecuteProcess(args);
99+
}
100+
101+
protected:
102+
virtual SubProcess^ GetSubprocess(IEnumerable<String^>^ args, int parentProcessId, IRenderProcessHandler^ handler)
103+
{
104+
return gcnew SubProcess(handler, args);
105+
}
106+
};
107+
}
108+
}

CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,15 @@
173173
<ClInclude Include="..\CefSharp.Core\Internals\Serialization\Primitives.h" />
174174
<ClInclude Include="..\CefSharp.Core\Internals\StringUtils.h" />
175175
<ClInclude Include="BindObjectAsyncHandler.h" />
176+
<ClCompile Include="BrowserSubprocessExecutable.h" />
176177
<ClInclude Include="SubProcessApp.h" />
177178
<ClInclude Include="Async\JavascriptAsyncMethodCallback.h" />
178179
<ClInclude Include="Async\JavascriptAsyncMethodHandler.h" />
179180
<ClInclude Include="Async\JavascriptAsyncMethodWrapper.h" />
180181
<ClInclude Include="Async\JavascriptAsyncObjectWrapper.h" />
181182
<ClInclude Include="CefAppUnmanagedWrapper.h" />
182183
<ClInclude Include="JavascriptPostMessageHandler.h" />
184+
<ClCompile Include="WcfBrowserSubprocessExecutable.h" />
183185
<ClInclude Include="Wrapper\Frame.h" />
184186
<ClInclude Include="Wrapper\Browser.h" />
185187
<ClInclude Include="Wrapper\V8Context.h" />

CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj.filters

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,12 @@
181181
<ClCompile Include="Wrapper\Browser.cpp">
182182
<Filter>Source Files</Filter>
183183
</ClCompile>
184+
<ClCompile Include="WcfBrowserSubprocessExecutable.h">
185+
<Filter>Header Files</Filter>
186+
</ClCompile>
187+
<ClCompile Include="BrowserSubprocessExecutable.h">
188+
<Filter>Header Files</Filter>
189+
</ClCompile>
184190
</ItemGroup>
185191
<ItemGroup>
186192
<ResourceCompile Include="Resource.rc" />
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright © 2019 The CefSharp Authors. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4+
5+
#pragma once
6+
7+
#include "Stdafx.h"
8+
9+
#include "SubProcess.h"
10+
#include "WcfEnabledSubProcess.h"
11+
#include "BrowserSubprocessExecutable.h"
12+
13+
using namespace System;
14+
using namespace CefSharp::Internals;
15+
16+
namespace CefSharp
17+
{
18+
namespace BrowserSubprocess
19+
{
20+
/// <summary>
21+
/// WcfBrowserSubprocessExecutable provides the fundimental browser process handling for
22+
/// CefSharp.BrowserSubprocess.exe and can be used to self host the BrowserSubProcess in your
23+
/// existing application (preferred approach for .Net Core).
24+
/// If the <see cref="CefSharpArguments.WcfEnabledArgument"/> command line argument is
25+
/// present then the WcfEnabledSubProcess implementation is used.
26+
/// </summary>
27+
public ref class WcfBrowserSubprocessExecutable : BrowserSubprocessExecutable
28+
{
29+
public:
30+
WcfBrowserSubprocessExecutable()
31+
{
32+
33+
}
34+
protected:
35+
SubProcess^ GetSubprocess(IEnumerable<String^>^ args, int parentProcessId, IRenderProcessHandler^ handler) override
36+
{
37+
auto wcfEnabled = CommandLineArgsParser::HasArgument(args, CefSharpArguments::WcfEnabledArgument);
38+
if (wcfEnabled)
39+
{
40+
return gcnew WcfEnabledSubProcess(parentProcessId, handler, args);
41+
}
42+
return gcnew SubProcess(handler, args);
43+
}
44+
};
45+
}
46+
}

CefSharp.BrowserSubprocess/Program.cs

Lines changed: 11 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,84 +2,39 @@
22
//
33
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
44

5-
using System;
65
using System.Diagnostics;
7-
using System.Threading.Tasks;
8-
using CefSharp.Internals;
96
using CefSharp.RenderProcess;
107

118
namespace CefSharp.BrowserSubprocess
129
{
1310
/// <summary>
1411
/// When implementing your own BrowserSubprocess
12+
/// - For Full .Net use <see cref="WcfBrowserSubprocessExecutable"/>
13+
/// - For .Net Core use <see cref="BrowserSubprocessExecutable"/> (No WCF Support)
1514
/// - Include an app.manifest with the dpi/compatability sections, this is required (this project contains the relevant).
1615
/// - If you are targeting x86/Win32 then you should set /LargeAddressAware (https://docs.microsoft.com/en-us/cpp/build/reference/largeaddressaware?view=vs-2017)
1716
/// </summary>
1817
public class Program
1918
{
2019
public static int Main(string[] args)
2120
{
22-
Debug.WriteLine("BrowserSubprocess starting up with command line: " + String.Join("\n", args));
21+
Debug.WriteLine("BrowserSubprocess starting up with command line: " + string.Join("\n", args));
2322

2423
SubProcess.EnableHighDPISupport();
2524

26-
int result;
27-
var type = args.GetArgumentValue(CefSharpArguments.SubProcessTypeArgument);
25+
//Add your own custom implementation of IRenderProcessHandler here
26+
IRenderProcessHandler handler = null;
2827

29-
var parentProcessId = -1;
30-
31-
// The Crashpad Handler doesn't have any HostProcessIdArgument, so we must not try to
32-
// parse it lest we want an ArgumentNullException.
33-
if (type != "crashpad-handler")
34-
{
35-
parentProcessId = int.Parse(args.GetArgumentValue(CefSharpArguments.HostProcessIdArgument));
36-
if (args.HasArgument(CefSharpArguments.ExitIfParentProcessClosed))
37-
{
38-
Task.Factory.StartNew(() => AwaitParentProcessExit(parentProcessId), TaskCreationOptions.LongRunning);
39-
}
40-
}
41-
42-
// Use our custom subProcess provides features like EvaluateJavascript
43-
if (type == "renderer")
44-
{
45-
//Add your own custom implementation of IRenderProcessHandler here
46-
IRenderProcessHandler handler = null;
47-
var wcfEnabled = args.HasArgument(CefSharpArguments.WcfEnabledArgument);
48-
var subProcess = wcfEnabled ? new WcfEnabledSubProcess(parentProcessId, handler, args) : new SubProcess(handler, args);
49-
50-
using (subProcess)
51-
{
52-
result = subProcess.Run();
53-
}
54-
}
55-
else
56-
{
57-
result = SubProcess.ExecuteProcess(args);
58-
}
28+
//The WcfBrowserSubprocessExecutable provides BrowserSubProcess functionality
29+
//specific to CefSharp, WCF support (required for Sync JSB) will optionally be
30+
//enabled if the CefSharpArguments.WcfEnabledArgument command line arg is present
31+
//For .Net Core use BrowserSubprocessExecutable as there is no WCF support
32+
var browserProcessExe = new WcfBrowserSubprocessExecutable();
33+
var result = browserProcessExe.Main(args, handler);
5934

6035
Debug.WriteLine("BrowserSubprocess shutting down.");
6136

6237
return result;
6338
}
64-
65-
private static async void AwaitParentProcessExit(int parentProcessId)
66-
{
67-
try
68-
{
69-
var parentProcess = Process.GetProcessById(parentProcessId);
70-
parentProcess.WaitForExit();
71-
}
72-
catch (Exception e)
73-
{
74-
//main process probably died already
75-
Debug.WriteLine(e);
76-
}
77-
78-
await Task.Delay(1000); //wait a bit before exiting
79-
80-
Debug.WriteLine("BrowserSubprocess shutting down forcibly.");
81-
82-
Process.GetCurrentProcess().Kill();
83-
}
8439
}
8540
}

CefSharp/CefSharp.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
<Compile Include="DefaultApp.cs" />
104104
<Compile Include="Enums\SchemeOptions.cs" />
105105
<Compile Include="Internals\CommandLineArgDictionary.cs" />
106+
<Compile Include="Internals\ParentProcessMonitor.cs" />
106107
<Compile Include="Internals\ReadOnlyNameValueCollection.cs" />
107108
<Compile Include="Internals\TaskScheduler\LimitedConcurrencyLevelTaskScheduler.cs" />
108109
<Compile Include="IUrlRequest.cs" />
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright © 2019 The CefSharp Authors. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4+
5+
using System;
6+
using System.Diagnostics;
7+
using System.Threading.Tasks;
8+
9+
namespace CefSharp.Internals
10+
{
11+
/// <summary>
12+
/// Monitor the parent process and exit if the parent process closes
13+
/// before the subprocess. This class is used by the CefSharp.BrowserSubprocess to
14+
/// self terminate if the parent dies without notifying it to exit.
15+
/// See https://github.com/cefsharp/CefSharp/issues/2359 for more information.
16+
/// </summary>
17+
public static class ParentProcessMonitor
18+
{
19+
/// <summary>
20+
/// Starts a long running task (spawns new thread) used to monitor the parent process
21+
/// and calls <see cref="Process.Kill"/> if the parent exits unexpectedly (usually result of a crash).
22+
/// </summary>
23+
/// <param name="parentProcessId">process Id of the parent application</param>
24+
public static void StartMonitorTask(int parentProcessId)
25+
{
26+
Task.Factory.StartNew(() => AwaitParentProcessExit(parentProcessId), TaskCreationOptions.LongRunning);
27+
}
28+
29+
private static async void AwaitParentProcessExit(int parentProcessId)
30+
{
31+
try
32+
{
33+
var parentProcess = Process.GetProcessById(parentProcessId);
34+
parentProcess.WaitForExit();
35+
}
36+
catch (Exception e)
37+
{
38+
//main process probably died already
39+
Debug.WriteLine(e);
40+
}
41+
42+
await Task.Delay(1000); //wait a bit before exiting
43+
44+
Debug.WriteLine("BrowserSubprocess shutting down forcibly.");
45+
46+
Process.GetCurrentProcess().Kill();
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)