28

I understand that the CLR needs to do marshaling in some cases, but let's say I have:

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();
    }
}

When I break into this program with a debugger, I always see:

Given that there is no marshaling that needs to be done (right?), could someone please explain what's actually happening in this "managed-to-native transition", and why it is necessary?

user541686
  • 205,094
  • 128
  • 528
  • 886
  • 6
    Maybe that line in the call stack is just informative, to let you know when you transitioned – David Heffernan Feb 08 '12 at 21:11
  • @DavidHeffernan: Oh....... I guess that works too... but I have a feeling there's something else happening as well (though I'd love to be proven wrong!). – user541686 Feb 08 '12 at 21:15
  • @DavidHeffernan: It's interesting that it doesn't say the same thing when going from `kernel32.dll` to `mscoree.dll`, though... so it makes me suspect something is actually going on. – user541686 Feb 08 '12 at 21:21
  • 1
    There is marshaling, because the CLR automatically treats a return value as if it is a normal argument using the `OutAttribute`. – Paolo Moretti Feb 08 '12 at 21:23
  • I'm not sure what you mean. Are you talking about callbacks? – David Heffernan Feb 08 '12 at 21:23
  • @Mehrdad - `kernel32.dll` and `mscoree.dll` are both native, so there's no managed/native transition to perform. `mscoree.dll` _houses_ the CLR, but that's why the transition happens higher in the stack. – Richard Szalay Feb 08 '12 at 21:24
  • 1
    @RichardSzalay: Oh you're absolutely right. But I guess the question is then, why isn't there a transition after `mscoreei.dll`? – user541686 Feb 08 '12 at 21:29
  • How about just looking at the disassembly around the transition? Should be able to see what's actually happening then – jalf Feb 08 '12 at 21:32
  • I'm no expert in marhalling, hence this is a comment rather than an answer, but I rather suspect that the return value *is* marshalled, even if that is simply to say "the return type of the method is representationally identical to the corresponding .NET type". Further, why *wouldn't* there be a transition? Control has passed into a native-code dll; that's still a transition even if there's no data being passed back and/or forth. – phoog Feb 08 '12 at 21:33

5 Answers5

17

First the call stack needs to be set up so that a STDCALL can happen. This is the calling convention for Win32.

Next the runtime will push a so called execution frame. There are many different types of frames: security asserts, GC protected regions, native code calls, ...

The runtime uses such a frame to track that currently native code is running. This has implications for a potentially concurrent garbage collection and probably other stuff. It also helps the debugger.

So not a lot is happening here actually. It is a pretty slim code path.

usr
  • 168,620
  • 35
  • 240
  • 369
7

Besides the marshaling layer, which is responsible for converting parameters for you and figuring out calling conventions, the runtime needs to do a few other things to keep internal state consistent.

The security context needs to be checked, to make sure the calling code is allowed to access native methods. The current managed stack frame needs to be saved, so that the runtime can do a stack walk back for things like debugging and exception handling (not to mention native code that calls into a managed callback). Internal bits of state need to be set to indicate that we're currently running native code.

Additionally, registers may need to be saved, depending on what needs to be tracked and which are guaranteed to be restored by the calling convention. GC roots that are in registers (locals) might need to be marked in some way so that they don't get garbage collected during the native method.

So mainly it's stack handling and type marshaling, with some security stuff thrown in. Though it's not a huge amount of stuff, it will represent a significant barrier against calling smaller native methods. For example, trying to P/Invoke into an optimized math library rarely results in a performance win, since the overhead is enough to negate any of the potential benefits. Some performance profiling results are discussed here.

MikeP
  • 7,829
  • 33
  • 34
5

I realise that this has been answered, but I'm surprised that no one has suggested that you show the external code in the debug window. If you right click on the [Native to Managed Transition] line and tick the Show External Code option, you will see exactly which .NET methods are being called in the transition. This may give you a better idea. Here is an example:

Displaying a Native to Managed Transition

Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • What version of Visual Studio? In Visual Studio 2017 I can do this for an [External Code] label in the call stack, but there's no effect on [Native to Managed Transition] – giles Oct 21 '20 at 18:30
0

I can't really see much that'd be necessary to do. I suspect that it is mainly informative, to indicate to you that part of your call stack shows native functions, and also to indicate that the IDE and debugger may behave differently across that transition (since managed code is handled very differently in the debugger, and some features you expect may not work)

But I guess you should be able to find out simply by inspecting the disassembly around the transition. See if it does anything unusual.

jalf
  • 243,077
  • 51
  • 345
  • 550
  • 1
    It's hard to inspect the disassembly: VS tells me that the instruction in my C# code is `call 0xFFEFBFAC`, but there is nothing at that address -- when I step into that function, it automatically goes to `_GetVersionStub@0`, which is at `75834437`. So it's obviously skipping some code. Maybe I could do it with WinDbg though? Not sure, I could try it and see. – user541686 Feb 08 '12 at 21:38
-1

Since you are calling a dll. it needs to go out of the managed environment. It is going into windows core. You are breaking the .net barrier and going into windows code that doesn't run the same as .NET.

iefpw
  • 6,816
  • 15
  • 55
  • 79
  • 2
    Not very informative. You're just rephrasing the question. :) – jalf Feb 08 '12 at 21:23
  • 2
    @jalf but "breaking the .NET barrier" is a nice rephrasing. – phoog Feb 08 '12 at 21:29
  • I'm not sure what he was asking. What exactly is happening during managed to native transition. It is calling the windows dll that is outside the .NET framework and that's why it is informing the user that there has been a "managed to native transition." Didn't try to be clever. Once there has been a transition to native, the resources needs to be released and cleaned by the user. Once it goes native, there isn't a lot of debugging information available. – iefpw Feb 08 '12 at 22:01
  • 3
    @iefpw If you aren't sure what someone is asking, *don't answer the question*. You have enough rep to leave a comment asking for clarification. Your "answer" is essentially without meaning, much less helpful. – Andrew Barber Feb 08 '12 at 22:47
  • @AndrewBarber I agree, but the pure joy reading and even more so plus-one-ing the "No kidding!!" comment more than made up for this, em, answer. – Evgeniy Berezovsky Jun 01 '15 at 06:58