Adjust the background icon size for high dpi monitors
[TortoiseGit.git] / src / Utils / ShellUpdater.cpp
blob8944a1df9bfc70cc5153a1949c341a8dc3f9d86d
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2013, 2015-2016 - TortoiseGit
4 // Copyright (C) 2003-2008,2011,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 "Shellupdater.h"
22 #include "../TGitCache/CacheInterface.h"
23 #include "registry.h"
24 #include "Git.h"
25 #include "SmartHandle.h"
27 CShellUpdater::CShellUpdater(void)
29 m_hInvalidationEvent = CreateEvent(nullptr, FALSE, FALSE, L"TortoiseGitCacheInvalidationEvent");
32 CShellUpdater::~CShellUpdater(void)
34 Flush();
36 CloseHandle(m_hInvalidationEvent);
39 CShellUpdater& CShellUpdater::Instance()
41 static CShellUpdater instance;
42 return instance;
45 /**
46 * Add a single path for updating.
47 * The update will happen at some suitable time in the future
49 void CShellUpdater::AddPathForUpdate(const CTGitPath& path)
51 // Tell the shell extension to purge its cache - we'll redo this when
52 // we actually do the shell-updates, but sometimes there's an earlier update, which
53 // might benefit from cache invalidation
54 SetEvent(m_hInvalidationEvent);
56 m_pathsForUpdating.AddPath(path);
58 /**
59 * Add a list of paths for updating.
60 * The update will happen when the list is destroyed, at the end of execution
62 void CShellUpdater::AddPathsForUpdate(const CTGitPathList& pathList)
64 for (int nPath=0; nPath < pathList.GetCount(); ++nPath)
66 AddPathForUpdate(pathList[nPath]);
70 void CShellUpdater::Flush()
72 if (m_pathsForUpdating.IsEmpty())
73 return;
75 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Flushing shell update list\n");
77 UpdateShell();
78 m_pathsForUpdating.Clear();
81 void CShellUpdater::UpdateShell()
83 // Tell the shell extension to purge its cache
84 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Setting cache invalidation event %I64u\n", GetTickCount64());
85 SetEvent(m_hInvalidationEvent);
87 // We use the SVN 'notify' call-back to add items to the list
88 // Because this might call-back more than once per file (for example, when committing)
89 // it's possible that there may be duplicates in the list.
90 // There's no point asking the shell to do more than it has to, so we remove the duplicates before
91 // passing the list on
92 m_pathsForUpdating.RemoveDuplicates();
94 // if we use the external cache, we tell the cache directly that something
95 // has changed, without the detour via the shell.
96 CAutoFile hPipe = CreateFile(
97 GetCacheCommandPipeName(), // pipe name
98 GENERIC_READ | // read and write access
99 GENERIC_WRITE,
100 0, // no sharing
101 nullptr, // default security attributes
102 OPEN_EXISTING, // opens existing pipe
103 FILE_FLAG_OVERLAPPED, // default attributes
104 nullptr); // no template file
107 if (!hPipe)
108 return;
110 // The pipe connected; change to message-read mode.
111 DWORD dwMode = PIPE_READMODE_MESSAGE;
112 if (!SetNamedPipeHandleState(
113 hPipe, // pipe handle
114 &dwMode, // new pipe mode
115 nullptr, // don't set maximum bytes
116 nullptr)) // don't set maximum time
118 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": SetNamedPipeHandleState failed");
119 return;
122 CTGitPath path;
123 for (int nPath = 0; nPath < m_pathsForUpdating.GetCount(); ++nPath)
125 path.SetFromWin(g_Git.CombinePath(m_pathsForUpdating[nPath]));
126 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Cache Item Update for %s (%I64u)\n", (LPCTSTR)path.GetWinPathString(), GetTickCount64());
127 if (!path.IsDirectory())
129 // send notifications to the shell for changed files - folders are updated by the cache itself.
130 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, path.GetWinPath(), nullptr);
132 DWORD cbWritten;
133 TGITCacheCommand cmd;
134 cmd.command = TGITCACHECOMMAND_CRAWL;
135 wcsncpy_s(cmd.path, path.GetDirectory().GetWinPath(), _countof(cmd.path) - 1);
136 BOOL fSuccess = WriteFile(
137 hPipe, // handle to pipe
138 &cmd, // buffer to write from
139 sizeof(cmd), // number of bytes to write
140 &cbWritten, // number of bytes written
141 nullptr); // not overlapped I/O
143 if (!fSuccess || sizeof(cmd) != cbWritten)
145 DisconnectNamedPipe(hPipe);
146 return;
150 // now tell the cache we don't need it's command thread anymore
151 DWORD cbWritten;
152 TGITCacheCommand cmd;
153 cmd.command = TGITCACHECOMMAND_END;
154 WriteFile(
155 hPipe, // handle to pipe
156 &cmd, // buffer to write from
157 sizeof(cmd), // number of bytes to write
158 &cbWritten, // number of bytes written
159 nullptr); // not overlapped I/O
160 DisconnectNamedPipe(hPipe);
163 bool CShellUpdater::RebuildIcons()
165 const int BUFFER_SIZE = 1024;
166 TCHAR buf[BUFFER_SIZE] = { 0 };
167 HKEY hRegKey = nullptr;
168 DWORD dwRegValue;
169 DWORD dwRegValueTemp;
170 DWORD dwSize;
171 DWORD_PTR dwResult;
172 LONG lRegResult;
174 lRegResult = RegOpenKeyEx(HKEY_CURRENT_USER, L"Control Panel\\Desktop\\WindowMetrics",
175 0, KEY_READ | KEY_WRITE, &hRegKey);
176 if (lRegResult != ERROR_SUCCESS)
177 return false;
178 SCOPE_EXIT { RegCloseKey(hRegKey); };
180 // we're going to change the Shell Icon Size value
181 const TCHAR* sRegValueName = L"Shell Icon Size";
183 // Read registry value
184 dwSize = BUFFER_SIZE;
185 lRegResult = RegQueryValueEx(hRegKey, sRegValueName, nullptr, nullptr,
186 (LPBYTE) buf, &dwSize);
187 if (lRegResult != ERROR_FILE_NOT_FOUND)
189 // If registry key doesn't exist create it using system current setting
190 int iDefaultIconSize = ::GetSystemMetrics(SM_CXICON);
191 if (0 == iDefaultIconSize)
192 iDefaultIconSize = 32;
193 _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, L"%d", iDefaultIconSize);
195 else if (lRegResult != ERROR_SUCCESS)
196 return false;
198 // Change registry value
199 dwRegValue = _wtoi(buf);
200 dwRegValueTemp = dwRegValue-1;
202 dwSize = _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, L"%lu", dwRegValueTemp) + sizeof(TCHAR);
203 lRegResult = RegSetValueEx(hRegKey, sRegValueName, 0, REG_SZ,
204 (LPBYTE) buf, dwSize);
205 if (lRegResult != ERROR_SUCCESS)
206 return false;
208 // Update all windows
209 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS,
210 0, SMTO_ABORTIFHUNG, 5000, &dwResult);
212 // Reset registry value
213 dwSize = _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, L"%lu", dwRegValue) + sizeof(TCHAR);
214 lRegResult = RegSetValueEx(hRegKey, sRegValueName, 0, REG_SZ,
215 (LPBYTE) buf, dwSize);
216 if (lRegResult != ERROR_SUCCESS)
217 return false;
219 // Update all windows
220 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS,
221 0, SMTO_ABORTIFHUNG, 5000, &dwResult);
223 return true;