Как получить исполняемый объект для стекового кадра?

При использовании отражения можно получить стек вызовов (кроме того, что это может быть грубое приближение из-за оптимизации JIT) с использованием System.Diagnostics.StackTrace и исследовать содержащиеся в нем объекты StackFrame.

Как я могу получить ссылку на объект (этот указатель), на котором выполняется метод в кадре стека?

Я знаю, что могу получить MethodBase, вызвав GetMethod() в объекте фрейма стека, но то, что я ищу, - это что-то вроде линий GetObject() (которое естественно возвращает null, если метод статичен), Кажется, что объект фрейма стека может быть запрошен только для статически определенной информации, такой как информация о методе, исходный файл и т.д.

Отладчик VS знает (хотя он, вероятно, использует другой метод получения трассировки стека вызовов), так как можно дважды щелкнуть любой стек стека в окне стека вызовов и посмотреть значения локалей и полей классов.

EDIT: Чтобы уточнить: мне нужен объект экземпляр, на который был вызван метод. I.e: Если метод Foo() вызывается в экземпляре объекта где-то в стеке вызовов, и он каскадирует к методу, который я выполняю трассировку стека, я бы хотел получить ссылку на A, откуда выполняю трассировку стека. (Не объявляющий тип базы методов)

Ответ 1

Я уверен, что это невозможно. Вот почему:

  • Это может нарушить безопасность типов, так как любой может найти кадр, получить объект независимо от того, какой AppDomain\Thread они выполняют или имеют ли они разрешение.

  • Идентификатор 'this' (С#) - это просто аргумент метода экземпляра (первый), поэтому на самом деле нет никакой разницы между статическими методами и методами экземпляра, компилятор делает свою магию для передайте правильный this метод экземпляра, что, конечно же, означает, что вам нужно иметь доступ ко всем аргументам метода для получения объекта this. (который StackFrame не поддерживает)

Возможно, с помощью кода unsafe можно получить указатель первого аргумента на метод экземпляра и затем применить его к правильному типу, но я не знаю, как это сделать, просто идея.

Кстати, вы можете представить методы экземпляра после компиляции, чтобы быть похожими на методы расширения С# 3.0, они получают указатель this в качестве своего первого аргумента.

Ответ 2

Можно получить ссылку на объект thiscall, но не только на .NET-код. Необходимо задействовать собственный код. Даже с использованием динамических классов и пространства имен System.Relection.Emit.NET не имеет инструментов для доступа к другим методам оценки и аргументов.

С другой стороны, если вы разбираете свой метод .NET, вы можете видеть, что эта ссылка вообще не передается в физическом стеке. thiscall ссылка сохраняется в регистре ECX (RCX для x64). Таким образом, можно подняться на стек от вашего метода до метода, из которого вы хотите получить объект thiscall. И затем найдите внутри этого метода машинные коды для инструкции, которые сохраняют регистр ECX (RCX) в стеке, и получите от этого относительного адреса инструкции, где эта ссылка лежит.

Конечно, метод альпинизма сильно отличается в приложениях x32 и x64. Для создания такой функции вы должны использовать не только С#, но и код сборки, и имейте в виду, что встроенный ассемблер не разрешен в x64; он должен быть полным модулем ассемблера.

Ответ 3

Я не уверен, что полностью понимаю, чего вы хотите, но если вы хотите узнать тип, в котором объявлен метод для определенного стекового фрейма, я думаю, что этот код возвращает это:

StackTrace trace = new StackTrace();    
Type methodOwner = trace.GetFrame(0).GetMethod().DeclaringType;

Конечно, вам необходимо передать индекс для интересующего фрейма (например, я использую 0).