1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2013, 2015-2018 - TortoiseGit
4 // Copyright (C) 2003-2008,2010 - 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"
31 extern CString sOrigCWD
;
32 extern CString g_sGroupingUUID
;
34 bool CCommonAppUtils::LaunchApplication(const CString
& sCommandLine
, UINT idErrMessageFormat
, bool bWaitForStartup
, CString
*cwd
, bool uac
)
36 CString theCWD
= sOrigCWD
;
43 SHELLEXECUTEINFO shellinfo
= { 0 };
44 shellinfo
.cbSize
= sizeof(shellinfo
);
45 shellinfo
.lpVerb
= L
"runas";
46 shellinfo
.nShow
= SW_SHOWNORMAL
;
47 shellinfo
.fMask
= SEE_MASK_NOCLOSEPROCESS
;
48 shellinfo
.lpDirectory
= theCWD
;
50 int pos
= sCommandLine
.Find('"');
53 pos
= sCommandLine
.Find('"', 2);
56 file
= sCommandLine
.Mid(1, pos
- 1);
57 param
= sCommandLine
.Mid(pos
+ 1);
61 if (idErrMessageFormat
!= 0)
64 temp
.Format(idErrMessageFormat
, (LPCTSTR
)CFormatMessageWrapper());
65 MessageBox(nullptr, temp
, L
"TortoiseGit", MB_OK
| MB_ICONINFORMATION
);
72 pos
= sCommandLine
.Find(' ', 1);
75 file
= sCommandLine
.Left(pos
);
76 param
= sCommandLine
.Mid(pos
+ 1);
82 shellinfo
.lpFile
= file
;
83 shellinfo
.lpParameters
= param
;
85 if (!ShellExecuteEx(&shellinfo
))
87 if (idErrMessageFormat
!= 0)
90 temp
.Format(idErrMessageFormat
, (LPCTSTR
)CFormatMessageWrapper());
91 MessageBox(nullptr, temp
, L
"TortoiseGit", MB_OK
| MB_ICONINFORMATION
);
96 if (shellinfo
.hProcess
)
99 WaitForInputIdle(shellinfo
.hProcess
, 10000);
101 CloseHandle(shellinfo
.hProcess
);
107 STARTUPINFO startup
= { 0 };
108 PROCESS_INFORMATION process
= { 0 };
109 startup
.cb
= sizeof(startup
);
111 CString
cleanCommandLine(sCommandLine
);
112 if (CreateProcess(nullptr, cleanCommandLine
.GetBuffer(), nullptr, nullptr, FALSE
, CREATE_UNICODE_ENVIRONMENT
, nullptr, theCWD
, &startup
, &process
) == 0)
114 if (idErrMessageFormat
)
117 temp
.Format(idErrMessageFormat
, (LPCTSTR
)CFormatMessageWrapper());
118 MessageBox(nullptr, temp
, L
"TortoiseGit", MB_OK
| MB_ICONINFORMATION
);
123 AllowSetForegroundWindow(process
.dwProcessId
);
126 WaitForInputIdle(process
.hProcess
, 10000);
128 CloseHandle(process
.hThread
);
129 CloseHandle(process
.hProcess
);
134 bool CCommonAppUtils::RunTortoiseGitProc(const CString
& sCommandLine
, bool uac
, bool includeGroupingUUID
)
136 CString pathToExecutable
= CPathUtils::GetAppDirectory() + L
"TortoiseGitProc.exe";
138 sCmd
.Format(L
"\"%s\" %s", (LPCTSTR
)pathToExecutable
, (LPCTSTR
)sCommandLine
);
139 if (AfxGetMainWnd()->GetSafeHwnd() && (sCommandLine
.Find(L
"/hwnd:") < 0))
140 sCmd
.AppendFormat(L
" /hwnd:%p", (void*)AfxGetMainWnd()->GetSafeHwnd());
141 if (!g_sGroupingUUID
.IsEmpty() && includeGroupingUUID
)
143 sCmd
+= L
" /groupuuid:\"";
144 sCmd
+= g_sGroupingUUID
;
148 return LaunchApplication(sCmd
, NULL
, false, nullptr, uac
);
151 bool CCommonAppUtils::IsAdminLogin()
153 SID_IDENTIFIER_AUTHORITY ntAuthority
= SECURITY_NT_AUTHORITY
;
154 PSID administratorsGroup
;
156 if (!AllocateAndInitializeSid(&ntAuthority
, 2, SECURITY_BUILTIN_DOMAIN_RID
, DOMAIN_ALIAS_RID_ADMINS
, 0, 0, 0, 0, 0, 0, &administratorsGroup
))
158 SCOPE_EXIT
{ FreeSid(administratorsGroup
); };
160 // Check whether the token is present in admin group.
161 BOOL isInAdminGroup
= FALSE
;
162 if (!CheckTokenMembership(nullptr, administratorsGroup
, &isInAdminGroup
))
165 return !!isInAdminGroup
;
168 bool CCommonAppUtils::SetListCtrlBackgroundImage(HWND hListCtrl
, UINT nID
)
170 return SetListCtrlBackgroundImage(hListCtrl
, nID
, CDPIAware::Instance().ScaleX(128), CDPIAware::Instance().ScaleY(128));
173 bool CCommonAppUtils::SetListCtrlBackgroundImage(HWND hListCtrl
, UINT nID
, int width
, int height
)
175 if ((((DWORD
)CRegStdDWORD(L
"Software\\TortoiseGit\\ShowListBackgroundImage", TRUE
)) == FALSE
))
177 ListView_SetTextBkColor(hListCtrl
, CLR_NONE
);
178 COLORREF bkColor
= ListView_GetBkColor(hListCtrl
);
179 // create a bitmap from the icon
180 auto hIcon
= ::LoadIconEx(AfxGetResourceHandle(), MAKEINTRESOURCE(nID
), width
, height
);
183 SCOPE_EXIT
{ DestroyIcon(hIcon
); };
187 rect
.bottom
= height
;
189 HWND desktop
= ::GetDesktopWindow();
193 HDC screen_dev
= ::GetDC(desktop
);
196 SCOPE_EXIT
{ ::ReleaseDC(desktop
, screen_dev
); };
198 // Create a compatible DC
199 HDC dst_hdc
= ::CreateCompatibleDC(screen_dev
);
202 SCOPE_EXIT
{ ::DeleteDC(dst_hdc
); };
204 // Create a new bitmap of icon size
205 HBITMAP bmp
= ::CreateCompatibleBitmap(screen_dev
, rect
.right
, rect
.bottom
);
209 // Select it into the compatible DC
210 HBITMAP old_dst_bmp
= (HBITMAP
)::SelectObject(dst_hdc
, bmp
);
211 // Fill the background of the compatible DC with the given color
212 ::SetBkColor(dst_hdc
, bkColor
);
213 ::ExtTextOut(dst_hdc
, 0, 0, ETO_OPAQUE
, &rect
, nullptr, 0, nullptr);
215 // Draw the icon into the compatible DC
216 ::DrawIconEx(dst_hdc
, 0, 0, hIcon
, rect
.right
, rect
.bottom
, 0, nullptr, DI_NORMAL
);
217 ::SelectObject(dst_hdc
, old_dst_bmp
);
220 lv
.ulFlags
= LVBKIF_TYPE_WATERMARK
;
222 lv
.xOffsetPercent
= 100;
223 lv
.yOffsetPercent
= 100;
224 ListView_SetBkImage(hListCtrl
, &lv
);
228 bool CCommonAppUtils::FileOpenSave(CString
& path
, int* filterindex
, UINT title
, UINT filterId
, bool bOpen
, HWND hwndOwner
, LPCTSTR defaultExt
, bool handleAsFile
)
230 // Create a new common save file dialog
231 CComPtr
<IFileDialog
> pfd
;
233 if (!SUCCEEDED(pfd
.CoCreateInstance(bOpen
? CLSID_FileOpenDialog
: CLSID_FileSaveDialog
, nullptr, CLSCTX_INPROC_SERVER
)))
236 // Set the dialog options
238 if (!SUCCEEDED(pfd
->GetOptions(&dwOptions
)))
243 if (!SUCCEEDED(pfd
->SetOptions(dwOptions
| FOS_FILEMUSTEXIST
| FOS_FORCEFILESYSTEM
| FOS_PATHMUSTEXIST
)))
248 if (!SUCCEEDED(pfd
->SetOptions(dwOptions
| FOS_OVERWRITEPROMPT
| FOS_FORCEFILESYSTEM
| FOS_PATHMUSTEXIST
)))
251 if ((!PathIsDirectory(path
) || handleAsFile
) && !SUCCEEDED(pfd
->SetFileName(CPathUtils::GetFileNameFromPath(path
))))
258 temp
.LoadString(title
);
259 CStringUtils::RemoveAccelerators(temp
);
262 CComPtr
<IShellItem
> psiDefaultFolder
;
265 CSelectFileFilter
fileFilter(filterId
);
266 if (!SUCCEEDED(pfd
->SetFileTypes(fileFilter
.GetCount(), fileFilter
)))
268 if (filterId
== 1602 || filterId
== 2501) // IDS_GITEXEFILEFILTER || IDS_PROGRAMSFILEFILTER
270 pfd
->SetClientGuid({ 0x323ca4b0, 0x62df, 0x4a08, { 0xa5, 0x5, 0x58, 0xde, 0xa2, 0xb9, 0x2d, 0xcd } });
271 if (SUCCEEDED(SHCreateItemFromParsingName(CPathUtils::GetProgramsDirectory(), nullptr, IID_PPV_ARGS(&psiDefaultFolder
))))
272 pfd
->SetDefaultFolder(psiDefaultFolder
);
274 else if (filterId
== 1120) // IDS_PUTTYKEYFILEFILTER
276 pfd
->SetClientGuid({ 0x271dbd3b, 0x50da, 0x4148, { 0x95, 0xfd, 0x64, 0x73, 0x69, 0xd1, 0x74, 0x2 } });
277 if (SUCCEEDED(SHCreateItemFromParsingName(CPathUtils::GetDocumentsDirectory(), nullptr, IID_PPV_ARGS(&psiDefaultFolder
))))
278 pfd
->SetDefaultFolder(psiDefaultFolder
);
282 if (defaultExt
&& !SUCCEEDED(pfd
->SetDefaultExtension(defaultExt
)))
285 // set the default folder
286 CComPtr
<IShellItem
> psiFolder
;
287 if (CStringUtils::StartsWith(path
, L
"\\") || path
.Mid(1, 2) == L
":\\")
290 if (!PathIsDirectory(dir
) || handleAsFile
)
292 if (PathRemoveFileSpec(dir
.GetBuffer()))
297 if (!dir
.IsEmpty() && SUCCEEDED(SHCreateItemFromParsingName(dir
, nullptr, IID_PPV_ARGS(&psiFolder
))))
298 pfd
->SetFolder(psiFolder
);
301 // Show the save/open file dialog
302 if (!SUCCEEDED(pfd
->Show(hwndOwner
)))
305 // Get the selection from the user
306 CComPtr
<IShellItem
> psiResult
;
307 if (!SUCCEEDED(pfd
->GetResult(&psiResult
)))
310 PWSTR pszPath
= nullptr;
311 if (!SUCCEEDED(psiResult
->GetDisplayName(SIGDN_FILESYSPATH
, &pszPath
)))
314 path
= CString(pszPath
);
318 pfd
->GetFileTypeIndex(&fi
);
324 HICON
CCommonAppUtils::LoadIconEx(UINT resourceId
, UINT cx
, UINT cy
)
326 return ::LoadIconEx(AfxGetResourceHandle(), MAKEINTRESOURCE(resourceId
), cx
, cy
);
329 void CCommonAppUtils::SetCharFormat(CWnd
* window
, DWORD mask
, DWORD effects
, const std::vector
<CHARRANGE
>& positions
)
331 CHARFORMAT2 format
= {};
332 format
.cbSize
= sizeof(CHARFORMAT2
);
333 format
.dwMask
= mask
;
334 format
.dwEffects
= effects
;
335 format
.crTextColor
= effects
;
337 for (const auto& range
: positions
)
339 window
->SendMessage(EM_EXSETSEL
, NULL
, (LPARAM
)&range
);
340 window
->SendMessage(EM_SETCHARFORMAT
, SCF_SELECTION
, (LPARAM
)&format
);
344 void CCommonAppUtils::SetCharFormat(CWnd
* window
, DWORD mask
, DWORD effects
)
346 CHARFORMAT2 format
= {};
347 format
.cbSize
= sizeof(CHARFORMAT2
);
348 format
.dwMask
= mask
;
349 format
.dwEffects
= effects
;
350 window
->SendMessage(EM_SETCHARFORMAT
, SCF_SELECTION
, (LPARAM
)&format
);