1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2008 - TortoiseSVN
\r
5 // This program is free software; you can redistribute it and/or
\r
6 // modify it under the terms of the GNU General Public License
\r
7 // as published by the Free Software Foundation; either version 2
\r
8 // of the License, or (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, write to the Free Software Foundation,
\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
20 #include "Shellupdater.h"
\r
21 #include "../TSVNCache/CacheInterface.h"
\r
22 #include "Registry.h"
\r
24 CShellUpdater::CShellUpdater(void)
\r
26 m_hInvalidationEvent = CreateEvent(NULL, FALSE, FALSE, _T("TortoiseSVNCacheInvalidationEvent"));
\r
29 CShellUpdater::~CShellUpdater(void)
\r
33 CloseHandle(m_hInvalidationEvent);
\r
36 CShellUpdater& CShellUpdater::Instance()
\r
38 static CShellUpdater instance;
\r
43 * Add a single path for updating.
\r
44 * The update will happen at some suitable time in the future
\r
46 void CShellUpdater::AddPathForUpdate(const CTSVNPath& path)
\r
48 // Tell the shell extension to purge its cache - we'll redo this when
\r
49 // we actually do the shell-updates, but sometimes there's an earlier update, which
\r
50 // might benefit from cache invalidation
\r
51 SetEvent(m_hInvalidationEvent);
\r
53 m_pathsForUpdating.AddPath(path);
\r
56 * Add a list of paths for updating.
\r
57 * The update will happen when the list is destroyed, at the end of execution
\r
59 void CShellUpdater::AddPathsForUpdate(const CTSVNPathList& pathList)
\r
61 for(int nPath=0; nPath < pathList.GetCount(); nPath++)
\r
63 AddPathForUpdate(pathList[nPath]);
\r
67 void CShellUpdater::Flush()
\r
69 if(m_pathsForUpdating.GetCount() > 0)
\r
71 ATLTRACE("Flushing shell update list\n");
\r
74 m_pathsForUpdating.Clear();
\r
78 void CShellUpdater::UpdateShell()
\r
80 // Tell the shell extension to purge its cache
\r
81 ATLTRACE("Setting cache invalidation event %d\n", GetTickCount());
\r
82 SetEvent(m_hInvalidationEvent);
\r
84 // We use the SVN 'notify' call-back to add items to the list
\r
85 // Because this might call-back more than once per file (for example, when committing)
\r
86 // it's possible that there may be duplicates in the list.
\r
87 // There's no point asking the shell to do more than it has to, so we remove the duplicates before
\r
88 // passing the list on
\r
89 m_pathsForUpdating.RemoveDuplicates();
\r
91 // if we use the external cache, we tell the cache directly that something
\r
92 // has changed, without the detour via the shell.
\r
93 HANDLE hPipe = CreateFile(
\r
94 GetCacheCommandPipeName(), // pipe name
\r
95 GENERIC_READ | // read and write access
\r
98 NULL, // default security attributes
\r
99 OPEN_EXISTING, // opens existing pipe
\r
100 FILE_FLAG_OVERLAPPED, // default attributes
\r
101 NULL); // no template file
\r
104 if (hPipe != INVALID_HANDLE_VALUE)
\r
106 // The pipe connected; change to message-read mode.
\r
109 dwMode = PIPE_READMODE_MESSAGE;
\r
110 if(SetNamedPipeHandleState(
\r
111 hPipe, // pipe handle
\r
112 &dwMode, // new pipe mode
\r
113 NULL, // don't set maximum bytes
\r
114 NULL)) // don't set maximum time
\r
116 for(int nPath = 0; nPath < m_pathsForUpdating.GetCount(); nPath++)
\r
118 ATLTRACE(_T("Cache Item Update for %s (%d)\n"), m_pathsForUpdating[nPath].GetDirectory().GetWinPathString(), GetTickCount());
\r
119 if (!m_pathsForUpdating[nPath].IsDirectory())
\r
121 // send notifications to the shell for changed files - folders are updated by the cache itself.
\r
122 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, m_pathsForUpdating[nPath].GetWinPath(), NULL);
\r
125 TSVNCacheCommand cmd;
\r
126 cmd.command = TSVNCACHECOMMAND_CRAWL;
\r
127 wcsncpy_s(cmd.path, MAX_PATH+1, m_pathsForUpdating[nPath].GetDirectory().GetWinPath(), MAX_PATH);
\r
128 BOOL fSuccess = WriteFile(
\r
129 hPipe, // handle to pipe
\r
130 &cmd, // buffer to write from
\r
131 sizeof(cmd), // number of bytes to write
\r
132 &cbWritten, // number of bytes written
\r
133 NULL); // not overlapped I/O
\r
135 if (! fSuccess || sizeof(cmd) != cbWritten)
\r
137 DisconnectNamedPipe(hPipe);
\r
138 CloseHandle(hPipe);
\r
139 hPipe = INVALID_HANDLE_VALUE;
\r
143 if (hPipe != INVALID_HANDLE_VALUE)
\r
145 // now tell the cache we don't need it's command thread anymore
\r
147 TSVNCacheCommand cmd;
\r
148 cmd.command = TSVNCACHECOMMAND_END;
\r
150 hPipe, // handle to pipe
\r
151 &cmd, // buffer to write from
\r
152 sizeof(cmd), // number of bytes to write
\r
153 &cbWritten, // number of bytes written
\r
154 NULL); // not overlapped I/O
\r
155 DisconnectNamedPipe(hPipe);
\r
156 CloseHandle(hPipe);
\r
157 hPipe = INVALID_HANDLE_VALUE;
\r
162 ATLTRACE("SetNamedPipeHandleState failed");
\r
163 CloseHandle(hPipe);
\r
168 bool CShellUpdater::RebuildIcons()
\r
170 const int BUFFER_SIZE = 1024;
\r
174 DWORD dwRegValueTemp;
\r
176 DWORD_PTR dwResult;
\r
178 std::wstring sRegValueName;
\r
179 std::wstring sDefaultIconSize;
\r
180 int iDefaultIconSize;
\r
181 bool bResult = false;
\r
183 lRegResult = RegOpenKeyEx(HKEY_CURRENT_USER, _T("Control Panel\\Desktop\\WindowMetrics"),
\r
184 0, KEY_READ | KEY_WRITE, &hRegKey);
\r
185 if (lRegResult != ERROR_SUCCESS)
\r
188 buf = new TCHAR[BUFFER_SIZE];
\r
192 // we're going to change the Shell Icon Size value
\r
193 sRegValueName = _T("Shell Icon Size");
\r
195 // Read registry value
\r
196 dwSize = BUFFER_SIZE;
\r
197 lRegResult = RegQueryValueEx(hRegKey, sRegValueName.c_str(), NULL, NULL,
\r
198 (LPBYTE) buf, &dwSize);
\r
199 if (lRegResult != ERROR_FILE_NOT_FOUND)
\r
201 // If registry key doesn't exist create it using system current setting
\r
202 iDefaultIconSize = ::GetSystemMetrics(SM_CXICON);
\r
203 if (0 == iDefaultIconSize)
\r
204 iDefaultIconSize = 32;
\r
205 _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, _T("%d"), iDefaultIconSize);
\r
207 else if (lRegResult != ERROR_SUCCESS)
\r
210 // Change registry value
\r
211 dwRegValue = _ttoi(buf);
\r
212 dwRegValueTemp = dwRegValue-1;
\r
214 dwSize = _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, _T("%d"), dwRegValueTemp) + sizeof(TCHAR);
\r
215 lRegResult = RegSetValueEx(hRegKey, sRegValueName.c_str(), 0, REG_SZ,
\r
216 (LPBYTE) buf, dwSize);
\r
217 if (lRegResult != ERROR_SUCCESS)
\r
221 // Update all windows
\r
222 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS,
\r
223 0, SMTO_ABORTIFHUNG, 5000, &dwResult);
\r
225 // Reset registry value
\r
226 dwSize = _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, _T("%d"), dwRegValue) + sizeof(TCHAR);
\r
227 lRegResult = RegSetValueEx(hRegKey, sRegValueName.c_str(), 0, REG_SZ,
\r
228 (LPBYTE) buf, dwSize);
\r
229 if(lRegResult != ERROR_SUCCESS)
\r
232 // Update all windows
\r
233 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS,
\r
234 0, SMTO_ABORTIFHUNG, 5000, &dwResult);
\r
241 RegCloseKey(hRegKey);
\r