2

This is related to How to install a DebugBreak handler? and How to get a declaration for DebugBreak without including Windows.h?. We would like to evaluate using IsDebuggerPresent() to avoid the crash when using DebugBreak() without a debugger present.

The problem we are having is, we have to include <windows.h>, and it brings in a lot of extra cruft, even with WIN32_LEAN_AND_MEAN defined. Some of the extra cruft, like min and max, breaks C++ compiles. In fact, testing our changes broke us.

Trying to do it with a simple extern BOOL IsDebuggerPresent() requires a lot of preprocessor magic and does not match signatures on all versions of the function. Considering we like to ensure "things just work" for 15 years of OS and compilers, it seems to be a thorny issue.

Is it possible to use IsDebuggerPresent() without including <windows.h>? If so, then how do we do it?

jww
  • 97,681
  • 90
  • 411
  • 885
  • 2
    exactly declaration is `extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();` and `and does not match signatures on all versions of the function` - the `IsDebuggerPresent` have only one signature and it invariant – RbMm May 28 '17 at 23:28
  • Thanks @RbMm, I'm testing it now... *"... have only one signature and it invariant"* - Microsoft declares it differently in their headers, depending on the platform. – jww May 28 '17 at 23:51
  • Thanks again @RbMm. The change tested good locally on VS2012/x86 and VS2015/x86. However, I'm seeing an [Appveyor hang](https://ci.appveyor.com/project/noloader/cryptopp/build/1.0.49/job/5kypk5xm0gm2bqlv) under their VS2015. I'll need more time to investigate it. – jww May 29 '17 at 00:08
  • 1
    `Microsoft declares it differently in their headers, depending on the platform` - you mistake. `extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();` is valid for all windows versions and platform – RbMm May 29 '17 at 00:16
  • I want to say that I love your hive mind even your profile description is from 3rd person. Anyway if you are developing on windows it is really good idea to make your project compile with windows.h even if you don't use it (don't include it just make sure that compilation will be ok if you do). It will protect you from many weird situations. – Logman May 29 '17 at 00:18
  • 2
    IIRC you disliked this approach, but I'm still of the opinion that the cleanest solution is to localize the Windows-specific stuff into separate modules, e.g., in this case you would have a MyIsDebuggerPresent function that does nothing but call IsDebuggerPresent. That module can then include without any other code being affected by it. But I think RbMm is right, I don't see why a simple declaration wouldn't work - unless you need to support compilers that don't support `__declspec` and/or `__stdcall` I guess. – Harry Johnston May 29 '17 at 00:25
  • ... out of curiosity, can you give a specific example of a different declaration for IsDebuggerPresent appearing in a Microsoft header? – Harry Johnston May 29 '17 at 00:27
  • You can `#define NOMINMAX` before including `` to suppress definition of the `min` and `max` macros. – 1201ProgramAlarm May 29 '17 at 00:38
  • @1201ProgramAlarm - We are a library. We try not to do those things because it could affect user programs. Defining `NOMINMAX` could break a user program since they may be depending on the `min` and `max` macros instead of the C++ templates. Plus, there are other ways to work around the min/max problem. – jww May 29 '17 at 00:42
  • @HarryJohnston - Its the second level of macros, where macros like `BOOL` and `WINAPI` start to take final values or are further replaced with macros. The desktops are mostly OK (IIRC), the problem is the Windows Kits for phones and tablets (IIRC). I'll dig up an example when I get into Windows Phone and Tablet testing, and legacy XP and Vista testing. I know the problem exists because I spent hours on it in the past. – jww May 29 '17 at 01:37
  • Looking at the headers, I think `BOOL` is OK, that's unconditionally typedef'd to `int`. I'm not quite as confident about `WINAPI`, although it looks to me as if it will be `__stdcall` for any version of Visual C++ that is generating Win32 code and regardless of whether it is a phone or a desktop build. YMMV. :-) – Harry Johnston May 29 '17 at 02:10
  • 1
    I work on a codebase that compiles for multiple mobile and desktop platforms. Isolating platform code into its own c++ files is really the only way to stay sane. – Chris Becke May 29 '17 at 05:47

1 Answers1

0

The code below is a runtime dubugger for windows that acts on the bare minimum without including windows.h itself. I know this doesn't answer the question directly but what you need to define specifically in order for your code to work is an architecture definition (preprocessor stuff), and <errhandlingapi.h>, and <windef.h>. I'm not completely sure what else so refer to my example below:

My code:

#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && defined(_M_IX86)
#define _X86_
#if !defined(_CHPE_X86_ARM64_) && defined(_M_HYBRID)
#define _CHPE_X86_ARM64_
#endif
#endif

#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && defined(_M_AMD64)
#define _AMD64_
#endif

#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && defined(_M_ARM)
#define _ARM_
#endif

#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && defined(_M_ARM64)
#define _ARM64_
#endif

#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && defined(_M_M68K)
#define _68K_
#endif

#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && defined(_M_MPPC)
#define _MPPC_
#endif

#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_M_IX86) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && defined(_M_IA64)
#if !defined(_IA64_)
#define _IA64_
#endif /* !_IA64_ */
#endif

#ifndef _MAC
#if defined(_68K_) || defined(_MPPC_)
#define _MAC
#endif
#endif
#include <string>
#include <windef.h>
#include <WinUser.h>
#include <WinNls.h>
#include <errhandlingapi.h>
#include <processthreadsapi.h>
#include <WinBase.h>

__pragma (comment(lib, "User32"));

namespace Microsoft {
void __stdcall rt_assert(const bool test, const wchar_t* FunctionName)
{
    if (test) return;

    // Retrieve the system error message for the last-error code
    unsigned __int32 ERROR_ID = GetLastError();
    void* MsgBuffer;
    LCID lcid; //language id
    GetLocaleInfoEx(L"en-US", LOCALE_RETURN_NUMBER | LOCALE_ILANGUAGE, (wchar_t*)&lcid, sizeof(lcid));

    //get error message and attach it to Msgbuffer
    FormatMessageW(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, ERROR_ID, lcid, (wchar_t*)&MsgBuffer, 0, NULL);

    //concatonate string to DisplayBuffer
    const std::wstring DisplayBuffer = (static_cast<std::wstring>(FunctionName) + L" failed with error " + std::to_wstring(ERROR_ID) + L": " + static_cast<const wchar_t*>(MsgBuffer)).c_str();

    // Display the error message and exit the process
    if (IsDebuggerPresent())
    {
        OutputDebugStringW(DisplayBuffer.c_str());
    }
    else
    {
        MessageBoxExW(NULL, DisplayBuffer.c_str(), L"Error", MB_ICONERROR | MB_OK, static_cast<unsigned __int16>(lcid));
    }
    ExitProcess(ERROR_ID);
}
}
Ram Koti
  • 2,203
  • 7
  • 26
  • 36