Search code examples
c#visual-studio-debuggingmixed-mode

Why can't I step into a C++ function from C# in mixed-mode debugging?


I have a C# application that uses an external C++ DLL, and for some reason, I can't step into its functions anymore in Visual Studio. I used to be able to, but now, every time I try, I end up in a disassembly inside NTDLL instead of in my C++ function. If I even put a call to DebugBreak() inside the C++ code, I get a bizarre error message like "a breakpoint has been hit that the CLR cannot handle."

I have "Enable native code debugging" turned on in the C# application's debug profile, and I've turned off "Hot Reload" and "Edit and Continue," which sometimes cause trouble for native code debugging, but I still can't step into the C++ code.

Here's my function declaration on the C# side:

[DllImport("Foo.dll"), SuppressGCTransition]
public static extern unsafe void DoSomething();

and here it is on the C++ side:

__declspec(dllexport) void DoSomething()
{
    ...
}

Why can't I step into this function? I'm using Visual Studio 2022, 17.5.3.


Solution

  • The most likely reason why mixed-mode debugging might not work in this case: The [DllImport] attribute you've applied in C# to the imported C/C++ function declares SuppressGCTransition. That may make the call considerably faster, especially if it's a short call, but it also can really freak out the debugger. So if you have something like this in C#:

    [DllImport("Foo.dll"), SuppressGCTransition]
    public static extern unsafe void DoSomething();
    

    You'll need to change it to the form below, or none of the breakpoints inside DoSomething() will ever be hit, not even calls to DebugBreak().

    [DllImport("Foo.dll")]
    public static extern unsafe void DoSomething();
    

    Microsoft states plainly that SuppressGCTransition breaks mixed-mode debugging, but it's easy to forget that fact when you're trying to track down why breakpoints aren't working: There have been enough odd glitches in mixed-mode debugging over the years that it's easy to blame Visual Studio when the fault, is, in fact, your own.

    (I wasted a lot more time today than I ought to have wasted on this exact issue, so hopefully this answer will help someone avoid wasting that time in the future!)