Use separate process for executing examples

Using a separate, isolated process protects against incompatible interactions between the Example Browser (WinForms) and the executing example (native or SDL). It also protects the main GUI from crashes in the example code.
This commit is contained in:
Stefanos A 2013-10-02 01:10:03 +02:00
parent 31f2df094b
commit f8d9667653
2 changed files with 77 additions and 120 deletions

View file

@ -358,98 +358,42 @@ namespace Examples
if (e == null) if (e == null)
return; return;
MethodInfo main = try
e.Example.GetMethod("Main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ??
e.Example.GetMethod("Main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] {
typeof(object),
typeof(object)
}, null);
if (main != null)
{ {
try if (parent != null)
{ {
if (parent != null) parent.Visible = false;
{ Application.DoEvents();
parent.Visible = false;
Application.DoEvents();
}
Trace.WriteLine(String.Format("Launching sample: \"{0}\"", e.Attribute.Title));
Trace.WriteLine(String.Empty);
AppDomain sandbox = AppDomain.CreateDomain("Sandbox");
sandbox.DomainUnload += HandleSandboxDomainUnload;
SampleRunner runner = new SampleRunner(main);
CrossAppDomainDelegate cross = new CrossAppDomainDelegate(runner.Invoke);
sandbox.DoCallBack(cross);
AppDomain.Unload(sandbox);
} }
finally Trace.WriteLine(String.Format("Launching sample: \"{0}\"", e.Attribute.Title));
Trace.WriteLine(String.Empty);
var info = new ProcessStartInfo
{ {
if (parent != null) FileName = Application.ExecutablePath,//.Replace("vshost.exe", String.Empty),
Arguments = e.Example.ToString()
};
var process = Process.Start(info);
process.WaitForExit();
}
finally
{
if (parent != null)
{
try
{ {
textBoxOutput.Text = File.ReadAllText("debug.log"); textBoxOutput.Text = File.ReadAllText("debug.log");
parent.Visible = true;
Application.DoEvents();
} }
} catch (Exception ex)
}
else
{
MessageBox.Show("The selected example does not define a Main method", "Entry point not found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
static void HandleSandboxDomainUnload(object sender, EventArgs e)
{
AppDomain sandbox = (AppDomain)sender;
sandbox.DomainUnload -= HandleSandboxDomainUnload;
}
[Serializable]
class SampleRunner
{
MethodInfo _main;
public SampleRunner(MethodInfo main)
{
_main = main;
}
public void Invoke()
{
try
{
using (TextWriterTraceListener dbg = new TextWriterTraceListener("debug.log"))
{ {
Trace.Listeners.Add(dbg); Debug.Print(ex.ToString());
Trace.Listeners.Add(new ConsoleTraceListener());
using (OpenTK.Toolkit.Init())
{
_main.Invoke(null, null);
}
dbg.Flush();
dbg.Close();
} }
} parent.Visible = true;
catch (TargetInvocationException expt) parent.Focus();
{ Application.DoEvents();
string ex_info;
if (expt.InnerException != null)
ex_info = expt.InnerException.ToString();
else
ex_info = expt.ToString();
//MessageBox.Show(ex_info, "An OpenTK example encountered an error.", MessageBoxButtons.OK, MessageBoxIcon.Warning);
Trace.WriteLine(ex_info.ToString());
}
catch (Exception expt)
{
Trace.WriteLine(expt.ToString());
} }
} }
} }
// Tries to detect the path that contains the source for the examples. // Tries to detect the path that contains the source for the examples.

View file

@ -27,62 +27,75 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Text; using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using System.Drawing;
namespace Examples namespace Examples
{ {
static class Program static class Program
{ {
static void EnableOpenTKHack() static void LaunchExample(string type)
{ {
// If OpenTK is not initialized before Windows.Forms, using (TextWriterTraceListener dbg = new TextWriterTraceListener("debug.log"))
// the program will crash on Mac OS X. This hack will using (OpenTK.Toolkit.Init())
// enable OpenTK in a temporary AppDomain before entering {
// the main program - this appears to be enough. Trace.Listeners.Add(dbg);
var domain = AppDomain.CreateDomain("sandbox"); Trace.Listeners.Add(new ConsoleTraceListener());
domain.DoCallBack(() => {
using (OpenTK.Toolkit.Init()) var example = Type.GetType(type);
if (example != null)
{ {
example.InvokeMember("Main",
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
null, null, null);
} }
});
dbg.Flush();
dbg.Close();
}
} }
[STAThread] [STAThread]
public static void Main() public static void Main(string[] args)
{ {
try if (args.Length > 0)
{ {
EnableOpenTKHack(); LaunchExample(args[0]);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (Form browser = new ExampleBrowser())
{
try
{
if (File.Exists("debug.log"))
File.Delete("debug.log");
if (File.Exists("trace.log"))
File.Delete("trace.log");
}
catch (Exception expt)
{
MessageBox.Show("Could not access debug.log", expt.ToString());
}
Application.Run(browser);
}
} }
catch (System.Security.SecurityException e) else
{ {
MessageBox.Show("The Example Launcher failed to start, due to insufficient permissions. This may happen if you execute the application from a network share.", "OpenTK Example Launcher failed to start.", try
MessageBoxButtons.OK, MessageBoxIcon.Exclamation); {
Trace.WriteLine(e.ToString()); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (Form browser = new ExampleBrowser())
{
try
{
if (File.Exists("debug.log"))
File.Delete("debug.log");
if (File.Exists("trace.log"))
File.Delete("trace.log");
}
catch (Exception expt)
{
MessageBox.Show("Could not access debug.log", expt.ToString());
}
Application.Run(browser);
}
}
catch (System.Security.SecurityException e)
{
MessageBox.Show("The Example Launcher failed to start, due to insufficient permissions. This may happen if you execute the application from a network share.", "OpenTK Example Launcher failed to start.",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
Trace.WriteLine(e.ToString());
}
} }
} }
} }