Follow up for issue #2909: Reset stored value if "No" was stored
[TortoiseGit.git] / src / TortoiseProc / TortoiseProc.cpp
blob3ec9c00e464cefdfc13bd0891fc730acb41cb27c
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2016 - TortoiseGit
4 // Copyright (C) 2003-2008, 2012-2014 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "TortoiseProc.h"
22 #include "SysImageList.h"
23 #include "..\Utils\CrashReport.h"
24 #include "CmdLineParser.h"
25 #include "Hooks.h"
26 #include "AppUtils.h"
27 #include "PathUtils.h"
28 #include "StringUtils.h"
29 #include "UnicodeUtils.h"
30 #include "MessageBox.h"
31 #include "DirFileEnum.h"
32 #include "GitAdminDir.h"
33 #include "Git.h"
34 #include "SmartHandle.h"
35 #include "Commands\Command.h"
36 #include "..\version.h"
37 #include "JumpListHelpers.h"
38 #include "ConfigureGitExe.h"
39 #include "Libraries.h"
40 #include "TaskbarUUID.h"
41 #include "ProjectProperties.h"
42 #include "HistoryCombo.h"
43 #include <math.h>
44 #include <random>
45 #include "SendMail.h"
46 #include "WindowsCredentialsStore.h"
47 #include "FirstStartWizard.h"
49 #define STRUCT_IOVEC_DEFINED
51 #ifdef _DEBUG
52 #define new DEBUG_NEW
53 #endif
55 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
57 BEGIN_MESSAGE_MAP(CTortoiseProcApp, CWinAppEx)
58 ON_COMMAND(ID_HELP, CWinAppEx::OnHelp)
59 END_MESSAGE_MAP()
61 //////////////////////////////////////////////////////////////////////////
63 CTortoiseProcApp::CTortoiseProcApp()
65 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Constructor\n");
66 SetDllDirectory(L"");
67 CCrashReport::Instance().AddUserInfoToReport(L"CommandLine", GetCommandLine());
68 EnableHtmlHelp();
69 CHooks::Create();
70 git_libgit2_init();
71 CGit::SetGit2CredentialCallback(CAppUtils::Git2GetUserPassword);
72 CGit::SetGit2CertificateCheckCertificate(CAppUtils::Git2CertificateCheck);
73 m_bLoadUserToolbars = FALSE;
74 m_bSaveState = FALSE;
75 retSuccess = false;
76 m_gdiplusToken = NULL;
77 #if defined (_WIN64) && _MSC_VER == 1800
78 _set_FMA3_enable(0);
79 #endif
82 CTortoiseProcApp::~CTortoiseProcApp()
84 CHooks::Destroy();
85 git_libgit2_shutdown();
88 // The one and only CTortoiseProcApp object
89 CTortoiseProcApp theApp;
90 CString sOrigCWD;
91 CString g_sGroupingUUID;
92 CString g_sGroupingIcon;
93 bool g_bGroupingRemoveIcon = false;
94 HWND hWndExplorer;
96 #if ENABLE_CRASHHANLDER
97 CCrashReportTGit crasher(L"TortoiseGit " _T(APP_X64_STRING), TGIT_VERMAJOR, TGIT_VERMINOR, TGIT_VERMICRO, TGIT_VERBUILD, TGIT_VERDATE);
98 #endif
100 // CTortoiseProcApp initialization
102 BOOL CTortoiseProcApp::InitInstance()
104 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": InitInstance\n");
105 CheckUpgrade();
106 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
107 CMFCButton::EnableWindowsTheming();
109 Gdiplus::GdiplusStartupInput gdiplusStartupInput;
110 Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, nullptr);
112 //set the resource dll for the required language
113 CRegDWORD loc = CRegDWORD(L"Software\\TortoiseGit\\LanguageID", 1033);
114 long langId = loc;
115 CString langDll;
116 CStringA langpath = CStringA(CPathUtils::GetAppParentDirectory());
117 langpath += "Languages";
120 langDll.Format(L"%sLanguages\\TortoiseProc%ld.dll", (LPCTSTR)CPathUtils::GetAppParentDirectory(), langId);
122 CString sVer = _T(STRPRODUCTVER);
123 CString sFileVer = CPathUtils::GetVersionFromFile(langDll);
124 if (sFileVer == sVer)
126 HINSTANCE hInst = LoadLibrary(langDll);
127 if (hInst)
129 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Load Language DLL %s\n", langDll);
130 AfxSetResourceHandle(hInst);
131 break;
135 DWORD lid = SUBLANGID(langId);
136 lid--;
137 if (lid > 0)
138 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
139 else
140 langId = 0;
142 } while (langId != 0);
144 CString langStr;
145 langStr.Format(L"%ld", langId);
146 CCrashReport::Instance().AddUserInfoToReport(L"LanguageID", langStr);
148 TCHAR buf[6] = { 0 };
149 wcscpy_s(buf, L"en");
150 langId = loc;
151 // MFC uses a help file with the same name as the application by default,
152 // which means we have to change that default to our language specific help files
153 CString sHelppath = CPathUtils::GetAppDirectory() + L"TortoiseGit_en.chm";
154 free((void*)m_pszHelpFilePath);
155 m_pszHelpFilePath=_wcsdup(sHelppath);
156 sHelppath = CPathUtils::GetAppParentDirectory() + L"Languages\\TortoiseGit_en.chm";
159 CString sLang = L"_";
160 if (GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO639LANGNAME, buf, _countof(buf)))
162 sLang += buf;
163 sHelppath.Replace(L"_en", sLang);
164 if (PathFileExists(sHelppath))
166 free((void*)m_pszHelpFilePath);
167 m_pszHelpFilePath=_wcsdup(sHelppath);
168 break;
171 sHelppath.Replace(sLang, L"_en");
172 if (GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO3166CTRYNAME, buf, _countof(buf)))
174 sLang += L'_';
175 sLang += buf;
176 sHelppath.Replace(L"_en", sLang);
177 if (PathFileExists(sHelppath))
179 free((void*)m_pszHelpFilePath);
180 m_pszHelpFilePath=_wcsdup(sHelppath);
181 break;
184 sHelppath.Replace(sLang, L"_en");
186 DWORD lid = SUBLANGID(langId);
187 lid--;
188 if (lid > 0)
189 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
190 else
191 langId = 0;
192 } while (langId);
193 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Set Help Filename %s\n", m_pszHelpFilePath);
194 setlocale(LC_ALL, "");
196 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Initializing UI components ...\n");
197 // InitCommonControls() is required on Windows XP if an application
198 // manifest specifies use of ComCtl32.dll version 6 or later to enable
199 // visual styles. Otherwise, any window creation will fail.
201 INITCOMMONCONTROLSEX used = {
202 sizeof(INITCOMMONCONTROLSEX),
203 ICC_ANIMATE_CLASS | ICC_BAR_CLASSES | ICC_COOL_CLASSES | ICC_DATE_CLASSES |
204 ICC_HOTKEY_CLASS | ICC_INTERNET_CLASSES | ICC_LISTVIEW_CLASSES |
205 ICC_NATIVEFNTCTL_CLASS | ICC_PAGESCROLLER_CLASS | ICC_PROGRESS_CLASS |
206 ICC_TAB_CLASSES | ICC_TREEVIEW_CLASSES | ICC_UPDOWN_CLASS |
207 ICC_USEREX_CLASSES | ICC_WIN95_CLASSES
209 InitCommonControlsEx(&used);
210 AfxOleInit();
211 AfxEnableControlContainer();
212 AfxInitRichEdit5();
213 CWinAppEx::InitInstance();
214 SetRegistryKey(L"TortoiseGit");
215 SYS_IMAGE_LIST();
216 CHistoryCombo::m_nGitIconIndex = SYS_IMAGE_LIST().AddIcon((HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_GITCONFIG), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE));
217 AfxGetApp()->m_pszProfileName = _wcsdup(L"TortoiseProc"); // w/o this ResizableLib will store data under TortoiseGitProc which is not compatible with older versions
219 CCmdLineParser parser(AfxGetApp()->m_lpCmdLine);
221 hWndExplorer = nullptr;
222 CString sVal = parser.GetVal(L"hwnd");
223 if (!sVal.IsEmpty())
224 hWndExplorer = (HWND)_wcstoui64(sVal, nullptr, 16);
226 while (GetParent(hWndExplorer))
227 hWndExplorer = GetParent(hWndExplorer);
228 if (!IsWindow(hWndExplorer))
229 hWndExplorer = nullptr;
231 // if HKCU\Software\TortoiseGit\Debug is not 0, show our command line
232 // in a message box
233 if (CRegDWORD(L"Software\\TortoiseGit\\Debug", FALSE) == TRUE)
234 AfxMessageBox(AfxGetApp()->m_lpCmdLine, MB_OK | MB_ICONINFORMATION);
236 if (parser.HasKey(L"command") && wcscmp(parser.GetVal(L"command"), L"firststart") == 0)
238 // CFirstStartWizard requires sOrigCWD to be set
239 DWORD len = GetCurrentDirectory(0, nullptr);
240 if (len)
242 auto originalCurrentDirectory = std::make_unique<TCHAR[]>(len);
243 if (GetCurrentDirectory(len, originalCurrentDirectory.get()))
245 sOrigCWD = originalCurrentDirectory.get();
246 sOrigCWD = CPathUtils::GetLongPathname(sOrigCWD);
249 CFirstStartWizard wizard(IDS_APPNAME, CWnd::FromHandle(hWndExplorer), parser.GetLongVal(L"page"));
250 return (wizard.DoModal() == ID_WIZFINISH);
253 if (!g_Git.CheckMsysGitDir())
255 UINT ret = CMessageBox::Show(hWndExplorer, IDS_PROC_NOMSYSGIT, IDS_APPNAME, 3, IDI_HAND, IDS_PROC_SETMSYSGITPATH, IDS_PROC_GOTOMSYSGITWEBSITE, IDS_ABORTBUTTON);
256 if (ret == 2)
257 ShellExecute(hWndExplorer, L"open", GIT_FOR_WINDOWS_URL, nullptr, nullptr, SW_SHOW);
258 else if (ret == 1)
260 CFirstStartWizard wizard(IDS_APPNAME, CWnd::FromHandle(hWndExplorer), 2);
261 wizard.DoModal();
263 return FALSE;
265 if (!CConfigureGitExe::CheckGitVersion(hWndExplorer))
266 return FALSE;
269 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Registering Crash Report ...\n");
270 CCrashReport::Instance().AddUserInfoToReport(L"msysGitDir", CGit::ms_LastMsysGitDir);
271 CString versionString;
272 versionString.Format(L"%X", CGit::ms_LastMsysGitVersion);
273 CCrashReport::Instance().AddUserInfoToReport(L"msysGitVersion", versionString);
276 if (parser.HasKey(L"urlhandler"))
278 CString url = parser.GetVal(L"urlhandler");
279 if (CStringUtils::StartsWith(url, L"tgit://clone/"))
280 url = url.Mid(13); // 21 = "tgit://clone/".GetLength()
281 else if (CStringUtils::StartsWith(url, L"github-windows://openRepo/"))
283 url = url.Mid(26); // 26 = "github-windows://openRepo/".GetLength()
284 int questioMark = url.Find('?');
285 if (questioMark > 0)
286 url = url.Left(questioMark);
288 else if (CStringUtils::StartsWith(url, L"smartgit://cloneRepo/"))
289 url = url.Mid(21); // 21 = "smartgit://cloneRepo/".GetLength()
290 else
292 CMessageBox::Show(nullptr, IDS_ERR_INVALIDPATH, IDS_APPNAME, MB_ICONERROR);
293 return FALSE;
295 CString newCmd;
296 newCmd.Format(L"/command:clone /url:\"%s\" /hasurlhandler", (LPCTSTR)url);
297 parser = CCmdLineParser(newCmd);
300 if (parser.HasKey(L"path") && parser.HasKey(L"pathfile"))
302 CMessageBox::Show(nullptr, IDS_ERR_INVALIDPATH, IDS_APPNAME, MB_ICONERROR);
303 return FALSE;
306 CTGitPath cmdLinePath;
307 CTGitPathList pathList;
308 if (g_sGroupingUUID.IsEmpty())
309 g_sGroupingUUID = parser.GetVal(L"groupuuid");
310 if (parser.HasKey(L"pathfile"))
312 CString sPathfileArgument = CPathUtils::GetLongPathname(parser.GetVal(L"pathfile"));
314 cmdLinePath.SetFromUnknown(sPathfileArgument);
315 if (pathList.LoadFromFile(cmdLinePath)==false)
316 return FALSE; // no path specified!
317 if (parser.HasKey(L"deletepathfile"))
319 // We can delete the temporary path file, now that we've loaded it
320 ::DeleteFile(cmdLinePath.GetWinPath());
322 // This was a path to a temporary file - it's got no meaning now, and
323 // anybody who uses it again is in for a problem...
324 cmdLinePath.Reset();
327 else
329 CString sPathArgument = CPathUtils::GetLongPathname(parser.GetVal(L"path"));
330 if (parser.HasKey(L"expaths"))
332 // an /expaths param means we're started via the buttons in our Win7 library
333 // and that means the value of /expaths is the current directory, and
334 // the selected paths are then added as additional parameters but without a key, only a value
336 // because of the "strange treatment of quotation marks and backslashes by CommandLineToArgvW"
337 // we have to escape the backslashes first. Since we're only dealing with paths here, that's
338 // a save bet.
339 // Without this, a command line like:
340 // /command:commit /expaths:"D:\" "D:\Utils"
341 // would fail because the "D:\" is treated as the backslash being the escape char for the quotation
342 // mark and we'd end up with:
343 // argv[1] = /command:commit
344 // argv[2] = /expaths:D:" D:\Utils
345 // See here for more details: http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
346 CString cmdLine = GetCommandLineW();
347 cmdLine.Replace(L"\\", L"\\\\");
348 int nArgs = 0;
349 LPWSTR *szArglist = CommandLineToArgvW(cmdLine, &nArgs);
350 if (szArglist)
352 // argument 0 is the process path, so start with 1
353 for (int i = 1; i < nArgs; ++i)
355 if (szArglist[i][0] != '/')
357 if (!sPathArgument.IsEmpty())
358 sPathArgument += '*';
359 sPathArgument += szArglist[i];
362 sPathArgument.Replace(L"\\\\", L"\\");
364 LocalFree(szArglist);
366 if (sPathArgument.IsEmpty() && parser.HasKey(L"path"))
368 CMessageBox::Show(hWndExplorer, IDS_ERR_INVALIDPATH, IDS_APPNAME, MB_ICONERROR);
369 return FALSE;
371 int asterisk = sPathArgument.Find('*');
372 cmdLinePath.SetFromUnknown(asterisk >= 0 ? sPathArgument.Left(asterisk) : sPathArgument);
373 pathList.LoadFromAsteriskSeparatedString(sPathArgument);
376 if (pathList.IsEmpty()) {
377 pathList.AddPath(CTGitPath::CTGitPath(g_Git.m_CurrentDir));
380 // Set CWD to temporary dir, and restore it later
382 DWORD len = GetCurrentDirectory(0, nullptr);
383 if (len)
385 auto originalCurrentDirectory = std::make_unique<TCHAR[]>(len);
386 if (GetCurrentDirectory(len, originalCurrentDirectory.get()))
388 sOrigCWD = originalCurrentDirectory.get();
389 sOrigCWD = CPathUtils::GetLongPathname(sOrigCWD);
392 TCHAR pathbuf[MAX_PATH] = {0};
393 GetTortoiseGitTempPath(_countof(pathbuf), pathbuf);
394 SetCurrentDirectory(pathbuf);
397 CheckForNewerVersion();
399 CAutoGeneralHandle TGitMutex = ::CreateMutex(nullptr, FALSE, L"TortoiseGitProc.exe");
400 if (!g_Git.SetCurrentDir(cmdLinePath.GetWinPathString(), parser.HasKey(L"submodule") == TRUE))
402 for (int i = 0; i < pathList.GetCount(); ++i)
403 if(g_Git.SetCurrentDir(pathList[i].GetWinPath()))
404 break;
406 if (parser.HasKey(L"pathfile") && parser.HasKey(L"submodule"))
407 g_Git.SetCurrentDir(pathList[0].GetWinPathString(), true);
409 if(!g_Git.m_CurrentDir.IsEmpty())
411 sOrigCWD = g_Git.m_CurrentDir;
412 SetCurrentDirectory(g_Git.m_CurrentDir);
415 if (g_sGroupingUUID.IsEmpty())
417 CRegStdDWORD groupSetting = CRegStdDWORD(L"Software\\TortoiseGit\\GroupTaskbarIconsPerRepo", 3);
418 switch (DWORD(groupSetting))
420 case 1:
421 case 2:
422 // implemented differently to TortoiseSVN atm
423 break;
424 case 3:
425 case 4:
427 CString wcroot;
428 if (GitAdminDir::HasAdminDir(g_Git.m_CurrentDir, true, &wcroot))
430 git_oid oid;
431 CStringA wcRootA(wcroot + CPathUtils::GetAppDirectory());
432 if (!git_odb_hash(&oid, wcRootA.MakeLower(), wcRootA.GetLength(), GIT_OBJ_BLOB))
434 CStringA hash;
435 git_oid_tostr(CStrBufA(hash, GIT_OID_HEXSZ, CStrBufA::SET_LENGTH), GIT_OID_HEXSZ + 1, &oid);
436 g_sGroupingUUID = hash;
438 ProjectProperties pp;
439 pp.ReadProps();
440 CString icon = pp.sIcon;
441 icon.Replace('/', '\\');
442 if (icon.IsEmpty())
443 g_bGroupingRemoveIcon = true;
444 g_sGroupingIcon = icon;
450 CString sAppID = GetTaskIDPerUUID(g_sGroupingUUID).c_str();
451 InitializeJumpList(sAppID);
452 EnsureGitLibrary(false);
455 CString err;
458 // requires CWD to be set
459 CGit::m_LogEncode = CAppUtils::GetLogOutputEncode();
461 // make sure all config files are read in order to check that none contains an error
462 g_Git.GetConfigValue(L"doesnot.exist");
464 catch (char* msg)
466 err = CString(msg);
469 if (!err.IsEmpty())
471 UINT choice = CMessageBox::Show(hWndExplorer, err, L"TortoiseGit", 1, IDI_ERROR, CString(MAKEINTRESOURCE(IDS_PROC_EDITLOCALGITCONFIG)), CString(MAKEINTRESOURCE(IDS_PROC_EDITGLOBALGITCONFIG)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON)));
472 if (choice == 1)
474 // open the config file with alternative editor
475 CAppUtils::LaunchAlternativeEditor(g_Git.GetGitLocalConfig());
477 else if (choice == 2)
479 // open the global config file with alternative editor
480 CAppUtils::LaunchAlternativeEditor(g_Git.GetGitGlobalConfig());
482 return FALSE;
486 // execute the requested command
487 CommandServer server;
488 Command* cmd = server.GetCommand(parser.GetVal(L"command"));
489 if (cmd)
491 cmd->SetExplorerHwnd(hWndExplorer);
493 cmd->SetParser(parser);
494 cmd->SetPaths(pathList, cmdLinePath);
496 retSuccess = cmd->Execute();
497 delete cmd;
500 // Look for temporary files left around by TortoiseSVN and
501 // remove them. But only delete 'old' files because some
502 // apps might still be needing the recent ones.
504 DWORD len = GetTortoiseGitTempPath(0, nullptr);
505 auto path = std::make_unique<TCHAR[]>(len + 100);
506 len = GetTortoiseGitTempPath (len + 100, path.get());
507 if (len != 0)
509 CDirFileEnum finder(path.get());
510 FILETIME systime_;
511 ::GetSystemTimeAsFileTime(&systime_);
512 __int64 systime = (((_int64)systime_.dwHighDateTime)<<32) | ((__int64)systime_.dwLowDateTime);
513 bool isDir;
514 CString filepath;
515 while (finder.NextFile(filepath, &isDir))
517 HANDLE hFile = ::CreateFile(filepath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, isDir ? FILE_FLAG_BACKUP_SEMANTICS : 0, nullptr);
518 if (hFile != INVALID_HANDLE_VALUE)
520 FILETIME createtime_;
521 if (::GetFileTime(hFile, &createtime_, nullptr, nullptr))
523 ::CloseHandle(hFile);
524 __int64 createtime = (((_int64)createtime_.dwHighDateTime)<<32) | ((__int64)createtime_.dwLowDateTime);
525 if ((createtime + 864000000000) < systime) //only delete files older than a day
527 ::SetFileAttributes(filepath, FILE_ATTRIBUTE_NORMAL);
528 if (isDir)
529 ::RemoveDirectory(filepath);
530 else
531 ::DeleteFile(filepath);
534 else
535 ::CloseHandle(hFile);
541 // Since the dialog has been closed, return FALSE so that we exit the
542 // application, rather than start the application's message pump.
543 return FALSE;
546 void CTortoiseProcApp::CheckUpgrade()
548 CRegString regVersion = CRegString(L"Software\\TortoiseGit\\CurrentVersion");
549 CString sVersion = regVersion;
550 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Current TGit Version %s\n", (LPCTSTR)sVersion);
551 if (sVersion.Compare(_T(STRPRODUCTVER))==0)
552 return;
553 // we're starting the first time with a new version!
555 LONG lVersion = 0;
556 int pos = sVersion.Find('.');
557 if (pos > 0)
559 lVersion = (_wtol(sVersion.Left(pos)) << 24);
560 lVersion |= (_wtol(sVersion.Mid(pos + 1)) << 16);
561 pos = sVersion.Find('.', pos+1);
562 lVersion |= (_wtol(sVersion.Mid(pos + 1)) << 8);
564 else
566 pos = sVersion.Find(',');
567 if (pos > 0)
569 lVersion = (_wtol(sVersion.Left(pos)) << 24);
570 lVersion |= (_wtol(sVersion.Mid(pos + 1)) << 16);
571 pos = sVersion.Find(',', pos+1);
572 lVersion |= (_wtol(sVersion.Mid(pos + 1)) << 8);
576 // generic cleanup
577 if (CRegStdDWORD(L"Software\\TortoiseGit\\UseLibgit2", TRUE) != TRUE)
579 if (CMessageBox::Show(nullptr, L"You have disabled the usage of libgit2 in TortoiseGit.\n\nThis might be the case in order to resolve an issue in an older TortoiseGit version.\n\nDo you want to restore the default value (i.e., enable it)?", L"TortoiseGit", MB_ICONQUESTION | MB_YESNO) == IDYES)
580 CRegStdDWORD(L"Software\\TortoiseGit\\UseLibgit2").removeValue();
583 if (CRegStdDWORD(L"Software\\TortoiseGit\\UseLibgit2_mask").exists())
585 if (CMessageBox::Show(nullptr, L"You have a non-default setting of UseLibgit2_mask in your registry.\n\nThis might be the case in order to resolve an issue in an older TortoiseGit version.\n\nDo you want to restore the default value (i.e., remove custom setting from registry)?", L"TortoiseGit", MB_ICONQUESTION | MB_YESNO) == IDYES)
586 CRegStdDWORD(L"Software\\TortoiseGit\\UseLibgit2_mask").removeValue();
589 CMessageBox::RemoveRegistryKey(L"OldMsysgitVersionWarning");
591 CRegDWORD checkNewerWeekDay = CRegDWORD(L"Software\\TortoiseGit\\CheckNewerWeekDay", 0);
592 if (!checkNewerWeekDay.exists() || lVersion <= 0x01081000)
594 std::random_device rd;
595 std::mt19937 mt(rd());
596 std::uniform_int_distribution<int> dist(0, 6);
597 checkNewerWeekDay = dist(mt);
600 // version specific updates
601 if (lVersion <= 0x02040000)
603 CRegStdDWORD commmitAskBeforeCancel(L"Software\\TortoiseGit\\CommitAskBeforeCancel");
604 if (commmitAskBeforeCancel.exists() && commmitAskBeforeCancel != IDYES)
605 commmitAskBeforeCancel = IDYES;
608 if (lVersion <= 0x02020100)
610 CString username = CRegString(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\Username", L"");
611 CString password = CRegString(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\Password", L"");
612 if (!username.IsEmpty() && !password.IsEmpty())
614 if (CWindowsCredentialsStore::SaveCredential(L"TortoiseGit:SMTP-Credentials", username, password) == 0)
616 CRegString(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\Username").removeValue();
617 CRegString(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\Password").removeValue();
620 for (const CString& setting : { L"SyncIn", L"SyncOut" })
622 CRegDWORD reg(L"Software\\TortoiseGit\\StatusColumns\\" + setting + L"loglistVersion", 0xff);
623 if ((DWORD)reg == 6)
624 reg.removeValue();
628 if (lVersion <= 0x02010500)
630 // We updated GITSLC_COL_VERSION, but only significant changes were made for GitStatusList
631 // so, smoothly migrate GitLoglistBase settings
632 for (const CString& setting : { L"log", L"Blame", L"Rebase", L"reflog", L"SyncIn", L"SyncOut" })
634 CRegDWORD reg(L"Software\\TortoiseGit\\StatusColumns\\" + setting + L"loglistVersion", 0xff);
635 if ((DWORD)reg == 5)
636 reg = 6;
640 if (lVersion <= 0x01090000)
642 if (CRegDWORD(L"Software\\TortoiseGit\\TGitCacheCheckContent", TRUE) == FALSE)
644 CRegDWORD(L"Software\\TortoiseGit\\TGitCacheCheckContentMaxSize") = 0;
645 CRegDWORD(L"Software\\TortoiseGit\\TGitCacheCheckContent").removeValue();
649 if (lVersion <= 0x01080801)
651 CRegStdDWORD(L"Software\\TortoiseGit\\StatusColumns\\BrowseRefs").removeValue();
652 CRegStdString(L"Software\\TortoiseGit\\StatusColumns\\BrowseRefs_Order").removeValue();
653 CRegStdString(L"Software\\TortoiseGit\\StatusColumns\\BrowseRefs_Width").removeValue();
656 if (lVersion <= 0x01080401)
658 if (CRegStdDWORD(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\UseMAPI", FALSE) == TRUE)
659 CRegStdDWORD(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\DeliveryType") = SEND_MAIL_MAPI;
660 CRegStdDWORD(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\UseMAPI").removeValue();
663 if (lVersion <= 0x01080202)
665 // upgrade to 1.8.3: force recreation of all diff scripts.
666 CAppUtils::SetupDiffScripts(true, CString());
669 if (lVersion <= 0x01080100)
671 if (CRegStdDWORD(L"Software\\TortoiseGit\\LogTopoOrder", TRUE) == FALSE)
672 CRegStdDWORD(L"Software\\TortoiseGit\\LogOrderBy") = 0;
674 // smoothly migrate broken msysgit path settings
675 CString oldmsysGitSetting = CRegString(REG_MSYSGIT_PATH);
676 oldmsysGitSetting.TrimRight(L'\\');
677 if (oldmsysGitSetting.GetLength() > 4 && CStringUtils::EndsWith(oldmsysGitSetting, L"\\cmd"))
679 CString newPath = oldmsysGitSetting.Mid(0, oldmsysGitSetting.GetLength() - 3) + L"bin";
680 if (PathFileExists(newPath + L"\\git.exe"))
682 CRegString(REG_MSYSGIT_PATH) = newPath;
683 g_Git.m_bInitialized = FALSE;
684 g_Git.CheckMsysGitDir();
689 if (lVersion <= 0x01040000)
691 CRegStdDWORD(L"Software\\TortoiseGit\\OwnerdrawnMenus").removeValue();
694 if (lVersion <= 0x01070600)
696 CoInitialize(nullptr);
697 EnsureGitLibrary();
698 CoUninitialize();
699 CRegStdDWORD(L"Software\\TortoiseGit\\ConvertBase").removeValue();
700 CRegStdDWORD(L"Software\\TortoiseGit\\DiffProps").removeValue();
701 if (CRegStdDWORD(L"Software\\TortoiseGit\\CheckNewer", TRUE) == FALSE)
702 CRegStdDWORD(L"Software\\TortoiseGit\\VersionCheck") = FALSE;
703 CRegStdDWORD(L"Software\\TortoiseGit\\CheckNewer").removeValue();
706 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Setting up diff scripts ...\n");
707 CAppUtils::SetupDiffScripts(false, CString());
709 // set the current version so we don't come here again until the next update!
710 regVersion = _T(STRPRODUCTVER);
713 void CTortoiseProcApp::InitializeJumpList(const CString& appid)
715 // for Win7 : use a custom jump list
716 CoInitialize(nullptr);
717 SetAppID(appid);
718 DeleteJumpList(appid);
719 DoInitializeJumpList(appid);
720 CoUninitialize();
723 void CTortoiseProcApp::DoInitializeJumpList(const CString& appid)
725 ATL::CComPtr<ICustomDestinationList> pcdl;
726 HRESULT hr = pcdl.CoCreateInstance(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER);
727 if (FAILED(hr))
728 return;
730 hr = pcdl->SetAppID(appid);
731 if (FAILED(hr))
732 return;
734 UINT uMaxSlots;
735 ATL::CComPtr<IObjectArray> poaRemoved;
736 hr = pcdl->BeginList(&uMaxSlots, IID_PPV_ARGS(&poaRemoved));
737 if (FAILED(hr))
738 return;
740 ATL::CComPtr<IObjectCollection> poc;
741 hr = poc.CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER);
742 if (FAILED(hr))
743 return;
745 CString sTemp = CString(MAKEINTRESOURCE(IDS_MENUSETTINGS));
746 CStringUtils::RemoveAccelerators(sTemp);
748 ATL::CComPtr<IShellLink> psl;
749 hr = CreateShellLink(L"/command:settings", (LPCTSTR)sTemp, 20, &psl);
750 if (SUCCEEDED(hr)) {
751 poc->AddObject(psl);
753 sTemp = CString(MAKEINTRESOURCE(IDS_MENUHELP));
754 CStringUtils::RemoveAccelerators(sTemp);
755 psl.Release(); // Need to release the object before calling operator&()
756 hr = CreateShellLink(L"/command:help", (LPCTSTR)sTemp, 19, &psl);
757 if (SUCCEEDED(hr)) {
758 poc->AddObject(psl);
761 ATL::CComPtr<IObjectArray> poa;
762 hr = poc.QueryInterface(&poa);
763 if (SUCCEEDED(hr)) {
764 pcdl->AppendCategory((LPCTSTR)CString(MAKEINTRESOURCE(IDS_PROC_TASKS)), poa);
765 pcdl->CommitList();
769 int CTortoiseProcApp::ExitInstance()
771 SYS_IMAGE_LIST().Cleanup();
772 Gdiplus::GdiplusShutdown(m_gdiplusToken);
774 CWinAppEx::ExitInstance();
775 if (retSuccess)
776 return 0;
777 return -1;
780 void CTortoiseProcApp::CheckForNewerVersion()
782 // check for newer versions
783 if (CRegDWORD(L"Software\\TortoiseGit\\VersionCheck", TRUE) != FALSE)
785 time_t now;
786 struct tm ptm;
788 time(&now);
789 if ((now != 0) && (localtime_s(&ptm, &now)==0))
791 #if PREVIEW
792 // Check daily for new preview releases
793 CRegDWORD oldday = CRegDWORD(L"Software\\TortoiseGit\\CheckNewerDay", (DWORD)-1);
794 if (((DWORD)oldday) == -1)
795 oldday = ptm.tm_yday;
796 else
798 if ((DWORD)oldday != (DWORD)ptm.tm_yday)
800 oldday = ptm.tm_yday;
801 #else
802 int week = 0;
803 // we don't calculate the real 'week of the year' here
804 // because just to decide if we should check for an update
805 // that's not needed.
806 week = (ptm.tm_yday + CRegDWORD(L"Software\\TortoiseGit\\CheckNewerWeekDay", 0)) / 7;
808 CRegDWORD oldweek = CRegDWORD(L"Software\\TortoiseGit\\CheckNewerWeek", (DWORD)-1);
809 if (((DWORD)oldweek) == -1)
810 oldweek = week; // first start of TortoiseProc, no update check needed
811 else
813 if ((DWORD)week != oldweek)
815 oldweek = week;
816 #endif
817 CAppUtils::RunTortoiseGitProc(L"/command:updatecheck", false, false);