2012-05-29 13:31:11 +00:00
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
# include "stdmisc.h"
2015-02-23 19:01:20 +00:00
# include <stringstream>
2012-05-29 13:31:11 +00:00
# include "nel/misc/common.h"
# include "nel/misc/ucstring.h"
# include "nel/misc/report.h"
# include "nel/misc/path.h"
2015-02-22 16:15:41 +00:00
# ifdef NL_OS_WINDOWS
# ifndef NL_COMP_MINGW
# define NOMINMAX
# endif
# include <windows.h>
# include <windowsx.h>
# include <winuser.h>
# endif // NL_OS_WINDOWS
# define NL_NO_DEBUG_FILES 1
2012-05-29 13:31:11 +00:00
using namespace std ;
2015-02-22 16:15:41 +00:00
# ifdef DEBUG_NEW
# define new DEBUG_NEW
# endif
2012-05-29 13:31:11 +00:00
namespace NLMISC
{
2015-02-23 19:01:20 +00:00
void setReportEmailFunction ( void * emailFunction )
2012-05-29 13:31:11 +00:00
{
2015-02-23 19:01:20 +00:00
// DEPRECATED
// no-op
2012-05-29 13:31:11 +00:00
}
2015-02-23 19:01:20 +00:00
// Contents of crash report
static string ReportBody ;
// Host url for crash report
static std : : string ReportPostUrl = " " ;
// Title for the crash report window
static std : : string ReportWindowTitle = " " ;
2015-02-23 00:36:50 +00:00
2015-02-23 19:01:20 +00:00
void setReportPostUrl ( const std : : string & postUrl )
{
ReportPostUrl = postUrl ;
}
2015-02-23 00:36:50 +00:00
2015-02-23 19:01:20 +00:00
// Launch the crash report application
2015-02-23 00:36:50 +00:00
static void doSendReport ( )
{
std : : string filename ;
2015-02-23 19:01:20 +00:00
filename = /*getLogDirectory() + */ " report_ " ; // FIXME: Should use log directory
2015-02-23 00:36:50 +00:00
filename + = NLMISC : : toString ( int ( time ( NULL ) ) ) ;
filename + = " .txt " ;
2015-02-23 19:01:20 +00:00
std : : stringstream params ;
params < < " -log " ;
params < < filename ; // FIXME: Escape the filepath with quotes
if ( ! ReportPostUrl . empty ( ) )
{
params < < " -host " ;
params < < ReportPostUrl ;
}
if ( ! ReportWindowTitle . empty ( ) )
{
params < < " -title " ;
params < < ReportWindowTitle ; // FIXME: Escape the title with quotes and test
}
2015-02-23 00:36:50 +00:00
std : : ofstream f ;
f . open ( filename . c_str ( ) ) ;
if ( ! f . good ( ) )
return ;
2015-02-23 19:01:20 +00:00
f < < ReportBody ;
2015-02-23 00:36:50 +00:00
f . close ( ) ;
# ifdef NL_OS_WINDOWS
2015-02-23 19:01:20 +00:00
NLMISC : : launchProgram ( " crash_report.exe " , params . str ( ) ) ;
2015-02-23 00:36:50 +00:00
# else
2015-02-23 19:01:20 +00:00
NLMISC : : launchProgram ( " crash_report " , params . str ( ) ) ;
2015-02-23 00:36:50 +00:00
# endif
// Added because NLSMIC::launcProgram needs time to launch
nlSleep ( 2 * 1000 ) ;
2015-02-23 19:01:20 +00:00
2015-02-23 00:36:50 +00:00
}
2015-02-23 19:01:20 +00:00
# if defined(FINAL_VERSION) || !defined(NL_OS_WINDOWS)
2015-02-22 16:20:24 +00:00
2015-02-23 19:01:20 +00:00
// For FINAL_VERSION, simply launch the crash report and exit the application
TReportResult report ( const std : : string & title , const std : : string & header , const std : : string & subject , const std : : string & body , bool enableCheckIgnore , uint debugButton , bool ignoreButton , sint quitButton , bool sendReportButton , bool & ignoreNextTime , const string & attachedFile )
2015-02-22 16:20:24 +00:00
{
2015-02-23 19:09:51 +00:00
ReportWindowTitle = title . empty ( ) ? " Nel Crash Report " : title ;
2015-02-23 19:01:20 +00:00
ReportBody = addSlashR ( body ) ;
doSendReport ( ) ;
2015-02-23 19:33:31 +00:00
# if defined(FINAL_VERSION) // TODO: This behaviour is used in the old report code when Quitting the application is the default crash report behaviour. Needs testing.
2015-02-23 19:01:20 +00:00
# ifdef NL_OS_WINDOWS
# ifndef NL_COMP_MINGW
// disable the Windows popup telling that the application aborted and disable the dr watson report.
_set_abort_behavior ( 0 , _WRITE_ABORT_MSG | _CALL_REPORTFAULT ) ;
# endif
# endif
// quit without calling atexit or static object dtors.
abort ( ) ;
# endif
2015-02-23 00:36:50 +00:00
2015-02-23 19:01:20 +00:00
return ReportQuit ;
2015-02-22 16:20:24 +00:00
}
# else
2015-02-23 19:01:20 +00:00
// Windows specific version for DEV builds, first shows a dialog box for debugging
2012-05-29 13:31:11 +00:00
2015-02-23 19:01:20 +00:00
static HWND sendReport = NULL ;
# define DELETE_OBJECT(a) if((a)!=NULL) { DeleteObject (a); a = NULL; }
2012-05-29 13:31:11 +00:00
2015-02-23 19:01:20 +00:00
static HWND checkIgnore = NULL ;
static HWND debug = NULL ;
static HWND ignore = NULL ;
static HWND quit = NULL ;
static HWND dialog = NULL ;
2015-02-22 16:38:06 +00:00
static bool NeedExit ;
static TReportResult Result ;
static bool IgnoreNextTime ;
2015-02-23 19:01:20 +00:00
static bool CanSendMailReport = false ;
2015-02-22 16:38:06 +00:00
static bool DebugDefaultBehavior , QuitDefaultBehavior ;
2015-02-23 19:01:20 +00:00
static void maybeSendReport ( )
2015-02-22 16:38:06 +00:00
{
if ( CanSendMailReport & & SendMessage ( sendReport , BM_GETCHECK , 0 , 0 ) ! = BST_CHECKED )
{
2015-02-23 19:01:20 +00:00
doSendReport ( ) ;
2015-02-22 16:38:06 +00:00
# ifndef NL_NO_DEBUG_FILES
2015-02-23 19:01:20 +00:00
CFile : : createEmptyFile ( getLogDirectory ( ) + " report_sent " ) ;
2015-02-22 16:38:06 +00:00
# endif
}
else
{
# ifndef NL_NO_DEBUG_FILES
CFile : : createEmptyFile ( getLogDirectory ( ) + " report_refused " ) ;
2015-02-20 01:03:33 +00:00
# endif
2015-02-23 19:01:20 +00:00
- }
2015-02-22 16:38:06 +00:00
}
static LRESULT CALLBACK WndProc ( HWND hWnd , UINT message , WPARAM wParam , LPARAM lParam )
{
//MSGFILTER *pmf;
if ( message = = WM_COMMAND & & HIWORD ( wParam ) = = BN_CLICKED )
{
if ( ( HWND ) lParam = = checkIgnore )
{
IgnoreNextTime = ! IgnoreNextTime ;
}
else if ( ( HWND ) lParam = = debug )
{
2015-02-23 19:01:20 +00:00
maybeSendReport ( ) ;
2015-02-22 16:38:06 +00:00
NeedExit = true ;
Result = ReportDebug ;
if ( DebugDefaultBehavior )
{
NLMISC_BREAKPOINT ;
}
}
else if ( ( HWND ) lParam = = ignore )
{
2015-02-23 19:01:20 +00:00
maybeSendReport ( ) ;
2015-02-22 16:38:06 +00:00
NeedExit = true ;
Result = ReportIgnore ;
}
else if ( ( HWND ) lParam = = quit )
{
2015-02-23 19:01:20 +00:00
maybeSendReport ( ) ;
2015-02-22 16:38:06 +00:00
NeedExit = true ;
Result = ReportQuit ;
2012-05-29 13:31:11 +00:00
2015-02-22 16:38:06 +00:00
if ( QuitDefaultBehavior )
{
// ace: we cannot call exit() because it's call the static object dtor and can crash the application
// if the dtor call order is not good.
//exit(EXIT_SUCCESS);
2012-05-29 13:31:11 +00:00
# ifdef NL_OS_WINDOWS
2014-06-17 22:28:08 +00:00
# ifndef NL_COMP_MINGW
2012-05-29 13:31:11 +00:00
// disable the Windows popup telling that the application aborted and disable the dr watson report.
_set_abort_behavior ( 0 , _WRITE_ABORT_MSG | _CALL_REPORTFAULT ) ;
2014-06-17 22:28:08 +00:00
# endif
2012-05-29 13:31:11 +00:00
# endif
// quit without calling atexit or static object dtors.
abort ( ) ;
2015-02-22 16:38:06 +00:00
}
}
}
else if ( message = = WM_CHAR )
{
if ( wParam = = 27 )
{
// ESC -> ignore
2015-02-23 19:01:20 +00:00
maybeSendReport ( ) ;
2015-02-22 16:38:06 +00:00
NeedExit = true ;
Result = ReportIgnore ;
}
}
2015-02-23 19:01:20 +00:00
return DefWindowProc ( hWnd , message , wParam , lParam ) ;
2015-02-22 16:38:06 +00:00
}
2015-02-23 19:01:20 +00:00
TReportResult report ( const std : : string & title , const std : : string & header , const std : : string & subject , const std : : string & body , bool enableCheckIgnore , uint debugButton , bool ignoreButton , sint quitButton , bool sendReportButton , bool & ignoreNextTime , const string & attachedFile )
2015-02-22 16:38:06 +00:00
{
// register the window
static bool AlreadyRegister = false ;
2015-02-23 19:01:20 +00:00
if ( ! AlreadyRegister )
2015-02-22 16:38:06 +00:00
{
WNDCLASSW wc ;
memset ( & wc , 0 , sizeof ( wc ) ) ;
wc . style = CS_HREDRAW | CS_VREDRAW ;
wc . lpfnWndProc = ( WNDPROC ) WndProc ;
wc . cbClsExtra = 0 ;
wc . cbWndExtra = 0 ;
wc . hInstance = GetModuleHandle ( NULL ) ;
wc . hIcon = NULL ;
wc . hCursor = LoadCursor ( NULL , IDC_ARROW ) ;
wc . hbrBackground = ( HBRUSH ) COLOR_WINDOW ;
wc . lpszClassName = L " NLReportWindow " ;
wc . lpszMenuName = NULL ;
if ( ! RegisterClassW ( & wc ) ) return ReportError ;
AlreadyRegister = true ;
}
2015-02-23 19:01:20 +00:00
ReportWindowTitle = title . empty ( ) ? " Nel Crash Report " : title ;
ucstring formatedTitle = ucstring : : makeFromUtf8 ( ReportWindowTitle ) ;
2015-02-22 16:38:06 +00:00
// create the window
dialog = CreateWindowW ( L " NLReportWindow " , ( LPCWSTR ) formatedTitle . c_str ( ) , WS_DLGFRAME | WS_CAPTION /*| WS_THICKFRAME*/ , CW_USEDEFAULT , CW_USEDEFAULT , 456 , 400 , NULL , NULL , GetModuleHandle ( NULL ) , NULL ) ;
// create the font
HFONT font = CreateFont ( - 12 , 0 , 0 , 0 , FW_DONTCARE , FALSE , FALSE , FALSE , DEFAULT_CHARSET , OUT_DEFAULT_PRECIS , CLIP_DEFAULT_PRECIS , DEFAULT_QUALITY , DEFAULT_PITCH | FF_DONTCARE , " Arial " ) ;
// create the edit control
HWND edit = CreateWindowW ( L " EDIT " , NULL , WS_BORDER | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | ES_READONLY | ES_LEFT | ES_MULTILINE , 7 , 70 , 429 , 212 , dialog , ( HMENU ) NULL , ( HINSTANCE ) GetWindowLongPtr ( dialog , GWLP_HINSTANCE ) , NULL ) ;
SendMessage ( edit , WM_SETFONT , ( WPARAM ) font , TRUE ) ;
// set the edit text limit to lot of :)
SendMessage ( edit , EM_LIMITTEXT , ~ 0U , 0 ) ;
2015-02-23 19:01:20 +00:00
ReportBody = addSlashR ( body ) ;
2015-02-22 16:38:06 +00:00
// set the message in the edit text
2015-02-23 19:01:20 +00:00
SendMessage ( edit , WM_SETTEXT , ( WPARAM ) 0 , ( LPARAM ) ReportBody . c_str ( ) ) ;
2015-02-22 16:38:06 +00:00
if ( enableCheckIgnore )
{
// create the combo box control
checkIgnore = CreateWindowW ( L " BUTTON " , L " Don't display this report again " , WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | BS_CHECKBOX , 7 , 290 , 429 , 18 , dialog , ( HMENU ) NULL , ( HINSTANCE ) GetWindowLongPtr ( dialog , GWLP_HINSTANCE ) , NULL ) ;
SendMessage ( checkIgnore , WM_SETFONT , ( WPARAM ) font , TRUE ) ;
if ( ignoreNextTime )
{
SendMessage ( checkIgnore , BM_SETCHECK , BST_CHECKED , 0 ) ;
}
}
// create the debug button control
debug = CreateWindowW ( L " BUTTON " , L " Debug " , WS_CHILD | WS_VISIBLE , 7 , 315 , 75 , 25 , dialog , ( HMENU ) NULL , ( HINSTANCE ) GetWindowLongPtr ( dialog , GWLP_HINSTANCE ) , NULL ) ;
SendMessage ( debug , WM_SETFONT , ( WPARAM ) font , TRUE ) ;
if ( debugButton = = 0 )
EnableWindow ( debug , FALSE ) ;
// create the ignore button control
ignore = CreateWindowW ( L " BUTTON " , L " Ignore " , WS_CHILD | WS_VISIBLE , 75 + 7 + 7 , 315 , 75 , 25 , dialog , ( HMENU ) NULL , ( HINSTANCE ) GetWindowLongPtr ( dialog , GWLP_HINSTANCE ) , NULL ) ;
SendMessage ( ignore , WM_SETFONT , ( WPARAM ) font , TRUE ) ;
if ( ignoreButton = = 0 )
EnableWindow ( ignore , FALSE ) ;
// create the quit button control
quit = CreateWindowW ( L " BUTTON " , L " Quit " , WS_CHILD | WS_VISIBLE , 75 + 75 + 7 + 7 + 7 , 315 , 75 , 25 , dialog , ( HMENU ) NULL , ( HINSTANCE ) GetWindowLongPtr ( dialog , GWLP_HINSTANCE ) , NULL ) ;
SendMessage ( quit , WM_SETFONT , ( WPARAM ) font , TRUE ) ;
if ( quitButton = = 0 )
EnableWindow ( quit , FALSE ) ;
// create the debug button control
sendReport = CreateWindowW ( L " BUTTON " , L " Don't send the report " , WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX , 7 , 315 + 32 , 429 , 18 , dialog , ( HMENU ) NULL , ( HINSTANCE ) GetWindowLongPtr ( dialog , GWLP_HINSTANCE ) , NULL ) ;
SendMessage ( sendReport , WM_SETFONT , ( WPARAM ) font , TRUE ) ;
string formatedHeader ;
if ( header . empty ( ) )
{
formatedHeader = " This application stopped to display this report. " ;
}
else
{
formatedHeader = header ;
}
// ace don't do that because it s slow to try to send a mail
2015-02-23 19:01:20 +00:00
CanSendMailReport = sendReportButton & & ! ReportPostUrl . empty ( ) ;
2015-02-22 16:38:06 +00:00
if ( CanSendMailReport )
formatedHeader + = " Send report will only email the contents of the box below. Please, send it to help us (it could take few minutes to send the email, be patient). " ;
else
EnableWindow ( sendReport , FALSE ) ;
ucstring uc = ucstring : : makeFromUtf8 ( formatedHeader ) ;
// create the label control
HWND label = CreateWindowW ( L " STATIC " , ( LPCWSTR ) uc . c_str ( ) , WS_CHILD | WS_VISIBLE /*| SS_WHITERECT*/ , 7 , 7 , 429 , 51 , dialog , ( HMENU ) NULL , ( HINSTANCE ) GetWindowLongPtr ( dialog , GWLP_HINSTANCE ) , NULL ) ;
SendMessage ( label , WM_SETFONT , ( WPARAM ) font , TRUE ) ;
DebugDefaultBehavior = debugButton = = 1 ;
QuitDefaultBehavior = quitButton = = 1 ;
IgnoreNextTime = ignoreNextTime ;
// show until the cursor really show :)
while ( ShowCursor ( TRUE ) < 0 )
;
SetWindowPos ( dialog , HWND_TOPMOST , 0 , 0 , 0 , 0 , SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW ) ;
SetFocus ( dialog ) ;
SetForegroundWindow ( dialog ) ;
NeedExit = false ;
while ( ! NeedExit )
{
MSG msg ;
while ( PeekMessageW ( & msg , NULL , 0 , 0 , PM_REMOVE ) )
{
TranslateMessage ( & msg ) ;
DispatchMessageW ( & msg ) ;
}
nlSleep ( 1 ) ;
}
// set the user result
ignoreNextTime = IgnoreNextTime ;
ShowWindow ( dialog , SW_HIDE ) ;
DELETE_OBJECT ( sendReport )
DELETE_OBJECT ( quit )
DELETE_OBJECT ( ignore )
DELETE_OBJECT ( debug )
DELETE_OBJECT ( checkIgnore )
DELETE_OBJECT ( edit )
DELETE_OBJECT ( label )
DELETE_OBJECT ( dialog )
2015-02-22 16:15:41 +00:00
2015-02-22 16:38:06 +00:00
return Result ;
2012-05-29 13:31:11 +00:00
}
2015-02-22 16:20:24 +00:00
# endif
2012-05-29 13:31:11 +00:00
} // NLMISC