Fix exception handler to obey UAC on windows machines.

It will write the crash report file to CSIDL_PERSONAL (c:\Users\user name\Documents\logs\warzone2100.rpt)

*All* platforms will now always have --debug-file enable as a default. This will write the logs to <configdir>/logs/WZlog*.txt

When a user overrides the configdir, then we will try to use that directory instead of the default as well.

refs ticket:1759

2.3: r10532


git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/trunk@10538 4a71c877-e1ca-e34f-864e-861f7616d084
master
Buginator 2010-04-10 20:44:20 +00:00 committed by Git SVN Gateway
parent 43b6c72448
commit 1c4c1cc5bd
10 changed files with 168 additions and 41 deletions

View File

@ -72,7 +72,7 @@ case ${host_os} in
*mingw32*)
host_os_mingw32=yes
AC_CHECK_TOOL([WINDRES], [windres], AC_MSG_ERROR([windres not found]))
WIN32_LIBS='-ldbghelp -lshfolder -lwinmm -lws2_32'
WIN32_LIBS='-ldbghelp -lshfolder -lshlwapi -lpsapi -lshell32 -lwinmm -lws2_32'
AC_SUBST([WIN32_LIBS], [${WIN32_LIBS}])
;;
*openbsd*)

View File

@ -212,7 +212,7 @@ static std::string getProgramPath(const char* programCommand)
}
else
{
debug(LOG_WARNING, "Could not retrieve full path to %s, will not create extended backtrace", programCommand);
debug(LOG_INFO, "Could not retrieve full path to %s, will not create extended backtrace", programCommand);
}
return programPath;

View File

@ -23,6 +23,9 @@
#include "dumpinfo.h"
#if defined(WZ_OS_WIN)
#include <tchar.h>
#include <shlobj.h>
#include <shlwapi.h>
# include "dbghelp.h"
# include "exchndl.h"
@ -33,6 +36,7 @@ static LPTOP_LEVEL_EXCEPTION_FILTER prevExceptionHandler = NULL;
/**
* Exception handling on Windows.
* Ask the user whether he wants to safe a Minidump and then dump it into the temp directory.
* NOTE: This is only for MSVC compiled programs.
*
* \param pExceptionInfo Information on the exception, passed from Windows
* \return whether further exception handlers (i.e. the Windows internal one) should be invoked
@ -703,3 +707,43 @@ void setupExceptionHandler(int argc, char * argv[])
setFatalSignalHandler(posixExceptionHandler);
#endif // WZ_OS_*
}
bool OverrideRPTDirectory(char *newPath)
{
# if defined(WZ_CC_MINGW)
TCHAR buf[MAX_PATH];
if (!MultiByteToWideChar(CP_UTF8, 0, newPath, strlen(newPath), buf, 0))
{
//conversion failed-- we won't use the user's directory.
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
TCHAR szBuffer[4196];
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
wsprintf(szBuffer, _T("Exception handler failed setting new directory with error %d: %s\n"), dw, lpMsgBuf);
MessageBox(MB_ICONEXCLAMATION, szBuffer, _T("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
return false;
}
_tcscpy(buf, newPath);
PathRemoveFileSpec(buf);
_tcscat(buf, _T("\\logs\\")); // stuff it in the logs directory
_tcscat(buf, _T("Warzone2100.RPT"));
ResetRPTDirectory(buf);
#endif
return true;
}

View File

@ -21,5 +21,5 @@
#define __INCLUDED_LIB_EXCEPTIONHANDLER_EXCEPTIONHANDLER_H__
extern void setupExceptionHandler(int argc, char * argv[]);
extern bool OverrideRPTDirectory(char *newPath);
#endif // __INCLUDED_LIB_EXCEPTIONHANDLER_EXCEPTIONHANDLER_H__

View File

@ -19,7 +19,10 @@
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if (_WIN32_WINNT < 0x0500) // must force win 2k or higher
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0500
#endif
#include "lib/framework/frame.h"
#include "dumpinfo.h"
#include "exchndl.h"
@ -32,7 +35,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <shlobj.h>
#include <shlwapi.h>
#if !defined(WZ_CC_MSVC)
#define HAVE_BFD 1
@ -1072,7 +1076,7 @@ void GenerateExceptionReport(PEXCEPTION_POINTERS pExceptionInfo)
rprintf(_T("\r\n\r\n"));
#endif
// FIXME: We *never* return from the below call!
StackBackTrace(GetCurrentProcess(), GetCurrentThread(), pContext);
rprintf(_T("\r\n\r\n"));
@ -1106,6 +1110,32 @@ LONG WINAPI TopLevelExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
FILE_FLAG_WRITE_THROUGH,
0
);
if (hReportFile == INVALID_HANDLE_VALUE)
{
// Retrieve the system error message for the last-error code
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
TCHAR szBuffer[4196];
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
wsprintf(szBuffer, _T("Exception handler failed with error %d: %s\n"), dw, lpMsgBuf);
MessageBox(MB_ICONEXCLAMATION, szBuffer, _T("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
debug(LOG_ERROR, "Exception handler failed to create file!");
}
#ifdef HAVE_BFD
bfd_set_error_handler((bfd_error_handler_type) rprintf);
@ -1113,28 +1143,43 @@ LONG WINAPI TopLevelExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
if (hReportFile)
{
TCHAR szBuffer[4196];
int err;
SetFilePointer(hReportFile, 0, 0, FILE_END);
// FIXME: We don't return from the below function call
GenerateExceptionReport(pExceptionInfo);
CloseHandle(hReportFile);
wsprintf(szBuffer, _T("Warzone has crashed.\r\nSee %s for more details\r\n"), szLogFileName);
err = MessageBox(MB_ICONERROR, szBuffer, _T("Warzone Crashed!"), MB_OK | MB_ICONERROR);
if (err == 0)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
TCHAR szBuffer[4196];
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
wsprintf(szBuffer, _T("Exception handler failed with error %d: %s\n"), dw, lpMsgBuf);
MessageBox(MB_ICONEXCLAMATION, szBuffer, _T("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
debug(LOG_ERROR, "Exception handler failed to create file!");
}
hReportFile = 0;
}
if(fuOldErrorMode & SEM_NOGPFAULTERRORBOX)
{
TCHAR szBuffer[4196];
wsprintf(szBuffer, _T("An unhandled exception ocurred\r\nSee %s for more details\r\n"), szLogFileName);
MessageBox(
NULL,
szBuffer,
_T("Error"),
MB_OK | MB_ICONERROR
);
}
SetErrorMode(fuOldErrorMode);
}
@ -1146,35 +1191,46 @@ LONG WINAPI TopLevelExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
void ExchndlSetup()
{
# if defined(WZ_CC_MINGW)
TCHAR miniDumpPath[PATH_MAX] = {'\0'};
// Install the unhandled exception filter function
prevExceptionFilter = SetUnhandledExceptionFilter(TopLevelExceptionFilter);
// Retrieve the current version
formattedVersionString = strdup(version_getFormattedVersionString());
// Figure out what the report file will be named, and store it away
if(GetModuleFileName(NULL, szLogFileName, MAX_PATH))
// Because of UAC on vista / win7 we use this to write our dumps to (unless we override it via OverrideRPTDirectory())
// NOTE: CSIDL_PERSONAL = C:\Users\user name\Documents
if ( SUCCEEDED( SHGetFolderPathA( NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, miniDumpPath ) ))
{
LPTSTR lpszDot;
PathAppend( miniDumpPath, TEXT( "Warzone 2100 2.3\\logs" ) );
// Look for the '.' before the "EXE" extension. Replace the extension
// with "RPT"
if((lpszDot = _tcsrchr(szLogFileName, _T('.'))))
if( !PathFileExists( miniDumpPath ) )
{
lpszDot++; // Advance past the '.'
_tcscpy(lpszDot, _T("RPT")); // "RPT" -> "Report"
if( ERROR_SUCCESS != SHCreateDirectoryEx( NULL, miniDumpPath, NULL ) )
{
_tcscpy(miniDumpPath, _T("c:\\temp"));
}
}
else
_tcscat(szLogFileName, _T(".RPT"));
}
else if(GetWindowsDirectory(szLogFileName, MAX_PATH))
{
_tcscat(szLogFileName, _T("EXCHNDL.RPT"));
else
{ // should never fail, but if it does, we fall back to this
_tcscpy(miniDumpPath, _T("c:\\temp"));
}
_tcscat(szLogFileName, _T("Warzone2100.RPT"));
_tcscat(miniDumpPath, _T("\\"));
_tcscat(miniDumpPath,szLogFileName);
_tcscpy(szLogFileName, miniDumpPath);
atexit(ExchndlShutdown);
#endif
}
void ResetRPTDirectory(char *newPath)
{
debug(LOG_WZ, "New RPT directory is %s, was %s", newPath, szLogFileName);
_tcscpy(szLogFileName, newPath);
}
void ExchndlShutdown(void)
{
if (prevExceptionFilter)

View File

@ -23,5 +23,5 @@
extern void ExchndlSetup(void);
extern void ExchndlShutdown(void);
void ResetRPTDirectory(char *newPath);
#endif // __INCLUDED_LIB_EXCEPTIONHANDLER_EXCHNDL_H__

View File

@ -73,7 +73,7 @@ CC:=gcc
CXX:=g++
WINDRES:=windres
WZ_CPPFLAGS+=-DWIN32
WZ_LDFLAGS+=-mwindows -lmingw32 -lSDLmain -lSDL -lpng12 -lphysfs -lz -lvorbisfile -lvorbis -logg -lpopt -lintl -lGLC -lglu32 -lopengl32 -lopenal32 -ldbghelp -lshfolder -lwinmm -lws2_32 -lbfd -liberty -liconv -lz -lfreetype -lfontconfig -lexpat -ltheora
WZ_LDFLAGS+=-mwindows -lmingw32 -lSDLmain -lSDL -lpng12 -lphysfs -lz -lvorbisfile -lvorbis -logg -lpopt -lintl -lGLC -lglu32 -lopengl32 -lopenal32 -ldbghelp -lshfolder -lwinmm -lshlwapi -lpsapi -lshell32 -lws2_32 -lbfd -liberty -liconv -lz -lfreetype -lfontconfig -lexpat -ltheora
# Import environment variables

View File

@ -342,6 +342,7 @@ bool ParseCommandLineEarly(int argc, const char** argv)
return false;
}
debug_register_callback( debug_callback_file, debug_callback_file_init, debug_callback_file_exit, (void*)token );
customDebugfile = true;
break;
case CLI_FLUSHDEBUGSTDERR:
@ -418,7 +419,7 @@ bool ParseCommandLine(int argc, const char** argv)
break;
case CLI_CHEAT:
printf(" ** DEBUG MODE UNLOCKED! **\n");
//printf(" ** DEBUG MODE UNLOCKED! **\n");
bAllowDebugMode = true;
break;

View File

@ -75,6 +75,7 @@
#include "map.h"
#include "parsetest.h"
#include "keybind.h"
#include <time.h>
/* Always use fallbacks on Windows */
#if defined(WZ_OS_WIN)
@ -114,6 +115,7 @@ bool use_override_mods = false;
char * loaded_mods[MAX_MODS] = { NULL };
char * mod_list = NULL;
bool customDebugfile = false; // Default false: user has NOT specified where to store the stdout/err file.
int num_loaded_mods = 0;
@ -485,6 +487,15 @@ static void initialize_ConfigDir(void)
tmpstr, PHYSFS_getLastError());
exit(1);
}
// NOTE: This is currently only used for mingw builds for now.
#if defined (WZ_CC_MINGW)
if (!OverrideRPTDirectory(tmpstr))
{
// since it failed, we just use our default path, and not the user supplied one.
debug(LOG_ERROR, "Error setting exception hanlder to use directory %s", tmpstr);
}
#endif
}
// User's home dir first so we allways see what we write
@ -1045,7 +1056,22 @@ int main(int argc, char *argv[])
* directory.
*/
initialize_ConfigDir();
if (!customDebugfile)
{
// there was no custom debug file specified (--debug-file=blah)
// so we use our write directory to store our logs.
time_t aclock;
struct tm *newtime;
char buf[PATH_MAX];
time( &aclock ); // Get time in seconds
newtime = localtime( &aclock ); // Convert time to struct
// Note: We are using fopen(), and not physfs routines to open the file
// log name is logs/(or \)WZlog-MMDD_HHMMSS.txt
snprintf(buf, sizeof(buf), "%slogs%sWZlog-%02d%02d_%02d%02d%02d.txt", PHYSFS_getWriteDir(), PHYSFS_getDirSeparator(),
newtime->tm_mon, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec );
debug_register_callback( debug_callback_file, debug_callback_file_init, debug_callback_file_exit, buf );
}
debug(LOG_WZ, "Warzone 2100 - %s", version_getFormattedVersionString());
/*** Initialize directory structure ***/
@ -1053,7 +1079,7 @@ int main(int argc, char *argv[])
make_dir(SaveGamePath, "savegame", NULL);
PHYSFS_mkdir("maps"); // MUST have this to prevent crashes when getting map
PHYSFS_mkdir("music");
PHYSFS_mkdir("logs"); // a place to hold our netplay logs
PHYSFS_mkdir("logs"); // a place to hold our netplay, mingw crash reports & WZ logs
make_dir(MultiPlayersPath, "multiplay", NULL);
make_dir(MultiPlayersPath, "multiplay", "players");
make_dir(MultiCustomMapsPath, "multiplay", "custommaps");

View File

@ -35,7 +35,7 @@ typedef enum {
//flag to indicate when initialisation is complete
extern BOOL gameInitialised;
extern BOOL bDisableLobby;
extern bool customDebugfile;
extern GS_GAMEMODE GetGameMode(void) WZ_DECL_PURE;
extern void SetGameMode(GS_GAMEMODE status);