如何在不包含Windows.h的情况下获取DebugBreak的声明?

时间:2016-09-17 20:09:08

标签: c++ windows visual-studio assert

我们有一个C ++库。我们正在提供自定义断言并放弃Posix NDEBUGassert(下面的背景故事)。

断言在Windows下看起来像这样:

#  define CRYPTOPP_ASSERT(exp) {                                  \
    if (!(exp)) {                                                 \
      std::ostringstream oss;                                     \
      oss << "Assertion failed: " << (char*)(__FILE__) << "("     \
          << (int)(__LINE__) << "): " << (char*)(__FUNCTION__)    \
          << std::endl;                                           \
      std::cerr << oss.str();                                     \
      DebugBreak();                                               \
    }                                                             \
  }

我们遇到的问题是,我们必须包含<windows.h>,并且即使定义了WIN32_LEAN_AND_MEAN,也会带来很多额外的问题。一些额外的错误,如minmax,打破了C ++编译。事实上,测试我们的变化打破了我们。

我们查看<windows.h>搜索&#34;调试仅包括&#34;键入define但我们找不到它。我们还尝试根据Microsoft's docs on DebugBreak添加extern void WINAPI DebugBreak(void);,但由于重新定义的符号导致编译错误。

添加NO_MIN_MAX(我认为这是宏)不是一个选项,因为我们正在改变用户程序中的定义,当定义交叉授权我们的标题和用户代码。相关,请参阅Limiting Scope of #include Directives和朋友。

使用#pragma push_macro#pragma pop_macro不是一种选择,因为我们支持Microsoft编译器返回VC ++ 6.0。最早的pragma是VS2003。

由于休息时间,我们 想要加入<windows.h>。如何在不包含Windows.h的情况下获得DebugBreak的声明?

提前致谢。

这是一个简化案例:

// cl.exe /c assert.cpp

#include <algorithm>

// #include <windows.h>
// #ifndef WINAPI
// # define WINAPI __stdcall
// #endif
// extern void WINAPI DebugBreak(void);

#define MY_ASSERT(exp) { \
   if (!(exp)) {         \
     DebugBreak();       \
   }                     \
}

void dummy(int x1, int x2)
{
    MY_ASSERT(x1 == std::min(x1, x2));
}

这是使用我们的extern声明模拟用户程序时的错误。此测试发生在安装了VS2012和VS2013的Windows 8.1 x64上。我们还使用了开发人员命令提示符。

Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

assert.cpp
assert.cpp(11) : warning C4273: 'DebugBreak' : inconsistent dll linkage
        C:\Program Files (x86)\Windows Kits\8.1\include\um\debugapi.h(70) : see
previous definition of 'DebugBreak'
assert.cpp(21) : error C2589: '(' : illegal token on right side of '::'
assert.cpp(21) : error C2059: syntax error : '::'
assert.cpp(21) : error C2143: syntax error : missing ';' before '{'

当我们检查<debugapi.h>时,我们会看到:

WINBASEAPI
VOID
WINAPI
DebugBreak(
    VOID
    );

WINBASEAPI扩展为其他宏。我认为我们无法在所有平台上完成任务。

我们有一个跨平台的C ++安全库,它最近发现了CVE-2016-7420。如果断言被解雇,则归类为信息披露,因为潜在的数据丢失。当敏感数据被出口到文件系统(核心转储和崩溃报告)时会发生丢失;并出口到第三方(Apple通过CrashReporter,Ubuntu通过Apport,Microsoft通过Windows错误报告,开发人员等)。

断言从未在我们的生产/发布中解雇,因为我们的Makefile和我们的Visual Studio解决方案很好地配置了库。在生产/发布版本中,断言被删除,C ++ throw()处理错误情况。断言存在于调试/开发人员配置中,因此代码将自行调试,并使程序员免于执行任务。

在我们分析之后,我们意识到documenting "release/production builds must use -DNDEBUG" is an incomplete remediation。人们不会阅读文档;如果RTFM会起作用,那么它现在就会发生。此外,CMake不会定义它,Autotools不会定义它,Eclipse不会定义它,等等。我们实际上与CVE之前的位置相同。我们所做的就是在不降低风险的情况下推动责任。

3 个答案:

答案 0 :(得分:3)

你可以使用内在的,它的工作原理不包括:

__debugbreak();

答案 1 :(得分:2)

只需添加一个新的源代码文件,其中只包含:

#include <windows.h>

void MyDebugBreak(void)
{
   DebugBreak();
}

根据需要导出,并在宏中调用MyDebugBreak()而不是DebugBreak()。

您可以仅在Windows版本中包含该文件,也可以根据需要添加#if块。

答案 2 :(得分:1)

使用Visual Studio 6和2015声明DebugBreak似乎正常工作,前提是您使用__stdcallextern "C"声明它。由于VC ++ 6似乎在算法头中没有包含std::min,我已经修改了你的例子,但是如果第一个参数大于第二个参数,则在使用cl -nologo -W3 -O2 -Zi -NDEBUG -o assert.exe assert.cpp -link -subsystem:console -debug构建时会引发断言

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

extern "C" extern void __stdcall DebugBreak(void );

#define MY_ASSERT(exp) { \
   if (!(exp)) {         \
     DebugBreak();       \
   }                     \
}

void dummy(int x1, int x2)
{
    MY_ASSERT(x1 < x2);
}

int main(int argc, char *argv[])
{
    if (argc != 3) {
        fprintf(stderr, "usage: assert integer integer\n");
        exit(1);
    }
    int a = strtol(argv[1], NULL, 0);
    int b = strtol(argv[2], NULL, 0);
    dummy(a, b);
    return 0;
}