我正在调试Visual Studio 2013中的Qt应用程序。我安装了Qt的官方Visual Studio插件。我可以看到QString
的内容,但对于任何其他QObject
,例如QDir
或QFileInfo
对象,我无法看到其内容。
我做错了什么或者这根本不可能?
当我展开QDir
实例时,我只能看到一个名为d_ptr
的成员,该成员引用了我无法检查的QDirPrivate
对象。我也无法在运行时从调试器调用QDir
或QFileInfo
的其他函数,例如path()
或filePath()
。当我尝试Visual Studio声称函数的地址已被优化时。
无论如何调试这个而不向程序添加几十个日志语句?
答案 0 :(得分:5)
<强>更新强>
在最后一个回答中详细说明了这是不可能的,今天我找到了一种让调试器显示隐私(隐藏)信息的方法。
注意:我保留下面的旧帖子,因为它暴露了我为达到这个解决方案而遵循的整个路径,我认为值得追踪错误和错误的结论,所以没有人别的再做一次;)。
假设您有一个QDir d;
变量。您可以通过在Watch窗口中进行手动转换来检查其内部数据:
(Qt5Cored.dll!QDirPrivate*)d.d_ptr.d
不幸的是,它没有显示任何有用的信息,因为您将面临内部成员的相同问题(私人数据未被打印)。
但是,从这一点开始,您现在可以修改.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)->dirEntry.m_filePath}</DisplayString>
</Type>
</AutoVisualizer>
我还没有能够在Watch窗口中打印此信息(如果我在.natvis文件中添加Expand
部分,似乎省略了),但至少你有这方面的信息调试时间。
我建议您查看要检查的类型的私有头文件或Watch窗口(如答案开头所述),这样您就可以将正确的数据添加到.natvis文件中。 / p>
之前的分析有什么问题?
我认为关键点在于手动铸造。我提到了
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()->dirEntry.m_filePath}</DisplayString>
</Type>
</AutoVisualizer>
但调试器在尝试观看QDir d(qApp->applicationDirPath());
时显示以下内容:
错误:课程&#34; QDirPrivate&#34;没有会员&#34; dirEntry&#34;
评估&#39; d_func()时出错 - &gt; dirEntry.m_filePath&#39;
但dirEntry
是 QDirPrivate
的一部分,因此显然调试器无法检查它。 dirEntry
的类型为QFileSystemEntry
,在qfilesystementry_p.h(另一个私有类)中定义。
最后,如果您查看Qt VS Tools中的qt5.natvis文件并在源代码中找到相应的类,您将看到所有包含的类公开了所使用的结构的定义。 .natvis文件。
<强>更新强>
此类的唯一公共API是方法,遗憾的是,不支持从调试器调用函数。引用MSDN forum from a Microsoft Staff中的回复:
从调试器调用函数正在玩火。您可能会遇到跨线程依赖关系死锁(即使您没有任何明确的跨线程依赖关系,也有内存分配等共享锁)。这就是C ++调试器不支持隐式funceval的原因。
实际上,如果您尝试在监视窗口或.natvis文件中调用QDir::absolutePath()
之类的任何方法,您将看到以下错误消息:
错误:函数QDir :: absolutePath没有地址,可能是由于编译器优化。
不是优雅的解决方法
一种可能的解决方案是使用存储这些私有值的包装类。您需要使用包装器替换每个对象,但可能有所帮助。
在下面您将找到QDir
的一个非常简单的示例(您需要完成所需的构造函数并保存所需的信息)。请记住,Qt中的这类类型被设计为不可扩展,因此没有虚拟方法(因此请注意重新实现其中的一些并使用转换为基类的对象)。
class MyQDir : public QDir {
public:
MyQDir(const QString& path) : QDir(path) {
m_absolutePath = absolutePath();
}
private:
QString m_absolutePath;
};
并关注.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>
最后,我认为唯一的解决方案是将信息打印到控制台(qDebug()
)。
作为旁注,this tutorial解释了如何编写自定义.natvis文件。这是2015年,但我在2017年完美地使用它。我希望它也适用于2013年。
答案 1 :(得分:2)
我对qt5.natvis进行了以下扩展,它基于上面的原始建议公开了QDir,QFile和QFileInfo的某些内容:
<Type Name="QFileInfoPrivate">
<DisplayString Condition="0 == this"><null></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"><null></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"><null></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"><null></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>