Fixed issue #1351: Add Hotkey to deselect all files on commit
[TortoiseGit.git] / src / Utils / CrashReport.h
blobfbc8f45adae86d972bf3be078a6e1f0a2e988b31
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #pragma once
20 #include "../../ext/CrashServer/CrashHandler/CrashHandler/CrashHandler.h"
21 #include "../version.h"
22 #include <time.h>
23 #include <string>
24 #include <tchar.h>
26 // dummy define, needed only when we use crashrpt instead of this.
27 #define CR_AF_MAKE_FILE_COPY 0
29 /**
30 * \ingroup Utils
31 * helper class for the CrashServerSDK
33 class CCrashReport
35 private:
36 CCrashReport(void)
38 LoadDll();
41 ~CCrashReport(void)
45 public:
46 static CCrashReport& Instance()
48 static CCrashReport instance;
49 return instance;
52 int Uninstall(void) { return FALSE; }
53 int AddFile2(LPCTSTR pszFile,LPCTSTR pszDestFile,LPCTSTR /*pszDesc*/,DWORD /*dwFlags*/)
55 return AddFileToReport(pszFile, pszDestFile) ? 1 : 0;
59 //! Initializes crash handler.
60 //! \note You may call this function multiple times if some data has changed.
61 //! \return Return \b true if crash handling was enabled.
62 bool InitCrashHandler(
63 ApplicationInfo* applicationInfo, //!< [in] Pointer to the ApplicationInfo structure that identifies your application.
64 HandlerSettings* handlerSettings, //!< [in] Pointer to the HandlerSettings structure that customizes crash handling behavior. This paramenter can be \b NULL.
65 BOOL ownProcess = TRUE //!< [in] If you own the process your code running in set this option to \b TRUE. If don't (for example you write
66 //!< a plugin to some external application) set this option to \b FALSE. In that case you need to explicitly
67 //!< catch exceptions. See \ref SendReport for more information.
68 ) throw()
70 if (!m_InitCrashHandler)
71 return false;
73 return m_InitCrashHandler(applicationInfo, handlerSettings, ownProcess) != FALSE;
76 //! You may add any key/value pair to crash report.
77 //! \return If the function succeeds, the return value is \b true.
78 bool AddUserInfoToReport(
79 LPCWSTR key, //!< [in] key string that will be added to the report.
80 LPCWSTR value //!< [in] value for the key.
81 ) throw()
83 if (!m_AddUserInfoToReport)
84 return false;
85 m_AddUserInfoToReport(key, value);
86 return true;
89 //! You may add any file to crash report. This file will be read when crash appears and will be sent within the report.
90 //! Multiple files may be added. Filename of the file in the report may be changed to any name.
91 //! \return If the function succeeds, the return value is \b true.
92 bool AddFileToReport(
93 LPCWSTR path, //!< [in] Path to the file, that will be added to the report.
94 LPCWSTR reportFileName /* = NULL */ //!< [in] Filename that will be used in report for this file. If parameter is \b NULL, original name from path will be used.
95 ) throw()
97 if (!m_AddFileToReport)
98 return false;
99 m_AddFileToReport(path, reportFileName);
100 return true;
103 //! Remove from report the file that was registered earlier to be sent within report.
104 //! \return If the function succeeds, the return value is \b true.
105 bool RemoveFileFromReport(
106 LPCWSTR path //!< [in] Path to the file, that will be removed from the report.
107 ) throw()
109 if (!m_RemoveFileFromReport)
110 return false;
111 m_RemoveFileFromReport(path);
112 return true;
115 //! Fills version field (V) of ApplicationInfo with product version
116 //! found in the executable file of the current process.
117 //! \return If the function succeeds, the return value is \b true.
118 bool GetVersionFromApp(
119 ApplicationInfo* appInfo //!< [out] Pointer to ApplicationInfo structure. Its version field (V) will be set to product version.
120 ) throw()
122 if (!m_GetVersionFromApp)
123 return false;
124 return m_GetVersionFromApp(appInfo) != FALSE;
127 //! Fill version field (V) of ApplicationInfo with product version found in the file specified.
128 //! \return If the function succeeds, the return value is \b true.
129 bool GetVersionFromFile(
130 LPCWSTR path, //!< [in] Path to the file product version will be extracted from.
131 ApplicationInfo* appInfo //!< [out] Pointer to ApplicationInfo structure. Its version field (V) will be set to product version.
132 ) throw()
134 if (!m_GetVersionFromFile)
135 return false;
136 return m_GetVersionFromFile(path, appInfo) != FALSE;
139 //! If you do not own the process your code running in (for example you write a plugin to some
140 //! external application) you need to properly initialize CrashHandler using \b ownProcess option.
141 //! Also you need to explicitly catch all exceptions in all entry points to your code and in all
142 //! threads you create. To do so use this construction:
143 //! \code
144 //! bool SomeEntryPoint(PARAM p)
145 //! {
146 //! __try
147 //! {
148 //! return YouCode(p);
149 //! }
150 //! __except (CrashHandler::SendReport(GetExceptionInformation()))
151 //! {
152 //! ::ExitProcess(0); // It is better to stop the process here or else corrupted data may incomprehensibly crash it later.
153 //! return false;
154 //! }
155 //! }
156 //! \endcode
157 LONG SendReport(
158 EXCEPTION_POINTERS* exceptionPointers //!< [in] Pointer to EXCEPTION_POINTERS structure. You should get it using GetExceptionInformation()
159 //!< function inside __except keyword.
162 if (!m_SendReport)
163 return EXCEPTION_CONTINUE_SEARCH;
164 // There is no crash handler but asserts should continue anyway
165 if (exceptionPointers->ExceptionRecord->ExceptionCode == ExceptionAssertionViolated)
166 return EXCEPTION_CONTINUE_EXECUTION;
167 return m_SendReport(exceptionPointers);
170 //! To send a report about violated assertion you can throw exception with this exception code
171 //! using: \code RaiseException(CrashHandler::ExceptionAssertionViolated, 0, 0, NULL); \endcode
172 //! Execution will continue after report will be sent (EXCEPTION_CONTINUE_EXECUTION would be used).
173 //! \note If you called CrashHandler constructor and crshhdnl.dll was missing you still may using this exception.
174 //! It will be catched, ignored and execution will continue. \ref SendReport function also works safely
175 //! when crshhdnl.dll was missing.
176 static const DWORD ExceptionAssertionViolated = ((DWORD)0xCCE17000);
178 //! Sends assertion violation report from this point and continue execution.
179 //! \sa ExceptionAssertionViolated
180 //! \note Functions prefixed with "CrashServer_" will be ignored in stack parsing.
181 void CrashServer_SendAssertionViolated()
183 if (!m_InitCrashHandler)
184 return;
185 ::RaiseException(CrashHandler::ExceptionAssertionViolated, 0, 0, NULL);
188 private:
189 bool LoadDll() throw()
191 bool result = false;
193 // hCrshhndlDll should not be unloaded, crash may appear even after return from main().
194 // So hCrshhndlDll is not saved after construction.
195 HMODULE hCrshhndlDll = ::LoadLibraryW(L"crshhndl.dll");
196 if (hCrshhndlDll != NULL)
198 m_InitCrashHandler = (pfnInitCrashHandler) GetProcAddress(hCrshhndlDll, "InitCrashHandler");
199 m_SendReport = (pfnSendReport) GetProcAddress(hCrshhndlDll, "SendReport");
200 m_IsReadyToExit = (pfnIsReadyToExit) GetProcAddress(hCrshhndlDll, "IsReadyToExit");
201 m_AddUserInfoToReport = (pfnAddUserInfoToReport) GetProcAddress(hCrshhndlDll, "AddUserInfoToReport");
202 m_AddFileToReport = (pfnAddFileToReport) GetProcAddress(hCrshhndlDll, "AddFileToReport");
203 m_RemoveFileFromReport = (pfnRemoveFileFromReport) GetProcAddress(hCrshhndlDll, "RemoveFileFromReport");
204 m_GetVersionFromApp = (pfnGetVersionFromApp) GetProcAddress(hCrshhndlDll, "GetVersionFromApp");
205 m_GetVersionFromFile = (pfnGetVersionFromFile) GetProcAddress(hCrshhndlDll, "GetVersionFromFile");
207 result = m_InitCrashHandler
208 && m_SendReport
209 && m_IsReadyToExit
210 && m_AddUserInfoToReport
211 && m_AddFileToReport
212 && m_RemoveFileFromReport
213 && m_GetVersionFromApp
214 && m_GetVersionFromFile;
217 // if no crash processing was started, we need to ignore ExceptionAssertionViolated exceptions.
218 if (!result)
219 ::AddVectoredExceptionHandler(TRUE, SkipAsserts);
221 return result;
224 static LONG CALLBACK SkipAsserts(EXCEPTION_POINTERS* pExceptionInfo)
226 if (pExceptionInfo->ExceptionRecord->ExceptionCode == ExceptionAssertionViolated)
227 return EXCEPTION_CONTINUE_EXECUTION;
228 return EXCEPTION_CONTINUE_SEARCH;
231 typedef BOOL (*pfnInitCrashHandler)(ApplicationInfo* applicationInfo, HandlerSettings* handlerSettings, BOOL ownProcess);
232 typedef LONG (*pfnSendReport)(EXCEPTION_POINTERS* exceptionPointers);
233 typedef BOOL (*pfnIsReadyToExit)();
234 typedef void (*pfnAddUserInfoToReport)(LPCWSTR key, LPCWSTR value);
235 typedef void (*pfnAddFileToReport)(LPCWSTR path, LPCWSTR reportFileName /* = NULL */);
236 typedef void (*pfnRemoveFileFromReport)(LPCWSTR path);
237 typedef BOOL (*pfnGetVersionFromApp)(ApplicationInfo* appInfo);
238 typedef BOOL (*pfnGetVersionFromFile)(LPCWSTR path, ApplicationInfo* appInfo);
240 pfnInitCrashHandler m_InitCrashHandler;
241 pfnSendReport m_SendReport;
242 pfnIsReadyToExit m_IsReadyToExit;
243 pfnAddUserInfoToReport m_AddUserInfoToReport;
244 pfnAddFileToReport m_AddFileToReport;
245 pfnRemoveFileFromReport m_RemoveFileFromReport;
246 pfnGetVersionFromApp m_GetVersionFromApp;
247 pfnGetVersionFromFile m_GetVersionFromFile;
251 class CCrashReportTGit
253 public:
255 //! Installs exception handlers to the caller process
256 CCrashReportTGit(LPCTSTR appname, bool bOwnProcess = true)
257 : m_nInstallStatus(0)
259 char s_month[6];
260 int month, day, year;
261 struct tm t = {0};
262 static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
263 sscanf_s(__DATE__, "%s %d %d", s_month, _countof(s_month)-1, &day, &year);
264 month = (int)((strstr(month_names, s_month)-month_names))/3;
266 t.tm_mon = month;
267 t.tm_mday = day;
268 t.tm_year = year - 1900;
269 t.tm_isdst = -1;
270 __time64_t compiletime = _mktime64(&t);
272 __time64_t now;
273 _time64(&now);
275 if ((now - compiletime)<(60*60*24*31*4))
277 ApplicationInfo appInfo;
278 memset(&appInfo, 0, sizeof(appInfo));
279 appInfo.ApplicationInfoSize = sizeof(ApplicationInfo);
280 appInfo.ApplicationGUID = "7fbde3fc-94e9-408b-b5c8-62bd4e203570";
281 appInfo.Prefix = "tgit";
282 appInfo.AppName = appname;
283 appInfo.Company = L"TortoiseGit";
285 appInfo.Hotfix = 0;
286 appInfo.V[0] = TGIT_VERMAJOR;
287 appInfo.V[1] = TGIT_VERMINOR;
288 appInfo.V[2] = TGIT_VERMICRO;
289 appInfo.V[3] = TGIT_VERBUILD;
291 HandlerSettings handlerSettings;
292 memset(&handlerSettings, 0, sizeof(handlerSettings));
293 handlerSettings.HandlerSettingsSize = sizeof(handlerSettings);
294 handlerSettings.LeaveDumpFilesInTempFolder = FALSE;
295 handlerSettings.UseWER = FALSE;
296 handlerSettings.OpenProblemInBrowser = TRUE;
297 handlerSettings.SubmitterID = 0;
299 CCrashReport::Instance().InitCrashHandler(&appInfo, &handlerSettings, bOwnProcess);
304 //! Deinstalls exception handlers from the caller process
305 ~CCrashReportTGit()
307 CCrashReport::Instance().Uninstall();
310 //! Install status
311 int m_nInstallStatus;