Fixed issue #546: Exporting without specifying a zip filename sends all zip output...
[TortoiseGit.git] / src / Utils / ShellUpdater.cpp
blobbbd8492e65d81347c10c8bcc800767e22be97725
1 // TortoiseSVN - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "StdAfx.h"
20 #include "Shellupdater.h"
21 #include "../TGitCache/CacheInterface.h"
22 #include "Registry.h"
23 #include "git.h"
25 CShellUpdater::CShellUpdater(void)
27 m_hInvalidationEvent = CreateEvent(NULL, FALSE, FALSE, _T("TortoiseGitCacheInvalidationEvent"));
30 CShellUpdater::~CShellUpdater(void)
32 Flush();
34 CloseHandle(m_hInvalidationEvent);
37 CShellUpdater& CShellUpdater::Instance()
39 static CShellUpdater instance;
40 return instance;
43 /**
44 * Add a single path for updating.
45 * The update will happen at some suitable time in the future
47 void CShellUpdater::AddPathForUpdate(const CTGitPath& path)
49 // Tell the shell extension to purge its cache - we'll redo this when
50 // we actually do the shell-updates, but sometimes there's an earlier update, which
51 // might benefit from cache invalidation
52 SetEvent(m_hInvalidationEvent);
54 m_pathsForUpdating.AddPath(path);
56 /**
57 * Add a list of paths for updating.
58 * The update will happen when the list is destroyed, at the end of execution
60 void CShellUpdater::AddPathsForUpdate(const CTGitPathList& pathList)
62 for(int nPath=0; nPath < pathList.GetCount(); nPath++)
64 AddPathForUpdate(pathList[nPath]);
68 void CShellUpdater::Flush()
70 if(m_pathsForUpdating.GetCount() > 0)
72 ATLTRACE("Flushing shell update list\n");
74 UpdateShell();
75 m_pathsForUpdating.Clear();
79 void CShellUpdater::UpdateShell()
81 // Tell the shell extension to purge its cache
82 ATLTRACE("Setting cache invalidation event %d\n", GetTickCount());
83 SetEvent(m_hInvalidationEvent);
85 // We use the SVN 'notify' call-back to add items to the list
86 // Because this might call-back more than once per file (for example, when committing)
87 // it's possible that there may be duplicates in the list.
88 // There's no point asking the shell to do more than it has to, so we remove the duplicates before
89 // passing the list on
90 m_pathsForUpdating.RemoveDuplicates();
92 // if we use the external cache, we tell the cache directly that something
93 // has changed, without the detour via the shell.
94 HANDLE hPipe = CreateFile(
95 GetCacheCommandPipeName(), // pipe name
96 GENERIC_READ | // read and write access
97 GENERIC_WRITE,
98 0, // no sharing
99 NULL, // default security attributes
100 OPEN_EXISTING, // opens existing pipe
101 FILE_FLAG_OVERLAPPED, // default attributes
102 NULL); // no template file
105 if (hPipe != INVALID_HANDLE_VALUE)
107 // The pipe connected; change to message-read mode.
108 DWORD dwMode;
110 dwMode = PIPE_READMODE_MESSAGE;
111 if(SetNamedPipeHandleState(
112 hPipe, // pipe handle
113 &dwMode, // new pipe mode
114 NULL, // don't set maximum bytes
115 NULL)) // don't set maximum time
117 CTGitPath path;
118 for(int nPath = 0; nPath < m_pathsForUpdating.GetCount(); nPath++)
120 path.SetFromWin(g_Git.m_CurrentDir+_T("\\")+m_pathsForUpdating[nPath].GetWinPathString());
121 ATLTRACE(_T("Cache Item Update for %s (%d)\n"), path.GetWinPathString(), GetTickCount());
122 if (!path.IsDirectory())
124 // send notifications to the shell for changed files - folders are updated by the cache itself.
125 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, path.GetWinPath(), NULL);
127 DWORD cbWritten;
128 TSVNCacheCommand cmd;
129 cmd.command = TSVNCACHECOMMAND_CRAWL;
130 wcsncpy_s(cmd.path, MAX_PATH+1, path.GetDirectory().GetWinPath(), MAX_PATH);
131 BOOL fSuccess = WriteFile(
132 hPipe, // handle to pipe
133 &cmd, // buffer to write from
134 sizeof(cmd), // number of bytes to write
135 &cbWritten, // number of bytes written
136 NULL); // not overlapped I/O
138 if (! fSuccess || sizeof(cmd) != cbWritten)
140 DisconnectNamedPipe(hPipe);
141 CloseHandle(hPipe);
142 hPipe = INVALID_HANDLE_VALUE;
143 break;
146 if (hPipe != INVALID_HANDLE_VALUE)
148 // now tell the cache we don't need it's command thread anymore
149 DWORD cbWritten;
150 TSVNCacheCommand cmd;
151 cmd.command = TSVNCACHECOMMAND_END;
152 WriteFile(
153 hPipe, // handle to pipe
154 &cmd, // buffer to write from
155 sizeof(cmd), // number of bytes to write
156 &cbWritten, // number of bytes written
157 NULL); // not overlapped I/O
158 DisconnectNamedPipe(hPipe);
159 CloseHandle(hPipe);
160 hPipe = INVALID_HANDLE_VALUE;
163 else
165 ATLTRACE("SetNamedPipeHandleState failed");
166 CloseHandle(hPipe);
171 bool CShellUpdater::RebuildIcons()
173 const int BUFFER_SIZE = 1024;
174 TCHAR *buf = NULL;
175 HKEY hRegKey = 0;
176 DWORD dwRegValue;
177 DWORD dwRegValueTemp;
178 DWORD dwSize;
179 DWORD_PTR dwResult;
180 LONG lRegResult;
181 std::wstring sRegValueName;
182 std::wstring sDefaultIconSize;
183 int iDefaultIconSize;
184 bool bResult = false;
186 lRegResult = RegOpenKeyEx(HKEY_CURRENT_USER, _T("Control Panel\\Desktop\\WindowMetrics"),
187 0, KEY_READ | KEY_WRITE, &hRegKey);
188 if (lRegResult != ERROR_SUCCESS)
189 goto Cleanup;
191 buf = new TCHAR[BUFFER_SIZE];
192 if(buf == NULL)
193 goto Cleanup;
195 // we're going to change the Shell Icon Size value
196 sRegValueName = _T("Shell Icon Size");
198 // Read registry value
199 dwSize = BUFFER_SIZE;
200 lRegResult = RegQueryValueEx(hRegKey, sRegValueName.c_str(), NULL, NULL,
201 (LPBYTE) buf, &dwSize);
202 if (lRegResult != ERROR_FILE_NOT_FOUND)
204 // If registry key doesn't exist create it using system current setting
205 iDefaultIconSize = ::GetSystemMetrics(SM_CXICON);
206 if (0 == iDefaultIconSize)
207 iDefaultIconSize = 32;
208 _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, _T("%d"), iDefaultIconSize);
210 else if (lRegResult != ERROR_SUCCESS)
211 goto Cleanup;
213 // Change registry value
214 dwRegValue = _ttoi(buf);
215 dwRegValueTemp = dwRegValue-1;
217 dwSize = _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, _T("%d"), dwRegValueTemp) + sizeof(TCHAR);
218 lRegResult = RegSetValueEx(hRegKey, sRegValueName.c_str(), 0, REG_SZ,
219 (LPBYTE) buf, dwSize);
220 if (lRegResult != ERROR_SUCCESS)
221 goto Cleanup;
224 // Update all windows
225 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS,
226 0, SMTO_ABORTIFHUNG, 5000, &dwResult);
228 // Reset registry value
229 dwSize = _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, _T("%d"), dwRegValue) + sizeof(TCHAR);
230 lRegResult = RegSetValueEx(hRegKey, sRegValueName.c_str(), 0, REG_SZ,
231 (LPBYTE) buf, dwSize);
232 if(lRegResult != ERROR_SUCCESS)
233 goto Cleanup;
235 // Update all windows
236 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS,
237 0, SMTO_ABORTIFHUNG, 5000, &dwResult);
239 bResult = true;
241 Cleanup:
242 if (hRegKey != 0)
244 RegCloseKey(hRegKey);
246 if (buf != NULL)
248 delete [] buf;
251 return bResult;