Вызов частного метода, сохраняющий стек вызовов - PullRequest
14 голосов
/ 28 февраля 2011

Я пытаюсь найти решение для «взлома непубличных методов».

Я просто хочу вызвать RuntimeMethodInfo.InternalGetCurrentMethod(...), передав свой собственный параметр (чтобы я мог реализовать GetCallingMethod()), илинепосредственно используйте RuntimeMethodInfo.InternatGetCurrentMethod(ref StackCrawlMark.LookForMyCaller) в моих процедурах регистрации.GetCurrentMethod реализовано как:

[MethodImpl(MethodImplOptions.NoInlining)] 
public static MethodBase GetCurrentMethod() 
{     
    StackCrawlMark lookForMyCaller = StackCrawlMark.LookForMyCaller;     
    return RuntimeMethodInfo.InternalGetCurrentMethod(ref lookForMyCaller); 
}

, где объявлено InternalGetCurrentMethod: internal: -).

У меня нет проблем с вызовом метода с использованием отражения, но это портит вызовстек, и это единственная вещь, которую нужно сохранить, иначе она побеждает свою цель.

Каковы мои шансы держать трассировку стека близкой к оригиналу (по крайней мере, в пределах расстояния от разрешенного StackCrawlMarks, которые LookForMe, LookForMyCaller и LookForMyCallersCaller. Есть ли какой-нибудь сложный способ достичь того, чего я хочу?

1 Ответ

20 голосов
/ 28 февраля 2011

Если есть что-то, что мне нравится в C #, это динамические методы.

Они позволяют вам обойти каждую цель и намерение создателей .NET.: D

Вот (поточно-ориентированное) решение:

(Эрик Липперт, пожалуйста, не читайте это ...)

enum MyStackCrawlMark { LookForMe, LookForMyCaller, LookForMyCallersCaller, LookForThread }
delegate MethodBase MyGetCurrentMethodDelegate(ref MyStackCrawlMark mark);
static MyGetCurrentMethodDelegate dynamicMethod = null;

static MethodBase MyGetCurrentMethod(ref MyStackCrawlMark mark)
{
    if (dynamicMethod == null)
    {
        var m = new DynamicMethod("GetCurrentMethod",
            typeof(MethodBase),
            new Type[] { typeof(MyStackCrawlMark).MakeByRefType() },
            true //Ignore all privilege checks :D
        );
        var gen = m.GetILGenerator();
        gen.Emit(OpCodes.Ldarg_0); //NO type checking here!
        gen.Emit(OpCodes.Call,
            Type.GetType("System.Reflection.RuntimeMethodInfo", true)
                .GetMethod("InternalGetCurrentMethod",
                    BindingFlags.Static | BindingFlags.NonPublic));
        gen.Emit(OpCodes.Ret);
        Interlocked.CompareExchange(ref dynamicMethod,
            (MyGetCurrentMethodDelegate)m.CreateDelegate(
                typeof(MyGetCurrentMethodDelegate)), null);
    }
    return dynamicMethod(ref mark);
}
[MethodImpl(MethodImplOptions.NoInlining)]
static void Test()
{
    var mark = MyStackCrawlMark.LookForMe; //"Me" is Test's _caller_, NOT Test
    var method = MyGetCurrentMethod(ref mark);
    Console.WriteLine(method.Name);
}
...