Fixed issue #3304: Double click on stash list item does nothing, show log instead
[TortoiseGit.git] / src / TortoiseProc / TortoiseProc.cpp
blobfff92fcd556db76666388f99ee6e7f7f110aafa9
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2018 - 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"
48 #include "AnimationManager.h"
50 #define STRUCT_IOVEC_DEFINED
52 #ifdef _DEBUG
53 #define new DEBUG_NEW
54 #endif
56 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
58 BEGIN_MESSAGE_MAP(CTortoiseProcApp, CWinAppEx)
59 ON_COMMAND(ID_HELP, CWinAppEx::OnHelp)
60 END_MESSAGE_MAP()
62 //////////////////////////////////////////////////////////////////////////
64 CTortoiseProcApp::CTortoiseProcApp()
65 : hWndExplorer(nullptr)
67 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Constructor\n");
68 SetDllDirectory(L"");
69 CCrashReport::Instance().AddUserInfoToReport(L"CommandLine", GetCommandLine());
70 EnableHtmlHelp();
71 CHooks::Create();
72 git_libgit2_init();
73 CGit::SetGit2CredentialCallback(CAppUtils::Git2GetUserPassword);
74 CGit::SetGit2CertificateCheckCertificate(CAppUtils::Git2CertificateCheck);
75 m_bLoadUserToolbars = FALSE;
76 m_bSaveState = FALSE;
77 retSuccess = false;
78 m_gdiplusToken = NULL;
79 #if defined (_WIN64) && _MSC_VER == 1800
80 _set_FMA3_enable(0);
81 #endif
84 CTortoiseProcApp::~CTortoiseProcApp()
86 CHooks::Destroy();
87 git_libgit2_shutdown();
90 // The one and only CTortoiseProcApp object
91 CTortoiseProcApp theApp;
92 CString sOrigCWD;
93 CString g_sGroupingUUID;
94 CString g_sGroupingIcon;
95 bool g_bGroupingRemoveIcon = false;
96 HWND GetExplorerHWND()
98 return theApp.GetExplorerHWND();
101 #if ENABLE_CRASHHANLDER
102 CCrashReportTGit crasher(L"TortoiseGit " _T(APP_X64_STRING), TGIT_VERMAJOR, TGIT_VERMINOR, TGIT_VERMICRO, TGIT_VERBUILD, TGIT_VERDATE);
103 #endif
105 // CTortoiseProcApp initialization
107 BOOL CTortoiseProcApp::InitInstance()
109 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": InitInstance\n");
110 CheckUpgrade();
111 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
112 CMFCButton::EnableWindowsTheming();
114 Gdiplus::GdiplusStartupInput gdiplusStartupInput;
115 Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, nullptr);
117 //set the resource dll for the required language
118 CRegDWORD loc = CRegDWORD(L"Software\\TortoiseGit\\LanguageID", 1033);
119 long langId = loc;
120 CString langDll;
121 CStringA langpath = CStringA(CPathUtils::GetAppParentDirectory());
122 langpath += "Languages";
125 langDll.Format(L"%sLanguages\\TortoiseProc%ld.dll", (LPCTSTR)CPathUtils::GetAppParentDirectory(), langId);
127 CString sVer = _T(STRPRODUCTVER);
128 CString sFileVer = CPathUtils::GetVersionFromFile(langDll);
129 if (sFileVer == sVer)
131 HINSTANCE hInst = LoadLibrary(langDll);
132 if (hInst)
134 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Load Language DLL %s\n", (LPCTSTR)langDll);
135 AfxSetResourceHandle(hInst);
136 break;
140 DWORD lid = SUBLANGID(langId);
141 lid--;
142 if (lid > 0)
143 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
144 else
145 langId = 0;
147 } while (langId != 0);
149 CString langStr;
150 langStr.Format(L"%ld", langId);
151 CCrashReport::Instance().AddUserInfoToReport(L"LanguageID", langStr);
153 TCHAR buf[6] = { 0 };
154 wcscpy_s(buf, L"en");
155 langId = loc;
156 // MFC uses a help file with the same name as the application by default,
157 // which means we have to change that default to our language specific help files
158 CString sHelppath = CPathUtils::GetAppDirectory() + L"TortoiseGit_en.chm";
159 free((void*)m_pszHelpFilePath);
160 m_pszHelpFilePath=_wcsdup(sHelppath);
161 sHelppath = CPathUtils::GetAppParentDirectory() + L"Languages\\TortoiseGit_en.chm";
164 CString sLang = L"_";
165 if (GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO639LANGNAME, buf, _countof(buf)))
167 sLang += buf;
168 sHelppath.Replace(L"_en", sLang);
169 if (PathFileExists(sHelppath))
171 free((void*)m_pszHelpFilePath);
172 m_pszHelpFilePath=_wcsdup(sHelppath);
173 break;
176 sHelppath.Replace(sLang, L"_en");
177 if (GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO3166CTRYNAME, buf, _countof(buf)))
179 sLang += L'_';
180 sLang += buf;
181 sHelppath.Replace(L"_en", sLang);
182 if (PathFileExists(sHelppath))
184 free((void*)m_pszHelpFilePath);
185 m_pszHelpFilePath=_wcsdup(sHelppath);
186 break;
189 sHelppath.Replace(sLang, L"_en");
191 DWORD lid = SUBLANGID(langId);
192 lid--;
193 if (lid > 0)
194 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
195 else
196 langId = 0;
197 } while (langId);
198 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Set Help Filename %s\n", m_pszHelpFilePath);
199 setlocale(LC_ALL, "");
201 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Initializing UI components ...\n");
202 // InitCommonControls() is required on Windows XP if an application
203 // manifest specifies use of ComCtl32.dll version 6 or later to enable
204 // visual styles. Otherwise, any window creation will fail.
206 INITCOMMONCONTROLSEX used = {
207 sizeof(INITCOMMONCONTROLSEX),
208 ICC_ANIMATE_CLASS | ICC_BAR_CLASSES | ICC_COOL_CLASSES | ICC_DATE_CLASSES |
209 ICC_HOTKEY_CLASS | ICC_INTERNET_CLASSES | ICC_LISTVIEW_CLASSES |
210 ICC_NATIVEFNTCTL_CLASS | ICC_PAGESCROLLER_CLASS | ICC_PROGRESS_CLASS |
211 ICC_TAB_CLASSES | ICC_TREEVIEW_CLASSES | ICC_UPDOWN_CLASS |
212 ICC_USEREX_CLASSES | ICC_WIN95_CLASSES
214 InitCommonControlsEx(&used);
215 AfxOleInit();
216 AfxEnableControlContainer();
217 AfxInitRichEdit5();
218 CWinAppEx::InitInstance();
219 SetRegistryKey(L"TortoiseGit");
220 SYS_IMAGE_LIST();
221 CHistoryCombo::m_nGitIconIndex = SYS_IMAGE_LIST().AddIcon(CCommonAppUtils::LoadIconEx(IDI_GITCONFIG, 0, 0));
222 AfxGetApp()->m_pszProfileName = _wcsdup(L"TortoiseProc"); // w/o this ResizableLib will store data under TortoiseGitProc which is not compatible with older versions
224 CCmdLineParser parser(AfxGetApp()->m_lpCmdLine);
226 hWndExplorer = nullptr;
227 CString sVal = parser.GetVal(L"hwnd");
228 if (!sVal.IsEmpty())
229 hWndExplorer = (HWND)_wcstoui64(sVal, nullptr, 16);
231 while (GetParent(hWndExplorer))
232 hWndExplorer = GetParent(hWndExplorer);
233 if (!IsWindow(hWndExplorer))
234 hWndExplorer = nullptr;
236 // if HKCU\Software\TortoiseGit\Debug is not 0, show our command line
237 // in a message box
238 if (CRegDWORD(L"Software\\TortoiseGit\\Debug", FALSE) == TRUE)
239 AfxMessageBox(AfxGetApp()->m_lpCmdLine, MB_OK | MB_ICONINFORMATION);
241 if (parser.HasKey(L"command") && wcscmp(parser.GetVal(L"command"), L"firststart") == 0)
243 // CFirstStartWizard requires sOrigCWD to be set
244 DWORD len = GetCurrentDirectory(0, nullptr);
245 if (len)
247 auto originalCurrentDirectory = std::make_unique<TCHAR[]>(len);
248 if (GetCurrentDirectory(len, originalCurrentDirectory.get()))
250 sOrigCWD = originalCurrentDirectory.get();
251 sOrigCWD = CPathUtils::GetLongPathname(sOrigCWD);
254 CFirstStartWizard wizard(IDS_APPNAME, CWnd::FromHandle(hWndExplorer), parser.GetLongVal(L"page"));
255 theApp.m_pMainWnd = &wizard;
256 return (wizard.DoModal() == ID_WIZFINISH);
259 if (!g_Git.CheckMsysGitDir())
261 UINT ret = CMessageBox::Show(hWndExplorer, IDS_PROC_NOMSYSGIT, IDS_APPNAME, 3, IDI_HAND, IDS_PROC_SETMSYSGITPATH, IDS_PROC_GOTOMSYSGITWEBSITE, IDS_ABORTBUTTON);
262 if (ret == 2)
263 ShellExecute(hWndExplorer, L"open", GIT_FOR_WINDOWS_URL, nullptr, nullptr, SW_SHOW);
264 else if (ret == 1)
266 CFirstStartWizard wizard(IDS_APPNAME, CWnd::FromHandle(hWndExplorer), 2);
267 theApp.m_pMainWnd = &wizard;
268 wizard.DoModal();
270 return FALSE;
272 if (!CConfigureGitExe::CheckGitVersion(hWndExplorer))
273 return FALSE;
276 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Registering Crash Report ...\n");
277 CCrashReport::Instance().AddUserInfoToReport(L"msysGitDir", CGit::ms_LastMsysGitDir);
278 CString versionString;
279 versionString.Format(L"%X", CGit::ms_LastMsysGitVersion);
280 CCrashReport::Instance().AddUserInfoToReport(L"msysGitVersion", versionString);
281 if (CGit::ms_bCygwinGit)
282 CCrashReport::Instance().AddUserInfoToReport(L"CygwinHack", L"true");
283 if (CGit::ms_bMsys2Git)
284 CCrashReport::Instance().AddUserInfoToReport(L"Msys2Hack", L"true");
287 if (parser.HasKey(L"urlhandler"))
289 CString url = parser.GetVal(L"urlhandler");
290 if (CStringUtils::StartsWith(url, L"tgit://clone/"))
291 url = url.Mid((int)wcslen(L"tgit://clone/"));
292 else if (CStringUtils::StartsWith(url, L"github-windows://openRepo/"))
294 url = url.Mid((int)wcslen(L"github-windows://openRepo/"));
295 int questioMark = url.Find('?');
296 if (questioMark > 0)
297 url = url.Left(questioMark);
299 else if (CStringUtils::StartsWith(url, L"x-github-client://openRepo/")) {
300 url = url.Mid((int)wcslen(L"x-github-client://openRepo/"));
301 int questioMark = url.Find('?');
302 if (questioMark > 0)
303 url = url.Left(questioMark);
305 else if (CStringUtils::StartsWith(url, L"smartgit://cloneRepo/"))
306 url = url.Mid((int)wcslen(L"smartgit://cloneRepo/"));
307 else
309 CMessageBox::Show(nullptr, IDS_ERR_INVALIDPATH, IDS_APPNAME, MB_ICONERROR);
310 return FALSE;
312 CString newCmd;
313 newCmd.Format(L"/command:clone /url:\"%s\" /hasurlhandler", (LPCTSTR)url);
314 parser = CCmdLineParser(newCmd);
317 if (parser.HasKey(L"path") && parser.HasKey(L"pathfile"))
319 CMessageBox::Show(nullptr, IDS_ERR_INVALIDPATH, IDS_APPNAME, MB_ICONERROR);
320 return FALSE;
323 CTGitPath cmdLinePath;
324 CTGitPathList pathList;
325 if (g_sGroupingUUID.IsEmpty())
326 g_sGroupingUUID = parser.GetVal(L"groupuuid");
327 if (parser.HasKey(L"pathfile"))
329 CString sPathfileArgument = CPathUtils::GetLongPathname(parser.GetVal(L"pathfile"));
331 cmdLinePath.SetFromUnknown(sPathfileArgument);
332 if (pathList.LoadFromFile(cmdLinePath)==false)
333 return FALSE; // no path specified!
334 if (parser.HasKey(L"deletepathfile"))
336 // We can delete the temporary path file, now that we've loaded it
337 ::DeleteFile(cmdLinePath.GetWinPath());
339 // This was a path to a temporary file - it's got no meaning now, and
340 // anybody who uses it again is in for a problem...
341 cmdLinePath.Reset();
344 else
346 CString sPathArgument = CPathUtils::GetLongPathname(parser.GetVal(L"path"));
347 if (parser.HasKey(L"expaths"))
349 // an /expaths param means we're started via the buttons in our Win7 library
350 // and that means the value of /expaths is the current directory, and
351 // the selected paths are then added as additional parameters but without a key, only a value
353 // because of the "strange treatment of quotation marks and backslashes by CommandLineToArgvW"
354 // we have to escape the backslashes first. Since we're only dealing with paths here, that's
355 // a save bet.
356 // Without this, a command line like:
357 // /command:commit /expaths:"D:\" "D:\Utils"
358 // would fail because the "D:\" is treated as the backslash being the escape char for the quotation
359 // mark and we'd end up with:
360 // argv[1] = /command:commit
361 // argv[2] = /expaths:D:" D:\Utils
362 // See here for more details: http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
363 CString cmdLine = GetCommandLineW();
364 cmdLine.Replace(L"\\", L"\\\\");
365 int nArgs = 0;
366 LPWSTR *szArglist = CommandLineToArgvW(cmdLine, &nArgs);
367 if (szArglist)
369 // argument 0 is the process path, so start with 1
370 for (int i = 1; i < nArgs; ++i)
372 if (szArglist[i][0] != '/')
374 if (!sPathArgument.IsEmpty())
375 sPathArgument += '*';
376 sPathArgument += szArglist[i];
379 sPathArgument.Replace(L"\\\\", L"\\");
381 LocalFree(szArglist);
383 if (sPathArgument.IsEmpty() && parser.HasKey(L"path"))
385 CMessageBox::Show(hWndExplorer, IDS_ERR_INVALIDPATH, IDS_APPNAME, MB_ICONERROR);
386 return FALSE;
388 int asterisk = sPathArgument.Find('*');
389 cmdLinePath.SetFromUnknown(asterisk >= 0 ? sPathArgument.Left(asterisk) : sPathArgument);
390 pathList.LoadFromAsteriskSeparatedString(sPathArgument);
393 if (pathList.IsEmpty()) {
394 pathList.AddPath(CTGitPath::CTGitPath(g_Git.m_CurrentDir));
397 // Set CWD to temporary dir, and restore it later
399 DWORD len = GetCurrentDirectory(0, nullptr);
400 if (len)
402 auto originalCurrentDirectory = std::make_unique<TCHAR[]>(len);
403 if (GetCurrentDirectory(len, originalCurrentDirectory.get()))
405 sOrigCWD = originalCurrentDirectory.get();
406 sOrigCWD = CPathUtils::GetLongPathname(sOrigCWD);
409 TCHAR pathbuf[MAX_PATH] = {0};
410 GetTortoiseGitTempPath(_countof(pathbuf), pathbuf);
411 SetCurrentDirectory(pathbuf);
414 CheckForNewerVersion();
416 CAutoGeneralHandle TGitMutex = ::CreateMutex(nullptr, FALSE, L"TortoiseGitProc.exe");
417 if (!g_Git.SetCurrentDir(cmdLinePath.GetWinPathString(), parser.HasKey(L"submodule") == TRUE))
419 for (int i = 0; i < pathList.GetCount(); ++i)
420 if(g_Git.SetCurrentDir(pathList[i].GetWinPath()))
421 break;
423 if (parser.HasKey(L"pathfile") && parser.HasKey(L"submodule"))
424 g_Git.SetCurrentDir(pathList[0].GetWinPathString(), true);
426 if(!g_Git.m_CurrentDir.IsEmpty())
428 sOrigCWD = g_Git.m_CurrentDir;
429 SetCurrentDirectory(g_Git.m_CurrentDir);
432 if (g_sGroupingUUID.IsEmpty())
434 CRegStdDWORD groupSetting = CRegStdDWORD(L"Software\\TortoiseGit\\GroupTaskbarIconsPerRepo", 3);
435 switch (DWORD(groupSetting))
437 case 1:
438 case 2:
439 // implemented differently to TortoiseSVN atm
440 break;
441 case 3:
442 case 4:
444 CString wcroot;
445 if (GitAdminDir::HasAdminDir(g_Git.m_CurrentDir, true, &wcroot))
447 git_oid oid;
448 CStringA wcRootA(wcroot + CPathUtils::GetAppDirectory());
449 if (!git_odb_hash(&oid, wcRootA.MakeLower(), wcRootA.GetLength(), GIT_OBJ_BLOB))
451 CStringA hash;
452 git_oid_tostr(CStrBufA(hash, GIT_OID_HEXSZ, CStrBufA::SET_LENGTH), GIT_OID_HEXSZ + 1, &oid);
453 g_sGroupingUUID = hash;
455 ProjectProperties pp;
456 pp.ReadProps();
457 CString icon = pp.sIcon;
458 icon.Replace('/', '\\');
459 if (icon.IsEmpty())
460 g_bGroupingRemoveIcon = true;
461 g_sGroupingIcon = icon;
467 CString sAppID = GetTaskIDPerUUID(g_sGroupingUUID).c_str();
468 InitializeJumpList(sAppID);
469 EnsureGitLibrary(false);
472 CString err;
475 // requires CWD to be set
476 CGit::m_LogEncode = CAppUtils::GetLogOutputEncode();
478 // make sure all config files are read in order to check that none contains an error
479 g_Git.GetConfigValue(L"doesnot.exist");
481 catch (char* msg)
483 err = CString(msg);
486 if (!err.IsEmpty())
488 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)));
489 if (choice == 1)
491 // open the config file with alternative editor
492 CAppUtils::LaunchAlternativeEditor(g_Git.GetGitLocalConfig());
494 else if (choice == 2)
496 // open the global config file with alternative editor
497 CAppUtils::LaunchAlternativeEditor(g_Git.GetGitGlobalConfig());
499 return FALSE;
503 // execute the requested command
504 CommandServer server;
505 Command* cmd = server.GetCommand(parser.GetVal(L"command"));
506 if (cmd)
508 cmd->SetExplorerHwnd(hWndExplorer);
510 cmd->SetParser(parser);
511 cmd->SetPaths(pathList, cmdLinePath);
513 retSuccess = cmd->Execute();
514 delete cmd;
517 // Look for temporary files left around by TortoiseSVN and
518 // remove them. But only delete 'old' files because some
519 // apps might still be needing the recent ones.
521 DWORD len = GetTortoiseGitTempPath(0, nullptr);
522 auto path = std::make_unique<TCHAR[]>(len + 100);
523 len = GetTortoiseGitTempPath (len + 100, path.get());
524 if (len != 0)
526 CDirFileEnum finder(path.get());
527 FILETIME systime_;
528 ::GetSystemTimeAsFileTime(&systime_);
529 __int64 systime = (((_int64)systime_.dwHighDateTime)<<32) | ((__int64)systime_.dwLowDateTime);
530 bool isDir;
531 CString filepath;
532 while (finder.NextFile(filepath, &isDir))
534 HANDLE hFile = ::CreateFile(filepath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, isDir ? FILE_FLAG_BACKUP_SEMANTICS : 0, nullptr);
535 if (hFile != INVALID_HANDLE_VALUE)
537 FILETIME createtime_;
538 if (::GetFileTime(hFile, &createtime_, nullptr, nullptr))
540 ::CloseHandle(hFile);
541 __int64 createtime = (((_int64)createtime_.dwHighDateTime)<<32) | ((__int64)createtime_.dwLowDateTime);
542 if ((createtime + 864000000000) < systime) //only delete files older than a day
544 ::SetFileAttributes(filepath, FILE_ATTRIBUTE_NORMAL);
545 if (isDir)
546 ::RemoveDirectory(filepath);
547 else
548 ::DeleteFile(filepath);
551 else
552 ::CloseHandle(hFile);
558 Animator::Instance().ShutDown();
560 // Since the dialog has been closed, return FALSE so that we exit the
561 // application, rather than start the application's message pump.
562 return FALSE;
565 void CTortoiseProcApp::CheckUpgrade()
567 CRegString regVersion = CRegString(L"Software\\TortoiseGit\\CurrentVersion");
568 CString sVersion = regVersion;
569 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Current TGit Version %s\n", (LPCTSTR)sVersion);
570 if (sVersion.Compare(_T(STRPRODUCTVER))==0)
571 return;
572 // we're starting the first time with a new version!
574 LONG lVersion = 0;
575 int pos = sVersion.Find('.');
576 if (pos > 0)
578 lVersion = (_wtol(sVersion.Left(pos)) << 24);
579 lVersion |= (_wtol(sVersion.Mid(pos + 1)) << 16);
580 pos = sVersion.Find('.', pos+1);
581 lVersion |= (_wtol(sVersion.Mid(pos + 1)) << 8);
583 else
585 pos = sVersion.Find(',');
586 if (pos > 0)
588 lVersion = (_wtol(sVersion.Left(pos)) << 24);
589 lVersion |= (_wtol(sVersion.Mid(pos + 1)) << 16);
590 pos = sVersion.Find(',', pos+1);
591 lVersion |= (_wtol(sVersion.Mid(pos + 1)) << 8);
595 // generic cleanup
596 if (CRegStdDWORD(L"Software\\TortoiseGit\\UseLibgit2", TRUE) != TRUE)
598 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)
599 CRegStdDWORD(L"Software\\TortoiseGit\\UseLibgit2").removeValue();
602 if (CRegStdDWORD(L"Software\\TortoiseGit\\UseLibgit2_mask").exists())
604 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)
605 CRegStdDWORD(L"Software\\TortoiseGit\\UseLibgit2_mask").removeValue();
608 CMessageBox::RemoveRegistryKey(L"OldMsysgitVersionWarning");
610 CRegDWORD checkNewerWeekDay = CRegDWORD(L"Software\\TortoiseGit\\CheckNewerWeekDay", 0);
611 if (!checkNewerWeekDay.exists() || lVersion <= ConvertVersionToInt(1, 8, 16))
613 std::random_device rd;
614 std::mt19937 mt(rd());
615 std::uniform_int_distribution<int> dist(0, 6);
616 checkNewerWeekDay = dist(mt);
619 // version specific updates
620 if (lVersion <= ConvertVersionToInt(2, 4, 1))
622 CRegStdDWORD(L"Software\\TortoiseGit\\CommitAskBeforeCancel").removeValue();
625 if (lVersion <= ConvertVersionToInt(2, 4, 0))
627 CRegStdDWORD commmitAskBeforeCancel(L"Software\\TortoiseGit\\CommitAskBeforeCancel");
628 if (commmitAskBeforeCancel.exists() && commmitAskBeforeCancel != IDYES)
629 commmitAskBeforeCancel = IDYES;
632 if (lVersion <= ConvertVersionToInt(2, 2, 1))
634 CString username = CRegString(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\Username", L"");
635 CString password = CRegString(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\Password", L"");
636 if (!username.IsEmpty() && !password.IsEmpty())
638 if (CWindowsCredentialsStore::SaveCredential(L"TortoiseGit:SMTP-Credentials", username, password) == 0)
640 CRegString(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\Username").removeValue();
641 CRegString(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\Password").removeValue();
644 for (const CString& setting : { L"SyncIn", L"SyncOut" })
646 CRegDWORD reg(L"Software\\TortoiseGit\\StatusColumns\\" + setting + L"loglistVersion", 0xff);
647 if ((DWORD)reg == 6)
648 reg.removeValue();
652 if (lVersion <= ConvertVersionToInt(2, 1, 5))
654 // We updated GITSLC_COL_VERSION, but only significant changes were made for GitStatusList
655 // so, smoothly migrate GitLoglistBase settings
656 for (const CString& setting : { L"log", L"Blame", L"Rebase", L"reflog", L"SyncIn", L"SyncOut" })
658 CRegDWORD reg(L"Software\\TortoiseGit\\StatusColumns\\" + setting + L"loglistVersion", 0xff);
659 if ((DWORD)reg == 5)
660 reg = 6;
664 if (lVersion <= ConvertVersionToInt(1, 9, 0))
666 if (CRegDWORD(L"Software\\TortoiseGit\\TGitCacheCheckContent", TRUE) == FALSE)
668 CRegDWORD(L"Software\\TortoiseGit\\TGitCacheCheckContentMaxSize") = 0;
669 CRegDWORD(L"Software\\TortoiseGit\\TGitCacheCheckContent").removeValue();
673 if (lVersion <= ConvertVersionToInt(1, 8, 8, 1))
675 CRegStdDWORD(L"Software\\TortoiseGit\\StatusColumns\\BrowseRefs").removeValue();
676 CRegStdString(L"Software\\TortoiseGit\\StatusColumns\\BrowseRefs_Order").removeValue();
677 CRegStdString(L"Software\\TortoiseGit\\StatusColumns\\BrowseRefs_Width").removeValue();
680 if (lVersion <= ConvertVersionToInt(1, 8, 4, 1))
682 if (CRegStdDWORD(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\UseMAPI", FALSE) == TRUE)
683 CRegStdDWORD(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\DeliveryType") = SEND_MAIL_MAPI;
684 CRegStdDWORD(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\UseMAPI").removeValue();
687 if (lVersion <= ConvertVersionToInt(1, 8, 2, 2))
689 // upgrade to 1.8.3: force recreation of all diff scripts.
690 CAppUtils::SetupDiffScripts(true, CString());
693 if (lVersion <= ConvertVersionToInt(1, 8, 1))
695 if (CRegStdDWORD(L"Software\\TortoiseGit\\LogTopoOrder", TRUE) == FALSE)
696 CRegStdDWORD(L"Software\\TortoiseGit\\LogOrderBy") = 0;
698 // smoothly migrate broken msysgit path settings
699 CString oldmsysGitSetting = CRegString(REG_MSYSGIT_PATH);
700 oldmsysGitSetting.TrimRight(L'\\');
701 if (oldmsysGitSetting.GetLength() > 4 && CStringUtils::EndsWith(oldmsysGitSetting, L"\\cmd"))
703 CString newPath = oldmsysGitSetting.Left(oldmsysGitSetting.GetLength() - 3) + L"bin";
704 if (PathFileExists(newPath + L"\\git.exe"))
706 CRegString(REG_MSYSGIT_PATH) = newPath;
707 g_Git.m_bInitialized = FALSE;
708 g_Git.CheckMsysGitDir();
713 if (lVersion <= ConvertVersionToInt(1, 4, 0))
715 CRegStdDWORD(L"Software\\TortoiseGit\\OwnerdrawnMenus").removeValue();
718 if (lVersion <= ConvertVersionToInt(1, 7, 6))
720 CoInitialize(nullptr);
721 EnsureGitLibrary();
722 CoUninitialize();
723 CRegStdDWORD(L"Software\\TortoiseGit\\ConvertBase").removeValue();
724 CRegStdDWORD(L"Software\\TortoiseGit\\DiffProps").removeValue();
725 if (CRegStdDWORD(L"Software\\TortoiseGit\\CheckNewer", TRUE) == FALSE)
726 CRegStdDWORD(L"Software\\TortoiseGit\\VersionCheck") = FALSE;
727 CRegStdDWORD(L"Software\\TortoiseGit\\CheckNewer").removeValue();
730 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Setting up diff scripts ...\n");
731 CAppUtils::SetupDiffScripts(false, CString());
733 // set the current version so we don't come here again until the next update!
734 regVersion = _T(STRPRODUCTVER);
737 void CTortoiseProcApp::InitializeJumpList(const CString& appid)
739 // for Win7 : use a custom jump list
740 CoInitialize(nullptr);
741 SetAppID(appid);
742 DeleteJumpList(appid);
743 DoInitializeJumpList(appid);
744 CoUninitialize();
747 void CTortoiseProcApp::DoInitializeJumpList(const CString& appid)
749 ATL::CComPtr<ICustomDestinationList> pcdl;
750 HRESULT hr = pcdl.CoCreateInstance(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER);
751 if (FAILED(hr))
752 return;
754 hr = pcdl->SetAppID(appid);
755 if (FAILED(hr))
756 return;
758 UINT uMaxSlots;
759 ATL::CComPtr<IObjectArray> poaRemoved;
760 hr = pcdl->BeginList(&uMaxSlots, IID_PPV_ARGS(&poaRemoved));
761 if (FAILED(hr))
762 return;
764 ATL::CComPtr<IObjectCollection> poc;
765 hr = poc.CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER);
766 if (FAILED(hr))
767 return;
769 CString sTemp = CString(MAKEINTRESOURCE(IDS_MENUSETTINGS));
770 CStringUtils::RemoveAccelerators(sTemp);
772 ATL::CComPtr<IShellLink> psl;
773 hr = CreateShellLink(L"/command:settings", (LPCTSTR)sTemp, 20, &psl);
774 if (SUCCEEDED(hr)) {
775 poc->AddObject(psl);
777 sTemp = CString(MAKEINTRESOURCE(IDS_MENUHELP));
778 CStringUtils::RemoveAccelerators(sTemp);
779 psl.Release(); // Need to release the object before calling operator&()
780 hr = CreateShellLink(L"/command:help", (LPCTSTR)sTemp, 19, &psl);
781 if (SUCCEEDED(hr)) {
782 poc->AddObject(psl);
785 ATL::CComPtr<IObjectArray> poa;
786 hr = poc.QueryInterface(&poa);
787 if (SUCCEEDED(hr)) {
788 pcdl->AppendCategory((LPCTSTR)CString(MAKEINTRESOURCE(IDS_PROC_TASKS)), poa);
789 pcdl->CommitList();
793 int CTortoiseProcApp::ExitInstance()
795 SYS_IMAGE_LIST().Cleanup();
796 Gdiplus::GdiplusShutdown(m_gdiplusToken);
798 CWinAppEx::ExitInstance();
799 if (retSuccess)
800 return 0;
801 return -1;
804 void CTortoiseProcApp::CheckForNewerVersion()
806 // check for newer versions
807 if (CRegDWORD(L"Software\\TortoiseGit\\VersionCheck", TRUE) != FALSE)
809 time_t now;
810 struct tm ptm;
812 time(&now);
813 if ((now != 0) && (localtime_s(&ptm, &now)==0))
815 #if PREVIEW
816 // Check daily for new preview releases
817 CRegDWORD oldday = CRegDWORD(L"Software\\TortoiseGit\\CheckNewerDay", (DWORD)-1);
818 if (((DWORD)oldday) == -1)
819 oldday = ptm.tm_yday;
820 else
822 if ((DWORD)oldday != (DWORD)ptm.tm_yday)
824 oldday = ptm.tm_yday;
825 #else
826 int week = 0;
827 // we don't calculate the real 'week of the year' here
828 // because just to decide if we should check for an update
829 // that's not needed.
830 week = (ptm.tm_yday + CRegDWORD(L"Software\\TortoiseGit\\CheckNewerWeekDay", 0)) / 7;
832 CRegDWORD oldweek = CRegDWORD(L"Software\\TortoiseGit\\CheckNewerWeek", (DWORD)-1);
833 if (((DWORD)oldweek) == -1)
834 oldweek = week; // first start of TortoiseProc, no update check needed
835 else
837 if ((DWORD)week != oldweek)
839 oldweek = week;
840 #endif
841 CAppUtils::RunTortoiseGitProc(L"/command:updatecheck", false, false);