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.
21 #include "../Resources/LoglistCommonResource.h"
22 #include "CommonAppUtils.h"
23 #include "PathUtils.h"
24 #include "StringUtils.h"
25 #include "FormatMessageWrapper.h"
27 #include "SelectFileFilter.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
;
39 theCWD
= *flags
.psCWD
;
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('"');
54 pos
= sCommandLine
.Find('"', 2);
57 file
= sCommandLine
.Mid(1, pos
- 1);
58 param
= sCommandLine
.Mid(pos
+ 1);
62 if (flags
.uiIDErrMessageFormat
!= 0)
65 temp
.Format(flags
.uiIDErrMessageFormat
, static_cast<LPCWSTR
>(CFormatMessageWrapper()));
66 MessageBox(nullptr, temp
, L
"TortoiseGit", MB_OK
| MB_ICONINFORMATION
);
73 pos
= sCommandLine
.Find(' ', 1);
76 file
= sCommandLine
.Left(pos
);
77 param
= sCommandLine
.Mid(pos
+ 1);
83 shellinfo
.lpFile
= file
;
84 shellinfo
.lpParameters
= param
;
86 if (!ShellExecuteEx(&shellinfo
))
88 if (flags
.uiIDErrMessageFormat
!= 0)
91 temp
.Format(flags
.uiIDErrMessageFormat
, static_cast<LPCWSTR
>(CFormatMessageWrapper()));
92 MessageBox(nullptr, temp
, L
"TortoiseGit", MB_OK
| MB_ICONINFORMATION
);
97 CAutoGeneralHandle
piProcess(std::move(shellinfo
.hProcess
));
100 if (flags
.bWaitForStartup
)
101 WaitForInputIdle(piProcess
, 10000);
103 if (flags
.bWaitForExit
)
107 handles
[0] = piProcess
;
108 if (flags
.hWaitHandle
)
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
))
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
)
138 temp
.Format(flags
.uiIDErrMessageFormat
, static_cast<LPCWSTR
>(CFormatMessageWrapper()));
139 MessageBox(nullptr, temp
, L
"TortoiseGit", MB_OK
| MB_ICONINFORMATION
);
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
)
156 handles
[0] = piProcess
;
157 if (flags
.hWaitHandle
)
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
))
176 bool CCommonAppUtils::RunTortoiseGitProc(const CString
& sCommandLine
, bool uac
, bool includeGroupingUUID
)
178 CString pathToExecutable
= CPathUtils::GetAppDirectory() + L
"TortoiseGitProc.exe";
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
;
190 return LaunchApplication(sCmd
, LaunchApplicationFlags().UAC(uac
));
193 bool CCommonAppUtils::IsAdminLogin()
195 SID_IDENTIFIER_AUTHORITY ntAuthority
= SECURITY_NT_AUTHORITY
;
196 PSID administratorsGroup
;
198 if (!AllocateAndInitializeSid(&ntAuthority
, 2, SECURITY_BUILTIN_DOMAIN_RID
, DOMAIN_ALIAS_RID_ADMINS
, 0, 0, 0, 0, 0, 0, &administratorsGroup
))
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
))
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
)
219 // create a bitmap from the icon
220 CAutoIcon hIcon
= ::LoadIconEx(AfxGetResourceHandle(), MAKEINTRESOURCE(nID
), width
, height
);
224 IconBitmapUtils iutils
;
225 auto bmp
= iutils
.IconToBitmapPARGB32(hIcon
, width
, height
);
228 lv
.ulFlags
= LVBKIF_TYPE_WATERMARK
| LVBKIF_FLAG_ALPHABLEND
;
230 lv
.xOffsetPercent
= 100;
231 lv
.yOffsetPercent
= 100;
232 ListView_SetBkImage(hListCtrl
, &lv
);
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
)))
244 // Set the dialog options
246 if (!SUCCEEDED(pfd
->GetOptions(&dwOptions
)))
251 if (!SUCCEEDED(pfd
->SetOptions(dwOptions
| FOS_FILEMUSTEXIST
| FOS_FORCEFILESYSTEM
| FOS_PATHMUSTEXIST
)))
256 if (!SUCCEEDED(pfd
->SetOptions(dwOptions
| FOS_OVERWRITEPROMPT
| FOS_FORCEFILESYSTEM
| FOS_PATHMUSTEXIST
)))
259 if ((!PathIsDirectory(path
) || handleAsFile
) && !SUCCEEDED(pfd
->SetFileName(CPathUtils::GetFileNameFromPath(path
))))
266 temp
.LoadString(title
);
267 CStringUtils::RemoveAccelerators(temp
);
270 CComPtr
<IShellItem
> psiDefaultFolder
;
273 CSelectFileFilter
fileFilter(filterId
);
274 if (!SUCCEEDED(pfd
->SetFileTypes(fileFilter
.GetCount(), fileFilter
)))
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
)))
293 // set the default folder
294 CComPtr
<IShellItem
> psiFolder
;
295 if (CStringUtils::StartsWith(path
, L
"\\") || path
.Mid(1, 2) == L
":\\")
298 if (!PathIsDirectory(dir
) || handleAsFile
)
300 if (PathRemoveFileSpec(dir
.GetBuffer()))
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
)))
313 // Get the selection from the user
314 CComPtr
<IShellItem
> psiResult
;
315 if (!SUCCEEDED(pfd
->GetResult(&psiResult
)))
318 CComHeapPtr
<WCHAR
> pszPath
;
319 if (!SUCCEEDED(psiResult
->GetDisplayName(SIGDN_FILESYSPATH
, &pszPath
)))
322 path
= CString(pszPath
);
326 pfd
->GetFileTypeIndex(&fi
);
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
)
374 HDC hScreenDC
= ::GetDC(nullptr);
375 logFont
.lfHeight
= -CDPIAware::Instance().PointsToPixelsY(hWnd
, GetLogFontSize());
376 ::ReleaseDC(nullptr, hScreenDC
);
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
));