Что именно происходит во время "переходного перехода"?
Я понимаю, что в некоторых случаях CLR нужно делать маршалинг, но позвольте сказать, что у меня есть:
using System.Runtime.InteropServices;
using System.Security;
[SuppressUnmanagedCodeSecurity]
static class Program
{
[DllImport("kernel32.dll", SetLastError = false)]
static extern int GetVersion();
static void Main()
{
for (; ; )
GetVersion();
}
}
Когда я ворвался в эту программу с помощью отладчика, я всегда вижу:
![]()
Учитывая, что не требуется маршалинг, который нужно сделать (правильно?), может кто-нибудь объяснить, что на самом деле происходит в этом "переход от перехода к родному", и почему это необходимо?
Ответы
Ответ 1
Сначала необходимо настроить стек вызовов, чтобы STDCALL мог произойти. Это вызов для Win32.
Далее среда выполнения вытолкнет так называемый кадр выполнения. Существует множество различных типов фреймов: безопасности, защищенных GC областей, собственных кодовых вызовов,...
Среда выполнения использует такой фрейм для отслеживания текущего исполняемого кода. Это может повлиять на потенциально одновременную сборку мусора и, возможно, на другие вещи. Это также помогает отладчику.
Так что на самом деле здесь не так много. Это довольно тонкий путь кода.
Ответ 2
Помимо слоя маршалинга, который отвечает за преобразование параметров для вас и вычисляет соглашения о вызовах, среда выполнения должна выполнять несколько других действий, чтобы поддерживать внутреннее состояние.
Необходимо проверить контекст безопасности, чтобы убедиться, что вызывающему коду разрешен доступ к собственным методам. Текущий управляемый стек стека должен быть сохранен, так что среда выполнения может выполнять переход в стеке для таких вещей, как отладка и обработка исключений (не говоря уже о собственном коде, который вызывает управляемый обратный вызов). Внутренние биты состояния должны быть установлены, чтобы указать, что мы в настоящее время выполняем собственный код.
Кроме того, регистры могут нуждаться в сохранении, в зависимости от того, что нужно отслеживать и которые, как гарантируется, будут восстановлены с помощью соглашения о вызовах. Корни GC, которые находятся в регистрах (locals), возможно, должны быть отмечены каким-то образом, чтобы они не собирали мусор во время собственного метода.
Таким образом, в основном это обработка стека и маршалинг типов, причем некоторые элементы безопасности загружаются. Хотя это не огромное количество вещей, это будет существенным препятствием для вызова небольших родных методов. Например, попытка P/Invoke в оптимизированной математической библиотеке редко приводит к выигрышу в производительности, поскольку накладных расходов достаточно, чтобы отрицать любые потенциальные выгоды. Некоторые результаты профилирования производительности обсуждаются здесь.
Ответ 3
Я понимаю, что на это был дан ответ, но я удивлен, что никто не предложил вам показать внешний код в окне отладки. Если вы щелкните правой кнопкой мыши по строке [Native to Managed Transition]
и отметьте опцию Show External Code
, вы увидите, какие именно методы .NET вызывают в процессе перехода. Это может дать вам лучшую идею. Вот пример:
![Displaying a Native to Managed Transition]()
Ответ 4
Я не вижу многого, что нужно сделать. Я подозреваю, что в основном информативный, указать вам, что часть вашего стека вызовов показывает собственные функции, а также указывает, что среда IDE и отладчик могут вести себя по-разному в течение этого перехода (поскольку управляемый код обрабатывается совсем по-разному в отладчике и некоторые функции, которые вы ожидаете, могут не работать)
Но я думаю, вы должны быть в состоянии узнать, просто осмотрев разборку вокруг перехода. Посмотрите, не делает ли что-нибудь необычное.
Ответ 5
Поскольку вы вызываете dll. он должен выйти из управляемой среды. Он входит в ядро Windows. Вы нарушаете барьер .net и переходите в код Windows, который не работает так же, как .NET.