using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.TypeSystem; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; namespace Vanara.PInvoke.Tests { public static class CSharpRunner { private static IEnumerable StdRefs => new[] { typeof(object).Assembly.Location, typeof(Enumerable).Assembly.Location }; public static object Run(string snippet, IEnumerable references, string typeName, string methodName, params object[] args) => Invoke(CompileLib(Parse(snippet), references), typeName, methodName, args); public static object Run(MethodInfo methodInfo, params object[] args) => Invoke(CompileLib(Decompile(methodInfo.DeclaringType), methodInfo.DeclaringType.GetReferencedAssemblyNames()), methodInfo.DeclaringType.FullName, methodInfo.Name, args); public static Process RunProcess(string args = null) where T : class => RunProcess(typeof(T), args); public static Process RunProcess(Type mainType, string args = null) { if (mainType.GetMethod("Main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) is null) throw new ArgumentException("Supplied type must include a static Main method."); var exe = Path.Combine(Path.GetDirectoryName(mainType.Assembly.Location), "tmp" + Guid.NewGuid().ToString("N") + ".exe"); CompileExe(exe, Decompile(mainType), mainType.GetReferencedAssemblyNames(), mainType.FullName); return CreateProcess(exe, args, Path.GetDirectoryName(mainType.Assembly.Location)); } public static Process RunProcess(string snippet, string workingDir, IEnumerable references = null, string typeName = null, string args = null) { var exe = Path.Combine(workingDir, "tmp" + Guid.NewGuid().ToString("N") + ".exe"); CompileExe(exe, Parse(snippet), references, typeName); return CreateProcess(exe, args, workingDir); } private static void CompileExe(string outputExePath, SyntaxTree syntaxTree, IEnumerable references = null, string mainTypeName = null) { var mrefs = (references ?? StdRefs).Select(a => MetadataReference.CreateFromFile(a)); var opts = new CSharpCompilationOptions(OutputKind.ConsoleApplication, allowUnsafe: true, mainTypeName: mainTypeName); var compilation = CSharpCompilation.Create(Path.GetRandomFileName(), new[] { syntaxTree }, mrefs, opts); var result = compilation.Emit(outputExePath); if (!result.Success) { throw new InvalidOperationException(string.Join("\n", result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error).Select(d => $"{d.Id}: {d.GetMessage()}"))); } } private static Assembly CompileLib(SyntaxTree syntaxTree, IEnumerable references = null) { var mrefs = (references ?? StdRefs).Select(a => MetadataReference.CreateFromFile(a)); var compilation = CSharpCompilation.Create(Path.GetRandomFileName(), new[] { syntaxTree }, mrefs, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); using (var ms = new MemoryStream()) { var result = compilation.Emit(ms); if (result.Success) { ms.Seek(0, SeekOrigin.Begin); return Assembly.Load(ms.ToArray()); } else { throw new InvalidOperationException(string.Join("\n", result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error).Select(d => $"{d.Id}: {d.GetMessage()}"))); } } } private static Process CreateProcess(string exe, string args, string workingDir) { var p = new Process { StartInfo = new ProcessStartInfo(exe, args) { WorkingDirectory = workingDir } }; p.Exited += (s, e) => File.Delete(exe); p.Start(); return p; } private static SyntaxTree Decompile(Type type) { var decompiler = new CSharpDecompiler(type.Assembly.Location, new DecompilerSettings()); var typeInfo = decompiler.TypeSystem.MainModule.Compilation.FindType(type).GetDefinition(); return Parse(decompiler.DecompileTypeAsString(typeInfo.FullTypeName)); } private static IEnumerable GetReferencedAssemblyNames(this Type type) => type.Assembly.GetReferencedAssemblies().Select(n => Assembly.Load(n).Location); private static object Invoke(Assembly assembly, string typeName, string methodName, object[] args) { var type = assembly.GetType(typeName); var obj = Activator.CreateInstance(type); return type.InvokeMember(methodName, BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, args); } private static SyntaxTree Parse(string snippet) => CSharpSyntaxTree.ParseText(snippet); } }