1 // TortoiseGit - 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.
20 #include "Shellupdater.h"
21 #include "../TGitCache/CacheInterface.h"
25 CShellUpdater::CShellUpdater(void)
27 m_hInvalidationEvent
= CreateEvent(NULL
, FALSE
, FALSE
, _T("TortoiseGitCacheInvalidationEvent"));
30 CShellUpdater::~CShellUpdater(void)
34 CloseHandle(m_hInvalidationEvent
);
37 CShellUpdater
& CShellUpdater::Instance()
39 static CShellUpdater instance
;
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
);
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");
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
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.
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
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
);
128 TGITCacheCommand cmd
;
129 cmd
.command
= TGITCACHECOMMAND_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
);
142 hPipe
= INVALID_HANDLE_VALUE
;
146 if (hPipe
!= INVALID_HANDLE_VALUE
)
148 // now tell the cache we don't need it's command thread anymore
150 TGITCacheCommand cmd
;
151 cmd
.command
= TGITCACHECOMMAND_END
;
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
);
160 hPipe
= INVALID_HANDLE_VALUE
;
165 ATLTRACE("SetNamedPipeHandleState failed");
171 bool CShellUpdater::RebuildIcons()
173 const int BUFFER_SIZE
= 1024;
177 DWORD dwRegValueTemp
;
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
)
191 buf
= new TCHAR
[BUFFER_SIZE
];
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
)
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
)
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
)
235 // Update all windows
236 SendMessageTimeout(HWND_BROADCAST
, WM_SETTINGCHANGE
, SPI_SETNONCLIENTMETRICS
,
237 0, SMTO_ABORTIFHUNG
, 5000, &dwResult
);
244 RegCloseKey(hRegKey
);