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)
return;
MethodInfo main =
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
{
try
if (parent != null)
{
if (parent != null)
{
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);
parent.Visible = false;
Application.DoEvents();
}
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");
parent.Visible = true;
Application.DoEvents();
}
}
}
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"))
catch (Exception ex)
{
Trace.Listeners.Add(dbg);
Trace.Listeners.Add(new ConsoleTraceListener());
using (OpenTK.Toolkit.Init())
{
_main.Invoke(null, null);
}
dbg.Flush();
dbg.Close();
Debug.Print(ex.ToString());
}
}
catch (TargetInvocationException expt)
{
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());
parent.Visible = true;
parent.Focus();
Application.DoEvents();
}
}
}
// Tries to detect the path that contains the source for the examples.

View file

@ -27,62 +27,75 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using System.Drawing;
namespace Examples
{
static class Program
{
static void EnableOpenTKHack()
static void LaunchExample(string type)
{
// If OpenTK is not initialized before Windows.Forms,
// the program will crash on Mac OS X. This hack will
// enable OpenTK in a temporary AppDomain before entering
// the main program - this appears to be enough.
var domain = AppDomain.CreateDomain("sandbox");
domain.DoCallBack(() => {
using (OpenTK.Toolkit.Init())
using (TextWriterTraceListener dbg = new TextWriterTraceListener("debug.log"))
using (OpenTK.Toolkit.Init())
{
Trace.Listeners.Add(dbg);
Trace.Listeners.Add(new ConsoleTraceListener());
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]
public static void Main()
public static void Main(string[] args)
{
try
if (args.Length > 0)
{
EnableOpenTKHack();
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);
}
LaunchExample(args[0]);
}
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.",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
Trace.WriteLine(e.ToString());
try
{
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());
}
}
}
}