Can I change current stacktrace in C#?

In short, I need to modify the current stack trace in C#, so that method that I call (which I cannot modify) would think that it was someone else who called it.

Now, to give a little context about why I need to do such a horrible hack.

In Unity3d, Debug.Log creates a new record in developer console. When you double-click it, it opens the IDE to show exact file and line that was responsible for that record. However, for various reasons (disable debug in production, make it usable in other threads) I created a wrapper class Print that I use instead of UnityEngine.Debug.

But with wrapper, now when a developer clicks on the log record in console, it is Print that gets opened, not the actual place where Print.Log was called. And since Debug.Log does it regardless of the error message or context object, I figured that it uses call stack to identify the file to open. Needless to say, I want to fix that.


I don't believe it is possible. Looking at the source code of Environment.StackTrace and the subsequent GetStackTrace() call we can see it creates the stacktrace each time whenever it is called. Since there doesn't seem to be anything you could inject to change some of its working, I think it's safe to conclude that you cannot change the stacktrace without modifying how it is called.

If you would have the possibility to do that, you could use something like this which comes down to retrieving the current stacktrace and using reflection to change the most recent StackFrame 's method field to mimic what it is you're after.

void Main()
{
    DoSomething();
}

void DoSomething(){
    Console.WriteLine (Environment.StackTrace); // Last frame: at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)

    var frames = new StackTrace().GetFrames();
    var lastframe = frames[0]; // last frame: DoSomething at offset 100 in file:line:column <filename unknown>:0:0
    var methodField = lastframe.GetType().GetField("method", BindingFlags.Instance | BindingFlags.NonPublic);
    var redirectedMethod = typeof(Redirect).GetMethod("RedirectToMethod", BindingFlags.Instance | BindingFlags.Public);
    methodField.SetValue(lastframe, redirectedMethod);
    Console.WriteLine (frames); // last frame: RedirectToMethod at offset 100 in file:line:column <filename unknown>:0:0
    Console.WriteLine (Environment.StackTrace); // last frame: at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
}

public class Redirect {
    public void RedirectToMethod() { }
}

But once again: this is very brittle and not applicable in your current situation. What you are trying to do is fairly exotic and should probably be avoided altogether.

As to an actual solution: I'm afraid I don't have any experience with Unity so I can only google but this thread might be interesting to you. I referred to .NET sources here but you can find very similar code in the Mono source ofcourse.


A work around to this, is compile your Print class into a unity compatible Dll (.Net 2 I think). When doubling clicking on the log, the console will ignore the dll information and go to where Print was called.

链接地址: http://www.djcxy.com/p/80820.html

上一篇: git apply确实没有输出,也没有修补任何东西

下一篇: 我可以更改C#中的当前堆栈跟踪吗?