调试 MFC header 代码不适用于 Visual Studio 2019
Debugging into MFC header code does not work with Visual Studio 2019
TL;DR:将 调试到 MFC (CString
) header 代码在 我的机器,据我所知,这是由于这些 header 的特殊编译方式造成的。
通过反汇编输入时单步执行 MFC header 代码有效,但设置 brealpoints 无效。
我正在寻找解决方法或至少是对我的分析的认可。
系统:
- Visual Studio 2019 专业版 16.9.6
- Windows 10 / 1809 企业 LTSC
设置:(我很抱歉这太长了。)
创建一个Visual Studio 2019示例MFC应用程序项目(SDI App)
确保 Enable Just My Code
在选项 -> 调试 -> 常规下 关闭 。
将构建配置设置为 Debug/x64(没有区别,但让我们保持一致)
导航至 MFCApplication1.cpp
-> CMFCApplication1App::InitInstance()
像这样插入 CString 初始化:
...
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
现在,您可以在调试器下启动程序,您应该在第一个断点处停止:
现在,确保所有符号都已加载,最简单的方法是通过调用堆栈完成:
只是 select 调用堆栈中的所有行 window 并点击上下文菜单中的加载符号。之后调用堆栈应该大致如下所示:
> 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
现在,您可以尝试 stepping-into(可能是 F11)CWinAppEx::InitInstance()
功能,它应该 工作 没问题,让你进入 mfc140ud.dll!CWinApp::InitInstance() Line 394
- 没关系。
再次走出去,然后尝试 step-into CString
ctor:
这在我的机器上不起作用!
然而,我能做的是(从上面的观点)切换到反汇编视图,进入那里的 call
s 并以这种方式进入 header 代码:
然后我可以成功单步执行(但永远不会进入)MFC header 代码。尝试设置断点将导致错误:
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 Studio19\Professional\VC\Tools\MSVC.28.29910\atlmfc\include\cstringt.h
这就是我所在的位置。
分析:
我们从 MFC 代码中可以看出,我们 可以 进入“常规”cpp 代码,但是一旦我们尝试进入(或设置断点)代码在里面 CStringt.h
它坏了。
这里很特别:这是模板 header 代码,执行的代码(如反汇编所示)仍然不在用户模块中,而是在 mfc###.dll
!我认为他们对预处理器做了一些巧妙的技巧(参见 defined(_MFC_DLL_BLD)
等),这使得 header 文件可以被多次使用,也许这也是破坏调试器的原因。
问题:
- 这是一个已知问题吗,所有 VS2019 安装都会出现这种情况吗,我的设置有什么特别之处吗?
- 也许在较新的 VS 版本中已修复?
- 如果这真的坏了,除了在进入 MFC 时不断切换到反汇编视图之外,还有什么可用的解决方法 headers。
这里最有趣的答案实际上是关于为什么会中断 - 调试器在哪里混淆?这是调试库代码时重新define
-ing 代码的普遍问题吗?
取消选中“工具”->“选项”->“调试”中的“仅启用我的代码”选项
我知道这适用于 c++ std:: 库代码调试。当我忘记取消选中此选项时,我使用的另一种技术与您上面描述的类似。
- 在我要进入的代码行设置断点。
- 到达断点后,右键单击编辑器window和select“Go To Disassemly”。
- 在反汇编模式下,单步执行直到到达
call
语句。这通常是 std 库。最终,您将导航到汇编和系统代码源的混合体。您可以通过再次右键单击并selecting“转到源代码”来退出反汇编模式。
MSVC 附带的源不匹配。
我认为会发生这种情况,因为 DLL 使用 Windows 更新或新的 vcredist 进行了更新,但 Visual Studio 包含未更新。如果您使用 /MT
或 /MTd
和 link MFC 静态构建,问题不会持续存在。
如果您关心,可能可以将此报告给 http://developercommunity.visualstudio.com。
解决方法 1
执行@selbie 描述的步骤:
- Set a breakpoint on the line of code I want to step into.
- When
the breakpoint is reached, right click in the editor window and select
"Go To Disassemly".
- In disassembly mode, step over until you get to
a
call
statement. [...] You
can flip out of disassembly mode by right-clicking again and selecting
"go to source code".
(跳过与本期无关的部分)
然后手动拾取header的位置,调试器会告诉它不匹配。虽然差异似乎微不足道,所以 header 是可用的。
解决方法 2
Link MFC 静态,用/MT
或/MTd
编译
解决方法 3
ATL 有一个类似的 CString
没有遇到这个问题:
#include <atlbase.h>
#include <atlstr.h>
int main() {
ATL::CString this_is_text("Debugging into CString header works");
}
分析在某些时候出现了问题,但我们终于在这里找到了问题的一部分:
Require source files to exactly match the original version
选项:
是问题所在,但以一种非常奇特的方式:
当您不需要匹配源文件(即禁用此默认选项)时,OP 的错误行为就会发生:调试器无法再将符号与 cstringt.h
文件匹配。
不幸的是,我在两台机器上都禁用了这个。拉入第三台机器显示我们可以设置断点(尽管 F11 仍然不起作用)并且通过比较 VS 设置的 xml 导出我们发现这是不同的。
所以,长话短说:对于我们来说,要能够在(未修改的!)MFC header 中设置断点,需要我们启用 Require source files to exactly match ..
选项。
如果该选项被禁用,这意味着调试器会采取更宽松的行为,它将不再有效。
而且,是的,我们仔细检查了它始终是相同的源文件 C:\Program Files (x86)\Microsoft Visual Studio19\Professional\VC\Tools\MSVC.28.29910\atlmfc\include\cstringt.h
step-into/F11 的谜团仍然存在,但我想这最好单独提出一个问题。
TL;DR:将 调试到 MFC (CString
) header 代码在 我的机器,据我所知,这是由于这些 header 的特殊编译方式造成的。
通过反汇编输入时单步执行 MFC header 代码有效,但设置 brealpoints 无效。
我正在寻找解决方法或至少是对我的分析的认可。
系统:
- Visual Studio 2019 专业版 16.9.6
- Windows 10 / 1809 企业 LTSC
设置:(我很抱歉这太长了。)
创建一个Visual Studio 2019示例MFC应用程序项目(SDI App)
确保
Enable Just My Code
在选项 -> 调试 -> 常规下 关闭 。将构建配置设置为 Debug/x64(没有区别,但让我们保持一致)
导航至
MFCApplication1.cpp
->CMFCApplication1App::InitInstance()
像这样插入 CString 初始化:
... 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
现在,您可以在调试器下启动程序,您应该在第一个断点处停止:
现在,确保所有符号都已加载,最简单的方法是通过调用堆栈完成:
只是 select 调用堆栈中的所有行 window 并点击上下文菜单中的加载符号。之后调用堆栈应该大致如下所示:
> 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
现在,您可以尝试 stepping-into(可能是 F11)CWinAppEx::InitInstance()
功能,它应该 工作 没问题,让你进入 mfc140ud.dll!CWinApp::InitInstance() Line 394
- 没关系。
再次走出去,然后尝试 step-into CString
ctor:
这在我的机器上不起作用!
然而,我能做的是(从上面的观点)切换到反汇编视图,进入那里的 call
s 并以这种方式进入 header 代码:
然后我可以成功单步执行(但永远不会进入)MFC header 代码。尝试设置断点将导致错误:
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 Studio19\Professional\VC\Tools\MSVC.28.29910\atlmfc\include\cstringt.h
这就是我所在的位置。
分析:
我们从 MFC 代码中可以看出,我们 可以 进入“常规”cpp 代码,但是一旦我们尝试进入(或设置断点)代码在里面 CStringt.h
它坏了。
这里很特别:这是模板 header 代码,执行的代码(如反汇编所示)仍然不在用户模块中,而是在 mfc###.dll
!我认为他们对预处理器做了一些巧妙的技巧(参见 defined(_MFC_DLL_BLD)
等),这使得 header 文件可以被多次使用,也许这也是破坏调试器的原因。
问题:
- 这是一个已知问题吗,所有 VS2019 安装都会出现这种情况吗,我的设置有什么特别之处吗?
- 也许在较新的 VS 版本中已修复?
- 如果这真的坏了,除了在进入 MFC 时不断切换到反汇编视图之外,还有什么可用的解决方法 headers。
这里最有趣的答案实际上是关于为什么会中断 - 调试器在哪里混淆?这是调试库代码时重新define
-ing 代码的普遍问题吗?
取消选中“工具”->“选项”->“调试”中的“仅启用我的代码”选项
我知道这适用于 c++ std:: 库代码调试。当我忘记取消选中此选项时,我使用的另一种技术与您上面描述的类似。
- 在我要进入的代码行设置断点。
- 到达断点后,右键单击编辑器window和select“Go To Disassemly”。
- 在反汇编模式下,单步执行直到到达
call
语句。这通常是 std 库。最终,您将导航到汇编和系统代码源的混合体。您可以通过再次右键单击并selecting“转到源代码”来退出反汇编模式。
MSVC 附带的源不匹配。
我认为会发生这种情况,因为 DLL 使用 Windows 更新或新的 vcredist 进行了更新,但 Visual Studio 包含未更新。如果您使用 /MT
或 /MTd
和 link MFC 静态构建,问题不会持续存在。
如果您关心,可能可以将此报告给 http://developercommunity.visualstudio.com。
解决方法 1
执行@selbie 描述的步骤:
- Set a breakpoint on the line of code I want to step into.
- When the breakpoint is reached, right click in the editor window and select "Go To Disassemly".
- In disassembly mode, step over until you get to a
call
statement. [...] You can flip out of disassembly mode by right-clicking again and selecting "go to source code".
(跳过与本期无关的部分)
然后手动拾取header的位置,调试器会告诉它不匹配。虽然差异似乎微不足道,所以 header 是可用的。
解决方法 2
Link MFC 静态,用/MT
或/MTd
解决方法 3
ATL 有一个类似的 CString
没有遇到这个问题:
#include <atlbase.h>
#include <atlstr.h>
int main() {
ATL::CString this_is_text("Debugging into CString header works");
}
分析在某些时候出现了问题,但我们终于在这里找到了问题的一部分:
Require source files to exactly match the original version
选项:
是问题所在,但以一种非常奇特的方式:
当您不需要匹配源文件(即禁用此默认选项)时,OP 的错误行为就会发生:调试器无法再将符号与 cstringt.h
文件匹配。
不幸的是,我在两台机器上都禁用了这个。拉入第三台机器显示我们可以设置断点(尽管 F11 仍然不起作用)并且通过比较 VS 设置的 xml 导出我们发现这是不同的。
所以,长话短说:对于我们来说,要能够在(未修改的!)MFC header 中设置断点,需要我们启用 Require source files to exactly match ..
选项。
如果该选项被禁用,这意味着调试器会采取更宽松的行为,它将不再有效。
而且,是的,我们仔细检查了它始终是相同的源文件 C:\Program Files (x86)\Microsoft Visual Studio19\Professional\VC\Tools\MSVC.28.29910\atlmfc\include\cstringt.h
step-into/F11 的谜团仍然存在,但我想这最好单独提出一个问题。