[.net] Line numbers in stack trace on dynamically-compiled assemblies

Started by
1 comment, last by BeanDog 15 years, 11 months ago
I'm using C# as a scripting language, having my script files compiled using the CSharpCodeProvider and run, with references to an assembly that has utility functions in it. When an exception is thrown by my dynamically-compiled assembly, I can get a stack trace on the exception, but it doesn't show what line the error occurred on. I need to be able to tell where in the (large) script the error occurred. It doesn't seem to matter if IncludeDebugInformation is true or false on my CompilerParameters. I tried compiling from temporary files rather than directly from a string in memory, but it had no effect. How can I get line numbers on stack traces for exceptions thrown by dynamically-compiled assemblies? Sample code:

using System;
using System.Collections.Generic;
using System.Text;
using System.CodeDom.Compiler;
using System.IO;
using Microsoft.CSharp;

namespace ConsoleApplication1 {
    class Program {
        static void Main(string[] args) {
            Dictionary<string, string> dInit = new Dictionary<string, string>();
            dInit.Add("header.cs", @"
void SomeFunc() {
    int x = 4 * 3;
    throw new Exception(""Hello exceptional world!"");
}");

            Compile(dInit, @"
SomeFunc();
");
        }

        static string WriteTempFile(string strData) {
            string strFilename = Path.GetTempFileName();

            StreamWriter sw = new StreamWriter(strFilename);
            sw.Write(strData);
            sw.Close();

            return strFilename;
        }

        static void Compile(Dictionary<string, string> dInit, string strScript) {
            Random r = new Random();

            CodeDomProvider Compiler = new CSharpCodeProvider();
            CompilerParameters Params = new CompilerParameters();
            Params.GenerateExecutable = false;
            Params.GenerateInMemory = true;
            Params.MainClass = "UnitScript";
            Params.OutputAssembly = Path.Combine(Path.GetTempPath(), "UnitScript" + r.Next() + r.Next() + r.Next());
            Params.ReferencedAssemblies.Add(System.Reflection.Assembly.GetExecutingAssembly().Location);
            Params.IncludeDebugInformation = false;

            Dictionary<string, string> dTempFiles = new Dictionary<string, string>();
            List<string> lFilenames = new List<string>();
            foreach (string key in dInit.Keys) {
                string strInit = "using System; namespace Test { partial class UnitScript {" + dInit[key] + Environment.NewLine + "}}";
                lFilenames.Add(WriteTempFile(strInit));
                dTempFiles[lFilenames[lFilenames.Count - 1].ToUpper()] = key;
            }

            lFilenames.Add(WriteTempFile("using System; namespace Test { partial class UnitScript { public void Run() { " + strScript + Environment.NewLine + "}}}"));
            dTempFiles[lFilenames[lFilenames.Count - 1].ToUpper()] = "";

            CompilerResults Result = Compiler.CompileAssemblyFromFile(Params, lFilenames.ToArray());

            foreach (string strFilename in lFilenames)
                File.Delete(strFilename);

            if (!Result.Errors.HasErrors) {
                Type tScript = Result.CompiledAssembly.GetTypes()[0];
                object CompiledScript = tScript.GetConstructor(new Type[] { }).Invoke(new object[] { });

                try {
                    CompiledScript.GetType().GetMethod("Run").Invoke(CompiledScript, new object[] { });
                }
                catch (Exception ex) {
                    Console.WriteLine(ex.InnerException.StackTrace);
                }
            }
            else {
                foreach (CompilerError e in Result.Errors) {
                    //Do something.
                }
            }
        }
    }
}


Output:

   at Test.UnitScript.SomeFunc()
   at Test.UnitScript.Run()
Please, any help getting that StackTrace to show line numbers will make my life much easier in the long run. Thanks in advance!
Advertisement
Perhaps this:

http://dnchannel.blogspot.com/2007/09/getting-line-numbers-in-exception-stack.html

and this:

http://forums.msdn.microsoft.com/en-US/csharplanguage/thread/a58dc2a0-0612-407b-8cbe-10f1784ba85a/

will help! Hope you get it working,

James
OK, got it! The answer was to not use the Result.CompiledAssembly, but to call Assembly.LoadFile on the actual output DLL (and make sure the assembly has the .dll extension), then use that copy of the assembly to do the work.

This topic is closed to new replies.

Advertisement