在Visual Studio中调试Qt应用程序并检查不透明对象(例如QDir或QFileInfo)

lan*_*oxx 5 c++ qt visual-studio-2013

我正在调试Visual Studio 2013中的Qt应用程序.我安装了Qt的官方Visual Studio插件.我可以看到内容QString,但对于任何其他QObject如a QDirQFileInfo对象,我无法看到它的内容.

我做错了什么或者这根本不可能?

当我展开一个QDir实例时,我只能看到一个名为的成员d_ptr,它引用了一个QDirPrivate我无法检查的对象.我还不能调用的其他功能QDirQFileInfo诸如path(),或filePath()在从调试器的运行时间.当我尝试Visual Studio声称该函数的地址已被优化.

无论如何调试这个而不向程序添加几十个日志语句?

cbu*_*art 6

UPDATE

在最后一个回答中详细说明这是不可能的,今天我找到了一种让调试器显示私有(隐藏)信息的方法.

注意:我保留下面的旧帖子,因为它暴露了我遵循的整个路径以达到此解决方案,我认为值得记录错误和错误的结论,所以没有其他人再次制作它们;).

假设你有一个QDir d;变量.您可以通过在Watch窗口中进行手动转换来检查其内部数据:

(Qt5Cored.dll!QDirPrivate*)d.d_ptr.d
Run Code Online (Sandbox Code Playgroud)

观看窗口

不幸的是,它没有显示任何有用的信息,因为您将面临内部成员的相同问题(私人数据未被打印).

但是,从这一点开始,您现在可以修改.natvis文件以在悬停时显示所需数据:

<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
  <Type Name="QDir">
    <DisplayString>path={((Qt5Cored.dll!QDirPrivate*)d_ptr.d)-&gt;dirEntry.m_filePath}</DisplayString>
  </Type>
</AutoVisualizer>
Run Code Online (Sandbox Code Playgroud)

完成了!

我无法在Watch窗口中打印此信息(如果我Expand在.natvis文件中添加了一个似乎被省略的部分),但至少你有关于调试时间的这些信息.

我建议您查看要检查的类型的私有头文件或Watch窗口(如答案开头所述),以便您可以将正确的数据添加到.natvis文件中.

以前的分析有什么问题?

我认为关键是手工铸造.我提到了那个

dirEntry是一部分QDirPrivate,所以很明显调试器无法检查它.

这是部分正确的.问题是调试器不知道在哪里找到QDirPrivate它,因为它不是Qt的导出符号; 一旦我们明确指出(通过(Qt5Cored.dll!QDirPrivate*)演员表)它就能够检查它.


原始答案

AFAIK对于那些课程是不可能的.不幸的是,有一些私有结构没有导出,似乎调试器无法查看.

注意:我正在使用Qt 5.6.1,所以我将使用qt.io存储库中的源代码来匹配我的示例.

我们QDir举个例子:它在qdir.h中定义,它是Qt的公共API的一部分.它声明在同一个文件中,QDirPrivate但它的完整定义是在qtdir_p.h中,它不是公共API的一部分.

即使加载了符号,这个类的定义仍然对调试器来说是模糊的.请参阅下面我使用的.natvis文件示例:

<?xml version="1.0" encoding="utf-8"?> 
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
  <Type Name="QDir">
    <DisplayString>Path: {d_func()-&gt;dirEntry.m_filePath}</DisplayString>
  </Type>
</AutoVisualizer>
Run Code Online (Sandbox Code Playgroud)

但调试器在尝试观察时显示以下内容QDir d(qApp->applicationDirPath());:

错误:类"QDirPrivate"没有成员"dirEntry"

评估'd_func() - > dirEntry.m_filePath'时出错

但是dirEntry 是其中的一部分QDirPrivate,因此显然调试器无法检查它.dirEntry是类型QFileSystemEntry,在qfilesystementry_p.h(另一个私有类)中定义.

最后,如果您查看Qt VS Tools 中的qt5.natvis文件并在源代码中找到相应的类,您将看到所有包含的类公开了.natvis文件使用的结构的定义.


UPDATE

这些类的唯一公共API是方法,遗憾的是,不支持从调试器调用函数.引用Microsoft员工MSDN论坛中的响应:

从调试器调用函数正在玩火.您可能会遇到跨线程依赖关系死锁(即使您没有任何显式的交叉线程依赖关系,也有内存分配等共享锁).这就是C++调试器不支持隐式funceval的原因.

实际上,如果您尝试调用QDir::absolutePath()监视窗口或.natvis文件中的任何方法,您将看到以下错误消息:

错误:函数QDir :: absolutePath没有地址,可能是由于编译器优化.


不是那么优雅的办法

一种可能的解决方案是使用存储这些私有值的包装类.您需要使用包装器替换每个对象,但可能有所帮助.

下面你会找到一个非常简单的例子QDir(你需要完成所需的构造函数并保存你想要的信息).请记住,Qt中的这类类型被设计为不可扩展,因此没有虚拟方法(因此请注意重新实现其中的一些并使用转换为基类的对象).

class MyQDir : public QDir {
public:
  MyQDir(const QString& path) : QDir(path) {
    m_absolutePath = absolutePath();
  }

private:
  QString m_absolutePath;
};
Run Code Online (Sandbox Code Playgroud)

并按照.natvis文件显示MyQDir悬停时的路径:

<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
  <Type Name="MyQDir">
    <DisplayString>Path: {m_absolutePath}</DisplayString>
  </Type>
</AutoVisualizer>
Run Code Online (Sandbox Code Playgroud)

最后,我认为剩下的唯一解决方案是将信息打印到console(qDebug()).


作为旁注,本教程将介绍如何编写自定义.natvis文件.这是2015年,但我已经完美地使用了2017年.我希望它也适用于2013年.

  • 我想到的只是手动和侵入您的代码.尽管如此,除了对.natvis限制的进一步分析外,我还更新了答案以包含其中一个解决方法.对不起,我对此无能为力.我遇到过这个问题太多次了,总是陷入困境,最后使用`qDebug()`. (2认同)

小智 6

我对 qt5.natvis 有以下扩展,它公开了 QDir、QFile 和 QFileInfo 的一些内容,建立在上面的原始建议的基础上:

<Type Name="QFileInfoPrivate">
    <DisplayString Condition="0 == this">&lt;null&gt;</DisplayString>
    <DisplayString>{fileEntry}</DisplayString>
    <StringView>fileEntry</StringView>
    <Expand>
        <Item Name="QSharedData">*((Qt5Cored.dll!QSharedData *) this)</Item>
        <Item Name="fileEntry">fileEntry</Item>
        <!--
        <Item Name="metaData">metaData</Item>
        <Item Name="fileListsInitialized">fileListsInitialized</Item>
        <Item Name="fileEngine">fileEngine</Item>
        <Item Name="fileNames">fileNames</Item>
        <Item Name="fileOwners">fileOwners</Item>
        <Item Name="cachedFlags">cachedFlags</Item>
        <Item Name="isDefaultConstructed">isDefaultConstructed</Item>
        <Item Name="cache_enabled">cache_enabled</Item>
        <Item Name="fileFlags">fileFlags</Item>
        <Item Name="fileSize">fileSize</Item>
        <Item Name="fileTimes">fileTimes</Item>
        -->
    </Expand>
</Type>


<Type Name="QFileInfo">
    <DisplayString>{*((Qt5Cored.dll!QFileInfoPrivate *) d_ptr.d)}</DisplayString>
    <StringView>*((Qt5Cored.dll!QFileInfoPrivate *) d_ptr.d)</StringView>
    <Expand>
        <Item Name="QFileInfoPrivate">*((Qt5Cored.dll!QFileInfoPrivate *) d_ptr.d)</Item>
    </Expand>
</Type>


<Type Name="QFileSystemEntry">
    <DisplayString Condition="0 == this">&lt;null&gt;</DisplayString>
    <DisplayString>{m_filePath}</DisplayString>
    <StringView>m_filePath</StringView>
    <!--
    <Expand>
        <Item Name="m_filePath">m_filePath</Item>
        <Item Name="m_nativeFilePath">m_nativeFilePath</Item>
        <Item Name="m_lastSeparator">m_lastSeparator</Item>
        <Item Name="m_firstDotInFileName">m_firstDotInFileName</Item>
        <Item Name="m_lastDotInFileName">m_lastDotInFileName</Item>
    </Expand>
    -->
</Type>


<Type Name="QDirPrivate">
    <DisplayString Condition="0 == this">&lt;null&gt;</DisplayString>
    <DisplayString>{dirEntry}</DisplayString>
    <StringView>dirEntry</StringView>
    <Expand>
        <Item Name="QSharedData">*((Qt5Cored.dll!QSharedData *) this)</Item>
        <Item Name="dirEntry">dirEntry</Item>
        <Item Name="nameFilters">nameFilters</Item>
        <Item Name="absoluteDirEntry">absoluteDirEntry</Item>
        <!--
        <Item Name="metaData">metaData</Item>
        <Item Name="fileListsInitialized">fileListsInitialized</Item>
        <Item Name="fileEngine">fileEngine</Item>
        <Item Name="files">files</Item>
        <Item Name="fileInfos">fileInfos</Item>
        <Item Name="sort">sort</Item>
        <Item Name="filters">filters</Item>
        -->
   </Expand>
</Type>


<Type Name="QDir">
    <DisplayString>{*((Qt5Cored.dll!QDirPrivate *) d_ptr.d)}</DisplayString>
    <StringView>*((Qt5Cored.dll!QDirPrivate *) d_ptr.d)</StringView>
    <Expand>
        <Item Name="QDirPrivate">*((Qt5Cored.dll!QDirPrivate *) d_ptr.d)</Item>
    </Expand>
</Type>


<Type Name="QFilePrivate">
    <DisplayString Condition="0 == this">&lt;null&gt;</DisplayString>
    <DisplayString>{fileName}</DisplayString>
    <StringView>fileName</StringView>
    <Expand>
        <Item Name="QFileDevice">*((Qt5Cored.dll!QFileDevice *) this)</Item>
        <Item Name="fileName">fileName</Item>
    </Expand>
</Type>


<Type Name="QFile">
    <DisplayString>{*((Qt5Cored.dll!QFilePrivate *) d_ptr.d)}</DisplayString>
    <StringView>*((Qt5Cored.dll!QFilePrivate *) d_ptr.d)</StringView>
    <Expand>
        <Item Name="QFilePrivate">*((Qt5Cored.dll!QFilePrivate *) d_ptr.d)</Item>
    </Expand>
</Type>
Run Code Online (Sandbox Code Playgroud)