Fix inconsistent function definition
[TortoiseGit.git] / src / Utils / CommonAppUtils.cpp
blobd8ba780ad53cca7ab363b8ccd136c1d5e0624768
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2020, 2023 - TortoiseGit
4 // Copyright (C) 2003-2008, 2010, 2020 - 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 "../Resources/LoglistCommonResource.h"
22 #include "CommonAppUtils.h"
23 #include "PathUtils.h"
24 #include "StringUtils.h"
25 #include "FormatMessageWrapper.h"
26 #include "registry.h"
27 #include "SelectFileFilter.h"
28 #include "DPIAware.h"
29 #include "LoadIconEx.h"
30 #include "IconBitmapUtils.h"
32 extern CString sOrigCWD;
33 extern CString g_sGroupingUUID;
35 bool CCommonAppUtils::LaunchApplication(const CString& sCommandLine, const LaunchApplicationFlags& flags)
37 CString theCWD = sOrigCWD;
38 if (flags.psCWD)
39 theCWD = *flags.psCWD;
41 if (flags.bUAC)
43 CString file, param;
44 SHELLEXECUTEINFO shellinfo = { 0 };
45 shellinfo.cbSize = sizeof(shellinfo);
46 shellinfo.lpVerb = L"runas";
47 shellinfo.nShow = SW_SHOWNORMAL;
48 shellinfo.fMask = SEE_MASK_NOCLOSEPROCESS;
49 shellinfo.lpDirectory = theCWD;
51 int pos = sCommandLine.Find('"');
52 if (pos == 0)
54 pos = sCommandLine.Find('"', 2);
55 if (pos > 1)
57 file = sCommandLine.Mid(1, pos - 1);
58 param = sCommandLine.Mid(pos + 1);
60 else
62 if (flags.uiIDErrMessageFormat != 0)
64 CString temp;
65 temp.Format(flags.uiIDErrMessageFormat, static_cast<LPCWSTR>(CFormatMessageWrapper()));
66 MessageBox(nullptr, temp, L"TortoiseGit", MB_OK | MB_ICONINFORMATION);
68 return false;
71 else
73 pos = sCommandLine.Find(' ', 1);
74 if (pos > 0)
76 file = sCommandLine.Left(pos);
77 param = sCommandLine.Mid(pos + 1);
79 else
80 file = sCommandLine;
83 shellinfo.lpFile = file;
84 shellinfo.lpParameters = param;
86 if (!ShellExecuteEx(&shellinfo))
88 if (flags.uiIDErrMessageFormat != 0)
90 CString temp;
91 temp.Format(flags.uiIDErrMessageFormat, static_cast<LPCWSTR>(CFormatMessageWrapper()));
92 MessageBox(nullptr, temp, L"TortoiseGit", MB_OK | MB_ICONINFORMATION);
94 return false;
97 CAutoGeneralHandle piProcess(std::move(shellinfo.hProcess));
98 if (piProcess)
100 if (flags.bWaitForStartup)
101 WaitForInputIdle(piProcess, 10000);
103 if (flags.bWaitForExit)
105 DWORD count = 1;
106 HANDLE handles[2];
107 handles[0] = piProcess;
108 if (flags.hWaitHandle)
110 count = 2;
111 handles[1] = flags.hWaitHandle;
113 WaitForMultipleObjects(count, handles, FALSE, INFINITE);
114 if (flags.hWaitHandle)
115 CloseHandle(flags.hWaitHandle);
117 if (flags.pdwExitCode)
119 if (!GetExitCodeProcess(piProcess, flags.pdwExitCode))
120 return false;
125 return true;
128 STARTUPINFO startup = { 0 };
129 PROCESS_INFORMATION process = { 0 };
130 startup.cb = sizeof(startup);
132 CString cleanCommandLine(sCommandLine);
133 if (CreateProcess(nullptr, cleanCommandLine.GetBuffer(), nullptr, nullptr, FALSE, CREATE_UNICODE_ENVIRONMENT, nullptr, theCWD, &startup, &process) == 0)
135 if (flags.uiIDErrMessageFormat)
137 CString temp;
138 temp.Format(flags.uiIDErrMessageFormat, static_cast<LPCWSTR>(CFormatMessageWrapper()));
139 MessageBox(nullptr, temp, L"TortoiseGit", MB_OK | MB_ICONINFORMATION);
141 return false;
144 CAutoGeneralHandle piThread(std::move(process.hThread));
145 CAutoGeneralHandle piProcess(std::move(process.hProcess));
147 AllowSetForegroundWindow(process.dwProcessId);
149 if (flags.bWaitForStartup)
150 WaitForInputIdle(piProcess, 10000);
152 if (flags.bWaitForExit)
154 DWORD count = 1;
155 HANDLE handles[2];
156 handles[0] = piProcess;
157 if (flags.hWaitHandle)
159 count = 2;
160 handles[1] = flags.hWaitHandle;
162 WaitForMultipleObjects(count, handles, FALSE, INFINITE);
163 if (flags.hWaitHandle)
164 CloseHandle(flags.hWaitHandle);
166 if (flags.pdwExitCode)
168 if (!GetExitCodeProcess(piProcess, flags.pdwExitCode))
169 return false;
173 return true;
176 bool CCommonAppUtils::RunTortoiseGitProc(const CString& sCommandLine, bool uac, bool includeGroupingUUID)
178 CString pathToExecutable = CPathUtils::GetAppDirectory() + L"TortoiseGitProc.exe";
179 CString sCmd;
180 sCmd.Format(L"\"%s\" %s", static_cast<LPCWSTR>(pathToExecutable), static_cast<LPCWSTR>(sCommandLine));
181 if (AfxGetMainWnd()->GetSafeHwnd() && (sCommandLine.Find(L"/hwnd:") < 0))
182 sCmd.AppendFormat(L" /hwnd:%p", static_cast<void*>(AfxGetMainWnd()->GetSafeHwnd()));
183 if (!g_sGroupingUUID.IsEmpty() && includeGroupingUUID)
185 sCmd += L" /groupuuid:\"";
186 sCmd += g_sGroupingUUID;
187 sCmd += L'"';
190 return LaunchApplication(sCmd, LaunchApplicationFlags().UAC(uac));
193 bool CCommonAppUtils::IsAdminLogin()
195 SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
196 PSID administratorsGroup;
197 // Initialize SID.
198 if (!AllocateAndInitializeSid(&ntAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &administratorsGroup))
199 return false;
200 SCOPE_EXIT { FreeSid(administratorsGroup); };
202 // Check whether the token is present in admin group.
203 BOOL isInAdminGroup = FALSE;
204 if (!CheckTokenMembership(nullptr, administratorsGroup, &isInAdminGroup))
205 return false;
207 return !!isInAdminGroup;
210 bool CCommonAppUtils::SetListCtrlBackgroundImage(HWND hListCtrl, UINT nID)
212 return SetListCtrlBackgroundImage(hListCtrl, nID, CDPIAware::Instance().ScaleX(hListCtrl, 128), CDPIAware::Instance().ScaleY(hListCtrl, 128));
215 bool CCommonAppUtils::SetListCtrlBackgroundImage(HWND hListCtrl, UINT nID, int width, int height)
217 if (static_cast<DWORD>(CRegStdDWORD(L"Software\\TortoiseGit\\ShowListBackgroundImage", TRUE)) == FALSE)
218 return false;
219 // create a bitmap from the icon
220 CAutoIcon hIcon = ::LoadIconEx(AfxGetResourceHandle(), MAKEINTRESOURCE(nID), width, height);
221 if (!hIcon)
222 return false;
224 IconBitmapUtils iutils;
225 auto bmp = iutils.IconToBitmapPARGB32(hIcon, width, height);
227 LVBKIMAGE lv;
228 lv.ulFlags = LVBKIF_TYPE_WATERMARK | LVBKIF_FLAG_ALPHABLEND;
229 lv.hbm = bmp;
230 lv.xOffsetPercent = 100;
231 lv.yOffsetPercent = 100;
232 ListView_SetBkImage(hListCtrl, &lv);
233 return true;
236 bool CCommonAppUtils::FileOpenSave(CString& path, int* filterindex, UINT title, UINT filterId, bool bOpen, HWND hwndOwner, LPCWSTR defaultExt, bool handleAsFile)
238 // Create a new common save file dialog
239 CComPtr<IFileDialog> pfd;
241 if (!SUCCEEDED(pfd.CoCreateInstance(bOpen ? CLSID_FileOpenDialog : CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER)))
242 return false;
244 // Set the dialog options
245 DWORD dwOptions;
246 if (!SUCCEEDED(pfd->GetOptions(&dwOptions)))
247 return false;
249 if (bOpen)
251 if (!SUCCEEDED(pfd->SetOptions(dwOptions | FOS_FILEMUSTEXIST | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST)))
252 return false;
254 else
256 if (!SUCCEEDED(pfd->SetOptions(dwOptions | FOS_OVERWRITEPROMPT | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST)))
257 return false;
259 if ((!PathIsDirectory(path) || handleAsFile) && !SUCCEEDED(pfd->SetFileName(CPathUtils::GetFileNameFromPath(path))))
260 return false;
262 // Set a title
263 if (title)
265 CString temp;
266 temp.LoadString(title);
267 CStringUtils::RemoveAccelerators(temp);
268 pfd->SetTitle(temp);
270 CComPtr<IShellItem> psiDefaultFolder;
271 if (filterId)
273 CSelectFileFilter fileFilter(filterId);
274 if (!SUCCEEDED(pfd->SetFileTypes(fileFilter.GetCount(), fileFilter)))
275 return false;
276 if (filterId == 1602 || filterId == 2501) // IDS_GITEXEFILEFILTER || IDS_PROGRAMSFILEFILTER
278 pfd->SetClientGuid({ 0x323ca4b0, 0x62df, 0x4a08, { 0xa5, 0x5, 0x58, 0xde, 0xa2, 0xb9, 0x2d, 0xcd } });
279 if (SUCCEEDED(SHCreateItemFromParsingName(CPathUtils::GetProgramsDirectory(), nullptr, IID_PPV_ARGS(&psiDefaultFolder))))
280 pfd->SetDefaultFolder(psiDefaultFolder);
282 else if (filterId == 1120) // IDS_PUTTYKEYFILEFILTER
284 pfd->SetClientGuid({ 0x271dbd3b, 0x50da, 0x4148, { 0x95, 0xfd, 0x64, 0x73, 0x69, 0xd1, 0x74, 0x2 } });
285 if (SUCCEEDED(SHCreateItemFromParsingName(CPathUtils::GetDocumentsDirectory(), nullptr, IID_PPV_ARGS(&psiDefaultFolder))))
286 pfd->SetDefaultFolder(psiDefaultFolder);
290 if (defaultExt && !SUCCEEDED(pfd->SetDefaultExtension(defaultExt)))
291 return false;
293 // set the default folder
294 CComPtr<IShellItem> psiFolder;
295 if (CStringUtils::StartsWith(path, L"\\") || path.Mid(1, 2) == L":\\")
297 CString dir = path;
298 if (!PathIsDirectory(dir) || handleAsFile)
300 if (PathRemoveFileSpec(dir.GetBuffer()))
301 dir.ReleaseBuffer();
302 else
303 dir.Empty();
305 if (!dir.IsEmpty() && SUCCEEDED(SHCreateItemFromParsingName(dir, nullptr, IID_PPV_ARGS(&psiFolder))))
306 pfd->SetFolder(psiFolder);
309 // Show the save/open file dialog
310 if (!SUCCEEDED(pfd->Show(hwndOwner)))
311 return false;
313 // Get the selection from the user
314 CComPtr<IShellItem> psiResult;
315 if (!SUCCEEDED(pfd->GetResult(&psiResult)))
316 return false;
318 CComHeapPtr<WCHAR> pszPath;
319 if (!SUCCEEDED(psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszPath)))
320 return false;
322 path = CString(pszPath);
323 if (filterindex)
325 UINT fi = 0;
326 pfd->GetFileTypeIndex(&fi);
327 *filterindex = fi;
329 return true;
332 HICON CCommonAppUtils::LoadIconEx(UINT resourceId, UINT cx, UINT cy)
334 return ::LoadIconEx(AfxGetResourceHandle(), MAKEINTRESOURCE(resourceId), cx, cy);
337 void CCommonAppUtils::SetCharFormat(CWnd* window, DWORD mask , DWORD effects, const std::vector<CHARRANGE>& positions)
339 CHARFORMAT2 format = {};
340 format.cbSize = sizeof(CHARFORMAT2);
341 format.dwMask = mask;
342 format.dwEffects = effects;
343 format.crTextColor = effects;
345 for (const auto& range : positions)
347 window->SendMessage(EM_EXSETSEL, 0, reinterpret_cast<LPARAM>(&range));
348 window->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast<LPARAM>(&format));
352 void CCommonAppUtils::SetCharFormat(CWnd* window, DWORD mask, DWORD effects )
354 CHARFORMAT2 format = {};
355 format.cbSize = sizeof(CHARFORMAT2);
356 format.dwMask = mask;
357 format.dwEffects = effects;
358 window->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast<LPARAM>(&format));
361 CString CCommonAppUtils::GetLogFontName()
363 return CRegString(L"Software\\TortoiseGit\\LogFontName", L"Consolas");
366 DWORD CCommonAppUtils::GetLogFontSize()
368 return CRegDWORD(L"Software\\TortoiseGit\\LogFontSize", 9);
371 void CCommonAppUtils::CreateFontForLogs(HWND hWnd, CFont& fontToCreate)
373 LOGFONT logFont;
374 HDC hScreenDC = ::GetDC(nullptr);
375 logFont.lfHeight = -CDPIAware::Instance().PointsToPixelsY(hWnd, GetLogFontSize());
376 ::ReleaseDC(nullptr, hScreenDC);
377 logFont.lfWidth = 0;
378 logFont.lfEscapement = 0;
379 logFont.lfOrientation = 0;
380 logFont.lfWeight = FW_NORMAL;
381 logFont.lfItalic = 0;
382 logFont.lfUnderline = 0;
383 logFont.lfStrikeOut = 0;
384 logFont.lfCharSet = DEFAULT_CHARSET;
385 logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
386 logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
387 logFont.lfQuality = DRAFT_QUALITY;
388 logFont.lfPitchAndFamily = FF_DONTCARE | FIXED_PITCH;
389 wcsncpy_s(logFont.lfFaceName, static_cast<LPCWSTR>(GetLogFontName()), _TRUNCATE);
390 VERIFY(fontToCreate.CreateFontIndirect(&logFont));