//////////////////////////////////////////////////////////////////////
// MessageBoxThread.h
//
#pragma once
// All Windows assertions have a problem:
// They all end up calling the Win32 API MessageBox(). The problem with MessageBox is that it has a message pump that will
// continue processing windows messages for the thread it was called on.
// This can lead to recursive assertions if a windows message handler also asserts.
//
// I have rewritten these macros so that the MessageBox is on its own thread. Now, the calling thread will actually, completely halt.
// All you need to do is #include this header file, and it will replace the standard assertions with these threaded ones.
//
// You can also call the functions directly if you want to do your own error handling or put up a different message type
// as in the code below
//
// CMessageBoxThread::DebugReport(_CRT_WARN, _T(__FILE__), __LINE__, _T(__FUNCTION__), msg);
//
// CMessageBoxThread::MessageBox will put up a standard MessageBox, but also in its own thread so the calling thread will be completely halted.
//
// CMessageBoxThread::MessageBox("The main thread is really halted", "Alert!", MB_ICONEXCLAMATION | MB_OK);
//
// Written by Curt Dixon (curtd2004-code@yahoo.com)
//
// This code is provided under the The Code Project Open License (CPOL)
#include <crtdbg.h>
#include <stdlib.h>
#include <stdarg.h>
#if _DEBUG
#ifdef ATLASSERT
#undef ATLASSERT
#endif
#ifdef ASSERT
#undef ASSERT
#endif
#ifdef assert
#undef assert
#endif
#ifdef _ASSERT_EXPR
#undef _ASSERT_EXPR
#endif
// replacing _RPT_BASE has the effect of replacing all the _RPTn() macros
#undef _RPT_BASE
#define _RPT_BASE(args) \
(void) ((1 != CMessageBoxThread::DebugReport args) || (__debugbreak(), 0))
#undef _RPT_BASE_W
#define _RPT_BASE_W(args) \
(void) ((1 != CMessageBoxThread::DebugReport args) || (__debugbreak(), 0))
// !! avoids problems with overloaded operator||()
#define _ASSERT_EXPR(exp, msg) \
(void)(!!(exp) || (CMessageBoxThread::DebugReport(_CRT_ASSERT, _T(__FILE__), __LINE__, _T(__FUNCTION__), msg) != 1) || (__debugbreak(), 0))
#define _ASSERTE(expr) _ASSERT_EXPR((expr), _CRT_WIDE(#expr))
#define ASSERT(expr) _ASSERT_EXPR((expr), NULL)
#define assert(expr) _ASSERT_EXPR((expr), NULL)
#ifdef _UNICODE
#define _CrtDbgReport _CrtDbgReportW
#endif
#else // _DEBUG
#ifndef ATLASSERT
#define ATLASSERT
#endif
#ifndef ASSERT
#define ASSERT
#endif
#ifndef assert
#define assert
#endif
#endif
class CMessageBoxThread
{
public:
// parameters for _CrtDbgReport that are passed between threads
struct _DEBUG_DATA
{
int nRptType; // _CrtDbgReport type (_CRT_WARN, _CRT_ERROR, or _CRT_ASSERT)
LPCTSTR pszFile; // File name
int nLine; // Line #
LPCTSTR pszModule; // Module name
TCHAR msgbuf[1024]; // make this as large as you need
};
// parameters for MessageBox that are passed between threads
struct _MSG_DATA
{
HWND hWnd;
LPCTSTR lpText;
LPCTSTR lpCaption;
UINT uType;
};
// this is the thread routine for an assertion (or other _CrtDbgReport type)
static DWORD WINAPI CrtDbgReportThread(LPVOID lpParameter)
{
return _CrtDbgReport(((_DEBUG_DATA*)lpParameter)->nRptType,
((_DEBUG_DATA*)lpParameter)->pszFile,
((_DEBUG_DATA*)lpParameter)->nLine,
((_DEBUG_DATA*)lpParameter)->pszModule,
_T("%s"),
((_DEBUG_DATA*)lpParameter)->msgbuf);
}
// this is the thread routine for a general purpose MessageBox
static DWORD WINAPI MessageBoxThread(LPVOID lpParameter)
{
return ::MessageBox(((_MSG_DATA*)lpParameter)->hWnd,
((_MSG_DATA*)lpParameter)->lpText,
((_MSG_DATA*)lpParameter)->lpCaption,
((_MSG_DATA*)lpParameter)->uType);
}
// creates the message box thread and waits for it to close
static DWORD CreateThread(LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter)
{
// create the MessageBoxThread and pass it the msg data
HANDLE hThread = ::CreateThread(NULL, 0, lpStartAddress, lpParameter, 0, NULL);
::WaitForSingleObject(hThread, INFINITE); // calling thread is now REALLY frozen right here
DWORD dwExitCode = 0;
GetExitCodeThread(hThread, &dwExitCode);
CloseHandle(hThread);
return dwExitCode;
}
// This function returns the results of the _CrtDbgReport() call.
// -1 if errors occurred or 0 if no errors are encountered.
// However, when the report destination is a debug message dialog and the user clicks the Retry button,
// dwExitCode = 1. If the user clicks the Abort button in the debug message dialog,
// _CrtDbgReport immediately aborts the application.
static DWORD DebugReport(int nRptType, LPCTSTR szFile, int nLine, LPCTSTR szModule, LPCTSTR szFormat, ...)
{
_DEBUG_DATA data;
data.nRptType = nRptType;
data.pszFile = szFile;
data.nLine = nLine;
data.pszModule = szModule;
va_list arglist;
va_start(arglist, szFormat);
memset(data.msgbuf, 0, sizeof(data.msgbuf));
if (szFormat)
_vstprintf_s(data.msgbuf, sizeof(data.msgbuf) / sizeof(*data.msgbuf), szFormat, arglist);
// create the CrtDbgReportThread and pass it the msg data
return CreateThread(&CrtDbgReportThread, (LPVOID)&data);
}
// This method returns the return code from the ::MessageBox() call
static DWORD MessageBox(LPCTSTR pText, LPCTSTR pCaption = NULL, UINT uType = MB_OK, HWND hWnd = NULL)
{
_MSG_DATA data;
data.hWnd = hWnd;
data.lpCaption = pCaption;
data.lpText = pText;
data.uType = uType;
// create the MessageBoxThread and pass it the msg data
return CreateThread(&MessageBoxThread, (LPVOID)&data);
}
};