从本机到托管转换后,Visual Studio 调试器值错误

Lau*_*ent 5 c++ debugging c++-cli visual-studio visual-studio-debugging

一般说明

我在调试 Visual Studio 项目时遇到问题。该项目有一个大型的中心类“MachineControl”。它还将当前所处的状态存储为枚举类的值。在尝试修复不相关的错误时,我设置了一些条件断点来解决它。经过一些令人困惑的情况后,我意识到从调试器获得的信息是错误的。代码运行时的值与调试器显示的值不同。

mState 在鼠标悬停时显示错误值

这个值是错误的,因为当我单步执行代码时,它实际上表现得好像处于 StateEnum::Running(1) 状态。

IDE 和代码信息

在与调试器相关的所有内容中发现错误信息:

  • 通过调试器鼠标悬停显示
  • 用于条件断点
  • 汽车
  • 当地人
  • 手表
  • 《即时之窗》
  • 内存视图

正确信息:

  • std::cout
  • 代码行为
  • 记录

环境:

  • 微软 Visual Studio 专业版 2019,16.9.0

代码:

  • MachineControl 类中的单线程本机代码
  • 托管 C++ GUI 项目围绕本机 C++ 静态库调用托管模拟包装器 DLL(这解释了后面提到的“托管到本机”转换)

代码示例

错误的调试信息发生在难以隔离的点。可悲的是,我无法生成一个最小的可重复示例。以下代码片段是我必须显示问题发生的一般情况的最简单的说明性代码:

class MachineControl : public EventReceiver <Evt>
{
    StateEnum mState;
public:
    void onEventReceived(const Evt& evt) override;
    void processPeriodicTimer();
    //...
};

void MachineControl::onEventReceived(const Evt& event) // called by other class through managed to native transition
{
    switch (mState) // shows wrong information
    {
    case StateEnum::Running:
        processRunning(event); // <-- code jumps here, even though mState is not shown to be in StateEnum::Running
        break;
   
    // handle other cases
    }
    //...
}

void MachineControl::processPeriodicTimer() // called from inside
{
    switch (mState) // shows correct information
    {
        // ...
    }
   //...
}
// a thousand more lines of code before and after this
Run Code Online (Sandbox Code Playgroud)

观察结果

  • StateEnum 的值最多定义为 18,但高于该值的值会显示在调试信息中。错误的显示值不遵循我所识别的任何模式
  • MachineControl 类包含许多“switch(mState)”实例。仔细查看所有这些,错误似乎恰好发生在调用堆栈在其下方显示 [Native to Managed Transition],然后是 [Managed to Native Transition],然后是其他一些类时。

调用堆栈图

上图展示了显示错误调试信息时的调用堆栈。

我尝试和/或验证的不成功的事情

  • 项目在调试配置中运行(在类似的 StackOverflow 帖子中提到)
  • 禁用优化(/Od),我在网上找到的很多文章中都提到过
  • 使用标志 /clr 而不是 /clr:pure,如相关问题的答案中所述:Visual Studio 调试器显示本机类型的错误值
  • 调试信息格式为“程序数据库”(/Zi)。切换到不同的格式并不能解决问题
  • 结构成员对齐在所有项目中都是相同的。在线搜索类似问题显示了一种情况,这种对齐可能是原因。我检查了所有相关的项目设置。每个“#pragma pack(1)”后面都跟着一个“#pragma pack()”
  • 没有其他名为“mState”的变量(在Visual Studio 中提到在调试时显示错误值?
  • 以下设置已关闭:“仅启用我的代码”、“使用托管兼容模式”

如何在本地解决问题

  • 在 switch 语句之前添加“auto tempState = mState”。tempState 变量然后显示正确的值,而 mState 仍然是错误的。
  • 添加间接:
void MachineControl::onEventReceived(const Evt& event)
{
    usessIndirection(event);
}

void MachineControl::uselessIndirection(const Evt& event)
{
    switch (mState)
    {
        //...
    }
    //...
}
Run Code Online (Sandbox Code Playgroud)

这两个“解决方案”并不令人满意,因为我不想到处添加局部变量和不必要的函数调用,只是为了使调试按预期工作。似乎有一个根本问题需要引起注意。

这个问题确实扰乱了我的调试工作,我很高兴听到任何建议或后续问题。谢谢!