Jump to content
  • Advertisement
Sign in to follow this  
BeanDog

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

This topic is 3771 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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!

Share this post


Link to post
Share on other sites
Advertisement
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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!