Fix typos
[TortoiseGit.git] / src / TortoiseProc / TortoiseProc.cpp
blobac136295df31cfbb3db3afd74568f233470b6d6c
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2023 - 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 "GitAdminDir.h"
32 #include "Git.h"
33 #include "SmartHandle.h"
34 #include "Commands\Command.h"
35 #include "../version.h"
36 #include "I18NHelper.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 <random>
44 #include "SendMail.h"
45 #include "WindowsCredentialsStore.h"
46 #include "FirstStartWizard.h"
47 #include "AnimationManager.h"
48 #include "../TGitCache/CacheInterface.h"
49 #include "VersioncheckParser.h"
50 #include "TempFile.h"
52 #define STRUCT_IOVEC_DEFINED
54 #ifdef _DEBUG
55 #define new DEBUG_NEW
56 #endif
58 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
60 BEGIN_MESSAGE_MAP(CTortoiseProcApp, CWinAppEx)
61 ON_COMMAND(ID_HELP, CWinAppEx::OnHelp)
62 END_MESSAGE_MAP()
64 //////////////////////////////////////////////////////////////////////////
66 CTortoiseProcApp::CTortoiseProcApp()
68 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Constructor\n");
69 SetDllDirectory(L"");
70 CCrashReport::Instance().AddUserInfoToReport(L"CommandLine", GetCommandLine());
71 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": CommandLine: %s\n", GetCommandLine());
72 EnableHtmlHelp();
73 CHooks::Create();
74 git_libgit2_init();
75 CGit::SetGit2CredentialCallback(CAppUtils::Git2GetUserPassword);
76 CGit::SetGit2CertificateCheckCertificate(CAppUtils::Git2CertificateCheck);
77 m_bLoadUserToolbars = FALSE;
78 m_bSaveState = FALSE;
81 CTortoiseProcApp::~CTortoiseProcApp()
83 CHooks::Destroy();
84 git_libgit2_shutdown();
87 // The one and only CTortoiseProcApp object
88 CTortoiseProcApp theApp;
89 CString sOrigCWD;
90 CString g_sGroupingUUID;
91 CString g_sGroupingIcon;
92 bool g_bGroupingRemoveIcon = false;
93 HWND GetExplorerHWND()
95 return theApp.GetExplorerHWND();
98 #if ENABLE_CRASHHANLDER && !_M_ARM64
99 CCrashReportTGit crasher(L"TortoiseGit " _T(APP_X64_STRING), TGIT_VERMAJOR, TGIT_VERMINOR, TGIT_VERMICRO, TGIT_VERBUILD, TGIT_VERDATE);
100 #endif
102 // CTortoiseProcApp initialization
104 BOOL CTortoiseProcApp::InitInstance()
106 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": InitInstance\n");
107 CheckUpgrade();
108 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
109 CMFCButton::EnableWindowsTheming();
111 Gdiplus::GdiplusStartupInput gdiplusStartupInput;
112 Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, nullptr);
114 //set the resource dll for the required language
115 CRegDWORD loc = CRegDWORD(L"Software\\TortoiseGit\\LanguageID", 1033);
116 long langId = loc;
117 CString langDll;
118 CStringA langpath = CStringA(CPathUtils::GetAppParentDirectory());
119 langpath += "Languages";
122 langDll.Format(L"%sLanguages\\TortoiseProc%ld.dll", static_cast<LPCWSTR>(CPathUtils::GetAppParentDirectory()), langId);
124 if (CI18NHelper::DoVersionStringsMatch(CPathUtils::GetVersionFromFile(langDll), _T(STRPRODUCTVER)))
126 HINSTANCE hInst = LoadLibrary(langDll);
127 if (hInst)
129 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Load Language DLL %s\n", static_cast<LPCWSTR>(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 free((void*)m_pszHelpFilePath);
149 m_pszHelpFilePath = _wcsdup(CPathUtils::GetAppDirectory() + L"TortoiseGit_en.chm");
150 setlocale(LC_ALL, "");
152 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Initializing UI components ...\n");
153 // InitCommonControls() is required on Windows XP if an application
154 // manifest specifies use of ComCtl32.dll version 6 or later to enable
155 // visual styles. Otherwise, any window creation will fail.
157 INITCOMMONCONTROLSEX used = {
158 sizeof(INITCOMMONCONTROLSEX),
159 ICC_ANIMATE_CLASS | ICC_BAR_CLASSES | ICC_COOL_CLASSES | ICC_DATE_CLASSES |
160 ICC_HOTKEY_CLASS | ICC_INTERNET_CLASSES | ICC_LISTVIEW_CLASSES |
161 ICC_NATIVEFNTCTL_CLASS | ICC_PAGESCROLLER_CLASS | ICC_PROGRESS_CLASS |
162 ICC_TAB_CLASSES | ICC_TREEVIEW_CLASSES | ICC_UPDOWN_CLASS |
163 ICC_USEREX_CLASSES | ICC_WIN95_CLASSES
165 InitCommonControlsEx(&used);
166 AfxOleInit();
167 AfxEnableControlContainer();
168 AfxInitRichEdit5();
169 CWinAppEx::InitInstance();
170 SetRegistryKey(L"TortoiseGit");
171 SYS_IMAGE_LIST();
172 CHistoryCombo::m_nGitIconIndex = SYS_IMAGE_LIST().AddIcon(CCommonAppUtils::LoadIconEx(IDI_GITCONFIG, 0, 0));
173 AfxGetApp()->m_pszProfileName = _wcsdup(L"TortoiseProc"); // w/o this ResizableLib will store data under TortoiseGitProc which is not compatible with older versions
175 CCmdLineParser parser(AfxGetApp()->m_lpCmdLine);
177 hWndExplorer = nullptr;
178 CString sVal = parser.GetVal(L"hwnd");
179 if (!sVal.IsEmpty())
180 hWndExplorer = reinterpret_cast<HWND>(_wcstoui64(sVal, nullptr, 16));
182 while (GetParent(hWndExplorer))
183 hWndExplorer = GetParent(hWndExplorer);
184 if (!IsWindow(hWndExplorer))
185 hWndExplorer = nullptr;
187 // if HKCU\Software\TortoiseGit\Debug is not 0, show our command line
188 // in a message box
189 if (CRegDWORD(L"Software\\TortoiseGit\\Debug", FALSE) == TRUE)
190 AfxMessageBox(AfxGetApp()->m_lpCmdLine, MB_OK | MB_ICONINFORMATION);
192 if (parser.HasKey(L"command") && wcscmp(parser.GetVal(L"command"), L"firststart") == 0)
194 // CFirstStartWizard requires sOrigCWD to be set
195 DWORD len = GetCurrentDirectory(0, nullptr);
196 if (len)
198 auto originalCurrentDirectory = std::make_unique<wchar_t[]>(len);
199 if (GetCurrentDirectory(len, originalCurrentDirectory.get()))
201 sOrigCWD = originalCurrentDirectory.get();
202 sOrigCWD = CPathUtils::GetLongPathname(sOrigCWD);
205 CFirstStartWizard wizard(IDS_APPNAME, CWnd::FromHandle(hWndExplorer), parser.GetLongVal(L"page"));
206 theApp.m_pMainWnd = &wizard;
207 return (wizard.DoModal() == ID_WIZFINISH);
210 if (!g_Git.CheckMsysGitDir())
212 UINT ret = CMessageBox::Show(hWndExplorer, IDS_PROC_NOMSYSGIT, IDS_APPNAME, 3, IDI_HAND, IDS_PROC_SETMSYSGITPATH, IDS_PROC_GOTOMSYSGITWEBSITE, IDS_ABORTBUTTON);
213 if (ret == 2)
214 ShellExecute(hWndExplorer, L"open", GIT_FOR_WINDOWS_URL, nullptr, nullptr, SW_SHOW);
215 else if (ret == 1)
217 CFirstStartWizard wizard(IDS_APPNAME, CWnd::FromHandle(hWndExplorer), 2);
218 theApp.m_pMainWnd = &wizard;
219 wizard.DoModal();
221 return FALSE;
223 if (!CConfigureGitExe::CheckGitVersion(hWndExplorer))
224 return FALSE;
227 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Registering Crash Report ...\n");
228 CCrashReport::Instance().AddUserInfoToReport(L"msysGitDir", CGit::ms_LastMsysGitDir);
229 CString versionString;
230 versionString.Format(L"%X", CGit::ms_LastMsysGitVersion);
231 CCrashReport::Instance().AddUserInfoToReport(L"msysGitVersion", versionString);
232 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": msysGitVersion: %s\n", static_cast<LPCWSTR>(versionString));
233 if (CGit::ms_bCygwinGit)
234 CCrashReport::Instance().AddUserInfoToReport(L"CygwinHack", L"true");
235 if (CGit::ms_bMsys2Git)
236 CCrashReport::Instance().AddUserInfoToReport(L"Msys2Hack", L"true");
237 #if PREVIEW
238 CCrashReport::Instance().AddUserInfoToReport(L"Preview", _T(PREVIEW_INFO));
239 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Preview: %s\n", _T(PREVIEW_INFO));
240 #else
241 if (CString hotfix = CPathUtils::GetAppDirectory() + L"hotfix.ini"; PathFileExists(hotfix))
243 CString err;
244 CVersioncheckParser versionparser;
245 if (versionparser.Load(hotfix, err))
247 auto version = versionparser.GetTortoiseGitVersion();
248 if (version.major == TGIT_VERMAJOR && version.minor == TGIT_VERMINOR && version.micro == TGIT_VERMICRO && version.build > TGIT_VERBUILD)
250 CCrashReport::Instance().AddUserInfoToReport(L"Hotfix", versionparser.GetTortoiseGitVersion().version);
251 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Hotfix: %s\n", static_cast<LPCWSTR>(versionparser.GetTortoiseGitVersion().version));
255 #endif
258 if (parser.HasKey(L"urlhandler"))
260 CString url = parser.GetVal(L"urlhandler");
261 if (CStringUtils::StartsWith(url, L"tgit://clone/"))
262 url = url.Mid(static_cast<int>(wcslen(L"tgit://clone/")));
263 else if (CStringUtils::StartsWith(url, L"github-windows://openRepo/"))
265 url = url.Mid(static_cast<int>(wcslen(L"github-windows://openRepo/")));
266 int questioMark = url.Find('?');
267 if (questioMark > 0)
268 url = url.Left(questioMark);
270 else if (CStringUtils::StartsWith(url, L"x-github-client://openRepo/")) {
271 url = url.Mid(static_cast<int>(wcslen(L"x-github-client://openRepo/")));
272 int questioMark = url.Find('?');
273 if (questioMark > 0)
274 url = url.Left(questioMark);
276 else if (CStringUtils::StartsWith(url, L"smartgit://cloneRepo/"))
277 url = url.Mid(static_cast<int>(wcslen(L"smartgit://cloneRepo/")));
278 else if (!CStringUtils::StartsWith(url, L"git://"))
280 CMessageBox::Show(nullptr, IDS_ERR_INVALIDPATH, IDS_APPNAME, MB_ICONERROR);
281 return FALSE;
283 CString newCmd;
284 newCmd.Format(L"/command:clone /url:\"%s\" /hasurlhandler", static_cast<LPCWSTR>(url));
285 parser = CCmdLineParser(newCmd);
288 if (parser.HasKey(L"path") && parser.HasKey(L"pathfile"))
290 CMessageBox::Show(nullptr, IDS_ERR_INVALIDPATH, IDS_APPNAME, MB_ICONERROR);
291 return FALSE;
294 CTGitPath cmdLinePath;
295 CTGitPathList pathList;
296 if (g_sGroupingUUID.IsEmpty())
297 g_sGroupingUUID = parser.GetVal(L"groupuuid");
298 if (parser.HasKey(L"pathfile"))
300 CString sPathfileArgument = CPathUtils::GetLongPathname(parser.GetVal(L"pathfile"));
302 cmdLinePath.SetFromUnknown(sPathfileArgument);
303 if (pathList.LoadFromFile(cmdLinePath)==false)
304 return FALSE; // no path specified!
305 if (parser.HasKey(L"deletepathfile"))
307 // We can delete the temporary path file, now that we've loaded it
308 ::DeleteFile(cmdLinePath.GetWinPath());
310 // This was a path to a temporary file - it's got no meaning now, and
311 // anybody who uses it again is in for a problem...
312 cmdLinePath.Reset();
315 else
317 CString sPathArgument = parser.GetVal(L"path");
318 if (parser.HasKey(L"expaths"))
320 // an /expaths param means we're started via the buttons in our Win7 library
321 // and that means the value of /expaths is the current directory, and
322 // the selected paths are then added as additional parameters but without a key, only a value
324 // because of the "strange treatment of quotation marks and backslashes by CommandLineToArgvW"
325 // we have to escape the backslashes first. Since we're only dealing with paths here, that's
326 // a save bet.
327 // Without this, a command line like:
328 // /command:commit /expaths:"D:\" "D:\Utils"
329 // would fail because the "D:\" is treated as the backslash being the escape char for the quotation
330 // mark and we'd end up with:
331 // argv[1] = /command:commit
332 // argv[2] = /expaths:D:" D:\Utils
333 // See here for more details: http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
334 CString cmdLine = GetCommandLineW();
335 cmdLine.Replace(L"\\", L"\\\\");
336 int nArgs = 0;
337 LPWSTR *szArglist = CommandLineToArgvW(cmdLine, &nArgs);
338 if (szArglist)
340 // argument 0 is the process path, so start with 1
341 for (int i = 1; i < nArgs; ++i)
343 if (szArglist[i][0] != '/')
345 if (!sPathArgument.IsEmpty())
346 sPathArgument += '*';
347 sPathArgument += szArglist[i];
350 sPathArgument.Replace(L"\\\\", L"\\");
352 LocalFree(szArglist);
354 if (sPathArgument.IsEmpty() && parser.HasKey(L"path"))
356 CMessageBox::Show(hWndExplorer, IDS_ERR_INVALIDPATH, IDS_APPNAME, MB_ICONERROR);
357 return FALSE;
359 pathList.LoadFromAsteriskSeparatedString(sPathArgument);
360 if (!pathList.IsEmpty())
361 cmdLinePath = pathList[0];
364 if (pathList.IsEmpty()) {
365 pathList.AddPath(CTGitPath::CTGitPath(g_Git.m_CurrentDir));
368 // Set CWD to temporary dir, and restore it later
370 DWORD len = GetCurrentDirectory(0, nullptr);
371 if (len)
373 auto originalCurrentDirectory = std::make_unique<wchar_t[]>(len);
374 if (GetCurrentDirectory(len, originalCurrentDirectory.get()))
376 sOrigCWD = originalCurrentDirectory.get();
377 sOrigCWD = CPathUtils::GetLongPathname(sOrigCWD);
380 wchar_t pathbuf[MAX_PATH] = { 0 };
381 GetTortoiseGitTempPath(_countof(pathbuf), pathbuf);
382 SetCurrentDirectory(pathbuf);
385 CheckForNewerVersion();
387 CAutoGeneralHandle TGitMutex = ::CreateMutex(nullptr, FALSE, L"TortoiseGitProc.exe");
388 if (!g_Git.SetCurrentDir(cmdLinePath.GetWinPathString(), parser.HasKey(L"submodule") == TRUE))
390 for (int i = 0; i < pathList.GetCount(); ++i)
391 if(g_Git.SetCurrentDir(pathList[i].GetWinPath()))
392 break;
394 if (parser.HasKey(L"pathfile") && parser.HasKey(L"submodule"))
395 g_Git.SetCurrentDir(pathList[0].GetWinPathString(), true);
397 if(!g_Git.m_CurrentDir.IsEmpty())
399 sOrigCWD = g_Git.m_CurrentDir;
400 SetCurrentDirectory(g_Git.m_CurrentDir);
403 if (g_sGroupingUUID.IsEmpty())
405 CRegStdDWORD groupSetting = CRegStdDWORD(L"Software\\TortoiseGit\\GroupTaskbarIconsPerRepo", 3);
406 switch (DWORD(groupSetting))
408 case 1:
409 case 2:
410 // implemented differently to TortoiseSVN atm
411 break;
412 case 3:
413 case 4:
415 CString wcroot;
416 if (GitAdminDir::HasAdminDir(g_Git.m_CurrentDir, true, &wcroot))
418 git_oid oid;
419 CStringA wcRootA(wcroot + CPathUtils::GetAppDirectory());
420 if (!git_odb_hash(&oid, wcRootA.MakeLower(), wcRootA.GetLength(), GIT_OBJECT_BLOB))
422 CStringA hash;
423 git_oid_tostr(CStrBufA(hash, GIT_OID_SHA1_HEXSIZE, CStrBufA::SET_LENGTH), GIT_OID_SHA1_HEXSIZE + 1, &oid);
424 g_sGroupingUUID = hash;
426 ProjectProperties pp;
427 pp.ReadProps();
428 CString icon = pp.sIcon;
429 icon.Replace('/', '\\');
430 if (icon.IsEmpty())
431 g_bGroupingRemoveIcon = true;
432 g_sGroupingIcon = icon;
438 CString sAppID = GetTaskIDPerUUID(g_sGroupingUUID).c_str();
439 InitializeJumpList(sAppID);
440 EnsureGitLibrary(false);
443 CString err;
446 // requires CWD to be set
447 CGit::m_LogEncode = CAppUtils::GetLogOutputEncode();
449 // make sure all config files are read in order to check that none contains an error
450 g_Git.GetConfigValue(L"doesnot.exist");
452 catch (const char* msg)
454 err = CUnicodeUtils::GetUnicode(msg);
457 if (!err.IsEmpty())
459 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)));
460 if (choice == 1)
462 // open the config file with alternative editor
463 CAppUtils::LaunchAlternativeEditor(g_Git.GetGitLocalConfig());
465 else if (choice == 2)
467 // open the global config file with alternative editor
468 CAppUtils::LaunchAlternativeEditor(g_Git.GetGitGlobalConfig());
470 return FALSE;
474 // execute the requested command
475 CommandServer server;
476 Command* cmd = server.GetCommand(parser.GetVal(L"command"));
477 if (cmd)
479 cmd->SetExplorerHwnd(hWndExplorer);
481 cmd->SetParser(parser);
482 cmd->SetPaths(pathList, cmdLinePath);
484 retSuccess = cmd->Execute();
485 delete cmd;
488 // Look for temporary files left around by TortoiseGit and
489 // remove them. But only delete 'old' files because some
490 // apps might still be needing the recent ones.
491 CTempFiles::Instance().DeleteOldTempFiles();
493 Animator::Instance().ShutDown();
495 // Since the dialog has been closed, return FALSE so that we exit the
496 // application, rather than start the application's message pump.
497 return FALSE;
500 void CTortoiseProcApp::CheckUpgrade()
502 CRegString regVersion = CRegString(L"Software\\TortoiseGit\\CurrentVersion");
503 CString sVersion = regVersion;
504 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Current TGit Version %s vs. last used version %s\n", _T(STRPRODUCTVER), static_cast<LPCWSTR>(sVersion));
505 if (sVersion.Compare(_T(STRPRODUCTVER))==0)
506 return;
507 // we're starting the first time with a new version!
509 LONG lVersion = 0;
510 int pos = sVersion.Find('.');
511 if (pos > 0)
513 lVersion = (_wtol(sVersion.Left(pos)) << 24);
514 lVersion |= (_wtol(sVersion.Mid(pos + 1)) << 16);
515 pos = sVersion.Find('.', pos+1);
516 lVersion |= (_wtol(sVersion.Mid(pos + 1)) << 8);
518 else
520 pos = sVersion.Find(',');
521 if (pos > 0)
523 lVersion = (_wtol(sVersion.Left(pos)) << 24);
524 lVersion |= (_wtol(sVersion.Mid(pos + 1)) << 16);
525 pos = sVersion.Find(',', pos+1);
526 lVersion |= (_wtol(sVersion.Mid(pos + 1)) << 8);
530 // generic cleanup
531 if (CRegStdDWORD(L"Software\\TortoiseGit\\UseLibgit2", TRUE) != TRUE)
533 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)
534 CRegStdDWORD(L"Software\\TortoiseGit\\UseLibgit2").removeValue();
537 if (CRegStdDWORD(L"Software\\TortoiseGit\\UseLibgit2_mask").exists())
539 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)
540 CRegStdDWORD(L"Software\\TortoiseGit\\UseLibgit2_mask").removeValue();
543 CMessageBox::RemoveRegistryKey(L"OldMsysgitVersionWarning");
545 CRegDWORD checkNewerWeekDay = CRegDWORD(L"Software\\TortoiseGit\\CheckNewerWeekDay", 0);
546 if (!checkNewerWeekDay.exists() || lVersion <= ConvertVersionToInt(1, 8, 16))
548 std::random_device rd;
549 std::mt19937 mt(rd());
550 std::uniform_int_distribution<int> dist(0, 6);
551 checkNewerWeekDay = dist(mt);
554 // version specific updates
555 if (lVersion <= ConvertVersionToInt(2, 9, 2))
557 if (auto inlineAdded = CRegDWORD(L"Software\\TortoiseGitMerge\\InlineAdded"); inlineAdded.exists())
559 CRegDWORD(L"Software\\TortoiseGitMerge\\Colors\\InlineAdded") = static_cast<DWORD>(inlineAdded);
560 inlineAdded.removeValue();
562 if (auto inlineRemoved = CRegDWORD(L"Software\\TortoiseGitMerge\\InlineRemoved"); inlineRemoved.exists())
564 CRegDWORD(L"Software\\TortoiseGitMerge\\Colors\\InlineRemoved") = static_cast<DWORD>(inlineRemoved);
565 inlineRemoved.removeValue();
569 if (lVersion <= ConvertVersionToInt(2, 9, 1))
570 CRegStdDWORD(L"Software\\TortoiseGit\\TortoiseProc\\PatchDlgWidth").removeValue();
572 if (lVersion <= ConvertVersionToInt(2, 4, 1))
574 CRegStdDWORD(L"Software\\TortoiseGit\\CommitAskBeforeCancel").removeValue();
577 if (lVersion <= ConvertVersionToInt(2, 4, 0))
579 CRegStdDWORD commmitAskBeforeCancel(L"Software\\TortoiseGit\\CommitAskBeforeCancel");
580 if (commmitAskBeforeCancel.exists() && commmitAskBeforeCancel != IDYES)
581 commmitAskBeforeCancel = IDYES;
584 if (lVersion <= ConvertVersionToInt(2, 2, 1))
586 CString username = CRegString(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\Username", L"");
587 CString password = CRegString(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\Password", L"");
588 if (!username.IsEmpty() && !password.IsEmpty())
590 if (CWindowsCredentialsStore::SaveCredential(L"TortoiseGit:SMTP-Credentials", username, password) == 0)
592 CRegString(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\Username").removeValue();
593 CRegString(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\Password").removeValue();
596 for (const CString& setting : { L"SyncIn", L"SyncOut" })
598 CRegDWORD reg(L"Software\\TortoiseGit\\StatusColumns\\" + setting + L"loglistVersion", 0xff);
599 if (reg == 6)
600 reg.removeValue();
604 if (lVersion <= ConvertVersionToInt(2, 1, 5))
606 // We updated GITSLC_COL_VERSION, but only significant changes were made for GitStatusList
607 // so, smoothly migrate GitLoglistBase settings
608 for (const CString& setting : { L"log", L"Blame", L"Rebase", L"reflog", L"SyncIn", L"SyncOut" })
610 CRegDWORD reg(L"Software\\TortoiseGit\\StatusColumns\\" + setting + L"loglistVersion", 0xff);
611 if (reg == 5)
612 reg = 6;
616 if (lVersion <= ConvertVersionToInt(1, 9, 0))
618 if (CRegDWORD(L"Software\\TortoiseGit\\TGitCacheCheckContent", TRUE) == FALSE)
620 CRegDWORD(L"Software\\TortoiseGit\\TGitCacheCheckContentMaxSize") = 0;
621 CRegDWORD(L"Software\\TortoiseGit\\TGitCacheCheckContent").removeValue();
625 if (lVersion <= ConvertVersionToInt(1, 8, 8, 1))
627 CRegStdDWORD(L"Software\\TortoiseGit\\StatusColumns\\BrowseRefs").removeValue();
628 CRegStdString(L"Software\\TortoiseGit\\StatusColumns\\BrowseRefs_Order").removeValue();
629 CRegStdString(L"Software\\TortoiseGit\\StatusColumns\\BrowseRefs_Width").removeValue();
632 if (lVersion <= ConvertVersionToInt(1, 8, 4, 1))
634 if (CRegStdDWORD(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\UseMAPI", FALSE) == TRUE)
635 CRegStdDWORD(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\DeliveryType") = SEND_MAIL_MAPI;
636 CRegStdDWORD(L"Software\\TortoiseGit\\TortoiseProc\\SendMail\\UseMAPI").removeValue();
639 if (lVersion <= ConvertVersionToInt(1, 8, 2, 2))
641 // upgrade to 1.8.3: force recreation of all diff scripts.
642 CAppUtils::SetupDiffScripts(true, CString());
645 if (lVersion <= ConvertVersionToInt(1, 8, 1))
647 if (CRegStdDWORD(L"Software\\TortoiseGit\\LogTopoOrder", TRUE) == FALSE)
648 CRegStdDWORD(L"Software\\TortoiseGit\\LogOrderBy") = 0;
650 // smoothly migrate broken msysgit path settings
651 CString oldmsysGitSetting = CRegString(REG_MSYSGIT_PATH);
652 oldmsysGitSetting.TrimRight(L'\\');
653 if (oldmsysGitSetting.GetLength() > 4 && CStringUtils::EndsWith(oldmsysGitSetting, L"\\cmd"))
655 CString newPath = oldmsysGitSetting.Left(oldmsysGitSetting.GetLength() - 3) + L"bin";
656 if (PathFileExists(newPath + L"\\git.exe"))
658 CRegString(REG_MSYSGIT_PATH) = newPath;
659 g_Git.m_bInitialized = FALSE;
660 g_Git.CheckMsysGitDir();
665 if (lVersion <= ConvertVersionToInt(1, 4, 0))
667 CRegStdDWORD(L"Software\\TortoiseGit\\OwnerdrawnMenus").removeValue();
670 if (lVersion <= ConvertVersionToInt(1, 7, 6))
672 CoInitialize(nullptr);
673 EnsureGitLibrary();
674 CoUninitialize();
675 CRegStdDWORD(L"Software\\TortoiseGit\\ConvertBase").removeValue();
676 CRegStdDWORD(L"Software\\TortoiseGit\\DiffProps").removeValue();
677 if (CRegStdDWORD(L"Software\\TortoiseGit\\CheckNewer", TRUE) == FALSE)
678 CRegStdDWORD(L"Software\\TortoiseGit\\VersionCheck") = FALSE;
679 CRegStdDWORD(L"Software\\TortoiseGit\\CheckNewer").removeValue();
682 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Setting up diff scripts ...\n");
683 CAppUtils::SetupDiffScripts(false, CString());
685 // set the current version so we don't come here again until the next update!
686 regVersion = _T(STRPRODUCTVER);
689 void CTortoiseProcApp::InitializeJumpList(const CString& appid)
691 // for Win7 : use a custom jump list
692 CoInitialize(nullptr);
693 SetAppID(appid);
694 DeleteJumpList(appid);
695 DoInitializeJumpList(appid);
696 CoUninitialize();
699 void CTortoiseProcApp::DoInitializeJumpList(const CString& appid)
701 ATL::CComPtr<ICustomDestinationList> pcdl;
702 HRESULT hr = pcdl.CoCreateInstance(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER);
703 if (FAILED(hr))
704 return;
706 hr = pcdl->SetAppID(appid);
707 if (FAILED(hr))
708 return;
710 UINT uMaxSlots;
711 ATL::CComPtr<IObjectArray> poaRemoved;
712 hr = pcdl->BeginList(&uMaxSlots, IID_PPV_ARGS(&poaRemoved));
713 if (FAILED(hr))
714 return;
716 ATL::CComPtr<IObjectCollection> poc;
717 hr = poc.CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER);
718 if (FAILED(hr))
719 return;
721 CString sTemp = CString(MAKEINTRESOURCE(IDS_MENUSETTINGS));
722 CStringUtils::RemoveAccelerators(sTemp);
724 ATL::CComPtr<IShellLink> psl;
725 hr = CreateShellLink(L"/command:settings", static_cast<LPCWSTR>(sTemp), 18, &psl);
726 if (SUCCEEDED(hr)) {
727 poc->AddObject(psl);
729 sTemp = CString(MAKEINTRESOURCE(IDS_MENUHELP));
730 CStringUtils::RemoveAccelerators(sTemp);
731 psl.Release(); // Need to release the object before calling operator&()
732 hr = CreateShellLink(L"/command:help", static_cast<LPCWSTR>(sTemp), 17, &psl);
733 if (SUCCEEDED(hr)) {
734 poc->AddObject(psl);
737 ATL::CComPtr<IObjectArray> poa;
738 hr = poc.QueryInterface(&poa);
739 if (SUCCEEDED(hr)) {
740 pcdl->AppendCategory(static_cast<LPCWSTR>(CString(MAKEINTRESOURCE(IDS_PROC_TASKS))), poa);
741 pcdl->CommitList();
745 int CTortoiseProcApp::ExitInstance()
747 SYS_IMAGE_LIST().Cleanup();
748 Gdiplus::GdiplusShutdown(m_gdiplusToken);
750 CWinAppEx::ExitInstance();
751 if (retSuccess)
752 return 0;
753 return -1;
756 void CTortoiseProcApp::CheckForNewerVersion()
758 // check for newer versions
759 if (CRegDWORD(L"Software\\TortoiseGit\\VersionCheck", TRUE) != FALSE)
761 time_t now;
762 struct tm ptm;
764 time(&now);
765 if ((now != 0) && (localtime_s(&ptm, &now)==0))
767 #if PREVIEW
768 // Check daily for new preview releases
769 CRegDWORD oldday = CRegDWORD(L"Software\\TortoiseGit\\CheckNewerDay", DWORD(-1));
770 if (oldday == -1)
771 oldday = ptm.tm_yday;
772 else
774 if (static_cast<DWORD>(oldday) != static_cast<DWORD>(ptm.tm_yday))
776 oldday = ptm.tm_yday;
777 #else
778 int week = 0;
779 // we don't calculate the real 'week of the year' here
780 // because just to decide if we should check for an update
781 // that's not needed.
782 week = (ptm.tm_yday + CRegDWORD(L"Software\\TortoiseGit\\CheckNewerWeekDay", 0)) / 7;
784 CRegDWORD oldweek = CRegDWORD(L"Software\\TortoiseGit\\CheckNewerWeek", DWORD(-1));
785 if (oldweek == -1)
786 oldweek = week; // first start of TortoiseProc, no update check needed
787 else
789 if (static_cast<DWORD>(week) != oldweek)
791 oldweek = week;
792 #endif
793 CAppUtils::RunTortoiseGitProc(L"/command:updatecheck", false, false);