1

我正在将 C++/ WTL项目从 Visual Studio 2005 移植到 VS 2008。项目配置之一是单元测试构建,它定义了预处理器符号 UNIT_TEST。

为了让我的 WTL 类进入我的测试工具,我创建了一个CFakeWindow存根所有 CWindow 方法的类。然后在我的 stdafx.h 文件中,我在 atlwin.h(它定义了 CWindow 类)的导入下方执行此操作:

#ifdef UNIT_TEST
#include "fakewindow.h"
#define CWindow CFakeWindow
#endif

我的窗口类看起来像这样:

class CAboutDialog : 
    public CDialogImpl< CAboutDialog, CWindow > 
    , public CDialogResize< CAboutDialog >
{
// class definition omitted...
};

这在 VS 2005 中效果很好。问题是在 VS 2008 中,来自原始 CWindow 类的方法被调用,而不是 CFakeWindow 类。这当然会导致测试失败,CWindow因为ATLASSERT(::IsWindow(m_hWnd)).

当我在调试器中单步执行代码时,我看到 CAboutDialog 类继承自CDialogImpl<CAboutDialog, CFakeWindow>. 然而,当我在CAboutDialog(例如EndDialog(code))上调用一个方法时,该CWindow方法被调用了。

这是 VS 2008 中的错误,还是我的条件模板继承技术是 VS 2005 允许但 VS 2008“修复”的可憎?是否有解决方法,或者我是否需要考虑一种不同的技术来对 WTL 类进行单元测试?我真的很喜欢这种技术,因为它让我可以将 WTL 类放入测试工具中,而无需与 WTL 库打交道。

编辑:正如下面对 Conal 的响应中所述,预处理器输出显示我的类是从 CFakeWindow 继承的:

class CAboutDialog :
    public CDialogImpl<CAboutDialog, CFakeWindow >
    , public CDialogResize< CAboutDialog >
    ...

如上所述,当我在调试器中单步执行代码时,CAboutDialog 在本地窗口中显示为继承自 CFakeWindow。

编辑 2:根据 Conal 的建议,我逐步完成了反汇编,代码应该调用 CFakeWindow 方法,但实际调用的是 CWindow 方法。

        if ( wID == IDCANCEL ) 
00434898  movzx       edx,word ptr [ebp+8] 
0043489C  cmp         edx,2 
0043489F  jne         CAboutDialog::OnCloseCmd+90h (4348B0h) 
        {
            EndDialog( wID ) ;
004348A1  movzx       eax,word ptr [ebp+8] 
004348A5  push        eax  
004348A6  mov         ecx,dword ptr [ebp-10h] 
004348A9  call        ATL::CDialogImpl<CAboutDialog,ATL::CFakeWindow>::EndDialog (40D102h) 
        }
        else
004348AE  jmp         CAboutDialog::OnCloseCmd+9Ah (4348BAh) 
        {
            EndDialog(IDOK);
004348B0  push        1    
004348B2  mov         ecx,dword ptr [ebp-10h] 
004348B5  call        ATL::CDialogImpl<CAboutDialog,ATL::CFakeWindow>::EndDialog (40D102h) 

我开始更倾向于 VC++ 2008 调试器中的错误。

4

6 回答 6

4

这可能是一种更清晰的处理方式:

#ifdef UNIT_TEST
#include "fakewindow.h"
#define TWindow CFakeWindow
#else
#define TWindow CWindow
#endif

也许有一种情况是重新定义没有通过预编译的头文件。如果是这样,这将解决任何此类问题。

于 2009-11-11T17:49:14.957 回答
2

我认为这是一个长镜头,但是预编译的标头是否可能对您的构建造成严重破坏?尝试在您的项目中关闭它们(如果它们已打开)并进行干净的构建以查看是否有更好的行为。

于 2009-11-10T08:17:58.340 回答
1

您是否查看过预处理器的输出以确认您的 hack 确实在按照您的想法进行?使用预处理器重命名模板库中的类是充满危险的。还要检查 CFakeWindow 中每个方法的签名是否与 CWindow 完全匹配。

VS2008 已经存在了很长时间,所以在您首先消除所有其他可能性之前,我不会考虑编译器错误。

于 2009-10-25T08:47:31.210 回答
1

尝试类似:

class CAboutDialog :
    public CDialogImpl<CAboutDialog, FAKE_CWindow_MACRO >

这样您就可以停止尝试使用预处理器来重写系统头文件,并将其用于您自己的代码。

是的,我知道你必须“重构”你的代码,但这是一项搜索和替换的工作。

您当前的技术有点像 hack,因为它做出了一个毫无根据的假设:没有源文件将 #include 来自 ATL 的任何头文件,它依赖于 CWindow 的一致定义。这不太可能是稳定的,因为代码会随着时间的推移而变化。

于 2009-11-11T17:39:39.320 回答
1

然后在我的 stdafx.h 文件中,我这样做

AFAIK,stdafx.h 默认是预编译头文件。因此,当您定义UNIT_TEST宏时,它不会影响预编译文件,它仍然具有CWindow模板参数。

于 2009-11-11T17:46:08.240 回答
1

我看到库符号位于命名空间 ATL 中。这可能会摆脱你的宏观诡计吗?

一般来说,我不会指望任何调试器完美地报告模板实例化。即使在调试版本中也需要进行太多奇怪的优化。

于 2009-11-12T15:40:13.013 回答