Home > database >  Debugging into MFC header code does not work with Visual Studio 2019
Debugging into MFC header code does not work with Visual Studio 2019

Time:12-02

TL;DR: Debuigging into MFC (CString) header code does not work on both my machines and as far as I can tell this is due to the peculiar way these headers are compiled.

Stepping through MFC header code when entered via disassembly works, but setting brealpoints does not work.

I'm looking for a workaround or at least acknowledgement of my analysis.


System:

  • Visual Studio 2019 Professional 16.9.6
  • Windows 10 / 1809 Enterprise LTSC

Setup: (I do apologize for this being rather long.)

  • Create a Visual Studio 2019 Example MFC Application Project (SDI App)

  • Make sure Enable Just My Codeis off under Options -> Debugging -> General.

  • Set the build configuration to Debug/x64 (does not make a difference, but let's all stay on the same page)

  • Navigate to MFCApplication1.cpp -> CMFCApplication1App::InitInstance()

  • Insert a CString init like this:

      ...
      InitCommonControlsEx(&InitCtrls);
    
      CWinAppEx::InitInstance(); // please put breakpoint 1 here
    
      // Add this line and set breakpoints
      CString this_is_text(L"Debugging into CString Header does not work!"); // breakpoint 2 here
    

Now, you can start the program under the debugger, and you should stop at the first breakpoint:

First breakpoint in InitInstance

Now, make sure all symbols are loaded, easiest done via the Call Stack:

Fully selected Call Stack with Context Menu and marked Load Symbols entry

Just select all lines in the call stack window and hit Load Symbols in the context menu. Afterwards the call stack should look roughly like this:

     >  MFCApplication1.exe!CMFCApplication1App::InitInstance() Line 75 C  
        mfc140ud.dll!AfxWinMain(HINSTANCE__ * hInstance=0x00007ff7b5070000, ...) Line 37    C  
        MFCApplication1.exe!wWinMain(HINSTANCE__ * hInstance=0x00007ff7b5070000, ...) Line 26   C  
        MFCApplication1.exe!invoke_main() Line 123  C  
        MFCApplication1.exe!__scrt_common_main_seh() Line 288   C  
        MFCApplication1.exe!__scrt_common_main() Line 331   C  
        MFCApplication1.exe!wWinMainCRTStartup(void * __formal=0x000000c2b7084000) Line 17  C  
        kernel32.dll!BaseThreadInitThunk()  Unknown
        ntdll.dll!RtlUserThreadStart()  Unknown

Now, you can try stepping-into (possibly F11) the CWinAppEx::InitInstance() function, which should work without a problem, landing you in mfc140ud.dll!CWinApp::InitInstance() Line 394 - this is OK.

Step out again, and then then try to step-into the CString ctor:

Breakpoint at CString ctor

This DOES NOT work on my machine(s)!

What I can do however, is (from the point above) switch to disassembly view, step into the calls there and get into the header code this way:

Disassembly step into MFC header copde

I can then successfully step through (but never into) the MFC header code. Trying to set a breakpoint will result in the error:

The breakpoint will not currently be hit. No executable code of the debugger's code type is associated with this line. Possible causes include ...

C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.28.29910\atlmfc\include\cstringt.h

Breakpoint error

And this is where I'm at.

Analysis:

What we can see from the MFC code is that we can step into "regular" cpp code, but as soon as we try to step into (or set breakpoint) code that is inside this CStringt.h it breaks.

Peculiar here: This is template header code, and still the executed code (as shown by the disassembly) is not in the user module but in the mfc###.dll! I think they do some clever tricks with the preprocessor (see defined(_MFC_DLL_BLD) and somesuch) which enables this multi use of the header file, and maybe, possibly this is also what breaks the debugger.

Question:

  • Is this a known problem, does this happen with all VS2019 installs, is there something peculiar to my setup?
  • Maybe fixed in a newer VS version?
  • Iff this is actually broken, what would be a useable workaround, other than constantly switching to disassembly view when into the MFC headers.

The most interesting answer here would actually be as to WHY this breaks - where does the debugger get confused? Is this a general problem with re-define-ing code when debugging library code?

CodePudding user response:

Analysis went sideways at some point, but we finally found one part of the problem here:

The Require source files to exactly match the original version option:

Debugging General Option

was the problem, but in a very peculiar way:

When you do NOT require source files to match (that is, disable this default option), then the erroneous behavior of the OP occurs: The debugger can no longer match the symbols to the cstringt.h file.

Unfortunately, I had this disabled on both machines. Pulling in a third machine showed that we could set breakpoints (though F11 still does not work) and by comparing the xml export of the VS settings we found that this was different.

So, long story short: For us, to be able to set breakpoints in the (unmodified!) MFC header, requires us to enable the Require source files to exactly match .. option.

If the option is disabled, which would imply a more lenient behavior by the debugger, it no longer works.

And, yes, we double checked it's always the same source file at C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.28.29910\atlmfc\include\cstringt.h

The mystery with step-into/F11 persists, but I guess this would better be taken to a separate question.

CodePudding user response:

The source shipped with MSVC does not match.

I think this happen, as DLLs got updated with Windows Update or a new vcredist, but Visual Studio includes are not updated. If you build with /MT or /MTd and link MFC statically, the problem does not persist.

Probably this can be reported to enter image description here

I know that works for c std:: library code debugging. The other technique I do, when I forget to uncheck this option, is similar to what you describe above.

  1. Set a breakpoint on the line of code I want to step into.
  2. When the breakpoint is reached, right click in the editor window and select "Go To Disassemly".
  3. In disassembly mode, step over until you get to a call statement. That's typically the std library. Eventually, you'll navigate into a mix of assembly and system code sources. You can flip out of disassembly mode by right-clicking again and selecting "go to source code".
  • Related