Prepare release and bump version numbers to 2.13.0
[TortoiseGit.git] / src / Utils / ShellUpdater.cpp
blob588cbeeb6a8a097584850a31bf92f0724af5a1cb
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2013, 2015-2016, 2019 - 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()
29 m_hInvalidationEvent = CreateEvent(nullptr, FALSE, FALSE, L"TortoiseGitCacheInvalidationEvent");
32 CShellUpdater::~CShellUpdater()
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", static_cast<LPCWSTR>(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 wchar_t buf[BUFFER_SIZE] = { 0 };
167 HKEY hRegKey = nullptr;
168 DWORD dwRegValue;
169 DWORD dwRegValueTemp;
170 DWORD dwSize;
171 DWORD_PTR dwResult;
173 if (RegOpenKeyEx(HKEY_CURRENT_USER, L"Control Panel\\Desktop\\WindowMetrics", 0, KEY_READ | KEY_WRITE, &hRegKey) != ERROR_SUCCESS)
174 return false;
175 SCOPE_EXIT { RegCloseKey(hRegKey); };
177 // we're going to change the Shell Icon Size value
178 const wchar_t* sRegValueName = L"Shell Icon Size";
180 // Read registry value
181 dwSize = BUFFER_SIZE;
182 if (LONG lRegResult = RegQueryValueEx(hRegKey, sRegValueName, nullptr, nullptr, reinterpret_cast<LPBYTE>(buf), &dwSize); lRegResult != ERROR_FILE_NOT_FOUND)
184 // If registry key doesn't exist create it using system current setting
185 int iDefaultIconSize = ::GetSystemMetrics(SM_CXICON);
186 if (0 == iDefaultIconSize)
187 iDefaultIconSize = 32;
188 _snwprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, L"%d", iDefaultIconSize);
190 else if (lRegResult != ERROR_SUCCESS)
191 return false;
193 // Change registry value
194 dwRegValue = _wtoi(buf);
195 dwRegValueTemp = dwRegValue-1;
197 dwSize = _snwprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, L"%lu", dwRegValueTemp) + sizeof(wchar_t);
198 if (RegSetValueEx(hRegKey, sRegValueName, 0, REG_SZ, reinterpret_cast<LPBYTE>(buf), dwSize) != ERROR_SUCCESS)
199 return false;
201 // Update all windows
202 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS,
203 0, SMTO_ABORTIFHUNG, 5000, &dwResult);
205 // Reset registry value
206 dwSize = _snwprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, L"%lu", dwRegValue) + sizeof(wchar_t);
207 if (RegSetValueEx(hRegKey, sRegValueName, 0, REG_SZ, reinterpret_cast<LPBYTE>(buf), dwSize) != ERROR_SUCCESS)
208 return false;
210 // Update all windows
211 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS,
212 0, SMTO_ABORTIFHUNG, 5000, &dwResult);
214 return true;