Fixed issue #1264: TortoiseProc might crash if commands are executed w/o a working...
[TortoiseGit.git] / src / Utils / ShellUpdater.cpp
blob949f6d8d7827cce34e7b50c7732a44fa3022a0f0
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012 - TortoiseGit
4 // Copyright (C) 2003-2008,2011 - 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(NULL, FALSE, FALSE, _T("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.GetCount() > 0)
74 ATLTRACE("Flushing shell update list\n");
76 UpdateShell();
77 m_pathsForUpdating.Clear();
81 void CShellUpdater::UpdateShell()
83 // Tell the shell extension to purge its cache
84 ATLTRACE("Setting cache invalidation event %d\n", GetTickCount());
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 NULL, // default security attributes
102 OPEN_EXISTING, // opens existing pipe
103 FILE_FLAG_OVERLAPPED, // default attributes
104 NULL); // no template file
107 if (!hPipe)
109 // The pipe connected; change to message-read mode.
110 DWORD dwMode;
112 dwMode = PIPE_READMODE_MESSAGE;
113 if(SetNamedPipeHandleState(
114 hPipe, // pipe handle
115 &dwMode, // new pipe mode
116 NULL, // don't set maximum bytes
117 NULL)) // don't set maximum time
119 CTGitPath path;
120 for(int nPath = 0; nPath < m_pathsForUpdating.GetCount(); nPath++)
122 path.SetFromWin(g_Git.m_CurrentDir+_T("\\")+m_pathsForUpdating[nPath].GetWinPathString());
123 ATLTRACE(_T("Cache Item Update for %s (%d)\n"), path.GetWinPathString(), GetTickCount());
124 if (!path.IsDirectory())
126 // send notifications to the shell for changed files - folders are updated by the cache itself.
127 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, path.GetWinPath(), NULL);
129 DWORD cbWritten;
130 TGITCacheCommand cmd;
131 cmd.command = TGITCACHECOMMAND_CRAWL;
132 wcsncpy_s(cmd.path, MAX_PATH+1, path.GetDirectory().GetWinPath(), MAX_PATH);
133 BOOL fSuccess = WriteFile(
134 hPipe, // handle to pipe
135 &cmd, // buffer to write from
136 sizeof(cmd), // number of bytes to write
137 &cbWritten, // number of bytes written
138 NULL); // not overlapped I/O
140 if (! fSuccess || sizeof(cmd) != cbWritten)
142 DisconnectNamedPipe(hPipe);
143 return;
146 if (!hPipe)
148 // now tell the cache we don't need it's command thread anymore
149 DWORD cbWritten;
150 TGITCacheCommand cmd;
151 cmd.command = TGITCACHECOMMAND_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);
161 else
163 ATLTRACE("SetNamedPipeHandleState failed");
168 bool CShellUpdater::RebuildIcons()
170 const int BUFFER_SIZE = 1024;
171 TCHAR *buf = NULL;
172 HKEY hRegKey = 0;
173 DWORD dwRegValue;
174 DWORD dwRegValueTemp;
175 DWORD dwSize;
176 DWORD_PTR dwResult;
177 LONG lRegResult;
178 std::wstring sRegValueName;
179 std::wstring sDefaultIconSize;
180 int iDefaultIconSize;
181 bool bResult = false;
183 lRegResult = RegOpenKeyEx(HKEY_CURRENT_USER, _T("Control Panel\\Desktop\\WindowMetrics"),
184 0, KEY_READ | KEY_WRITE, &hRegKey);
185 if (lRegResult != ERROR_SUCCESS)
186 goto Cleanup;
188 buf = new TCHAR[BUFFER_SIZE];
189 if(buf == NULL)
190 goto Cleanup;
192 // we're going to change the Shell Icon Size value
193 sRegValueName = _T("Shell Icon Size");
195 // Read registry value
196 dwSize = BUFFER_SIZE;
197 lRegResult = RegQueryValueEx(hRegKey, sRegValueName.c_str(), NULL, NULL,
198 (LPBYTE) buf, &dwSize);
199 if (lRegResult != ERROR_FILE_NOT_FOUND)
201 // If registry key doesn't exist create it using system current setting
202 iDefaultIconSize = ::GetSystemMetrics(SM_CXICON);
203 if (0 == iDefaultIconSize)
204 iDefaultIconSize = 32;
205 _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, _T("%d"), iDefaultIconSize);
207 else if (lRegResult != ERROR_SUCCESS)
208 goto Cleanup;
210 // Change registry value
211 dwRegValue = _ttoi(buf);
212 dwRegValueTemp = dwRegValue-1;
214 dwSize = _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, _T("%d"), dwRegValueTemp) + sizeof(TCHAR);
215 lRegResult = RegSetValueEx(hRegKey, sRegValueName.c_str(), 0, REG_SZ,
216 (LPBYTE) buf, dwSize);
217 if (lRegResult != ERROR_SUCCESS)
218 goto Cleanup;
221 // Update all windows
222 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS,
223 0, SMTO_ABORTIFHUNG, 5000, &dwResult);
225 // Reset registry value
226 dwSize = _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, _T("%d"), dwRegValue) + sizeof(TCHAR);
227 lRegResult = RegSetValueEx(hRegKey, sRegValueName.c_str(), 0, REG_SZ,
228 (LPBYTE) buf, dwSize);
229 if(lRegResult != ERROR_SUCCESS)
230 goto Cleanup;
232 // Update all windows
233 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS,
234 0, SMTO_ABORTIFHUNG, 5000, &dwResult);
236 bResult = true;
238 Cleanup:
239 if (hRegKey != 0)
241 RegCloseKey(hRegKey);
243 if (buf != NULL)
245 delete [] buf;
248 return bResult;