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.
21 #include "Shellupdater.h"
22 #include "../TGitCache/CacheInterface.h"
25 #include "SmartHandle.h"
27 CShellUpdater::CShellUpdater(void)
29 m_hInvalidationEvent
= CreateEvent(NULL
, FALSE
, FALSE
, _T("TortoiseGitCacheInvalidationEvent"));
32 CShellUpdater::~CShellUpdater(void)
36 CloseHandle(m_hInvalidationEvent
);
39 CShellUpdater
& CShellUpdater::Instance()
41 static CShellUpdater instance
;
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
);
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");
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
101 NULL
, // default security attributes
102 OPEN_EXISTING
, // opens existing pipe
103 FILE_FLAG_OVERLAPPED
, // default attributes
104 NULL
); // no template file
109 // The pipe connected; change to message-read mode.
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
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
);
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
);
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
);
163 ATLTRACE("SetNamedPipeHandleState failed");
168 bool CShellUpdater::RebuildIcons()
170 const int BUFFER_SIZE
= 1024;
174 DWORD dwRegValueTemp
;
178 std::wstring sRegValueName
;
179 int iDefaultIconSize
;
180 bool bResult
= false;
182 lRegResult
= RegOpenKeyEx(HKEY_CURRENT_USER
, _T("Control Panel\\Desktop\\WindowMetrics"),
183 0, KEY_READ
| KEY_WRITE
, &hRegKey
);
184 if (lRegResult
!= ERROR_SUCCESS
)
187 buf
= new TCHAR
[BUFFER_SIZE
];
191 // we're going to change the Shell Icon Size value
192 sRegValueName
= _T("Shell Icon Size");
194 // Read registry value
195 dwSize
= BUFFER_SIZE
;
196 lRegResult
= RegQueryValueEx(hRegKey
, sRegValueName
.c_str(), NULL
, NULL
,
197 (LPBYTE
) buf
, &dwSize
);
198 if (lRegResult
!= ERROR_FILE_NOT_FOUND
)
200 // If registry key doesn't exist create it using system current setting
201 iDefaultIconSize
= ::GetSystemMetrics(SM_CXICON
);
202 if (0 == iDefaultIconSize
)
203 iDefaultIconSize
= 32;
204 _sntprintf_s(buf
, BUFFER_SIZE
, BUFFER_SIZE
, _T("%d"), iDefaultIconSize
);
206 else if (lRegResult
!= ERROR_SUCCESS
)
209 // Change registry value
210 dwRegValue
= _ttoi(buf
);
211 dwRegValueTemp
= dwRegValue
-1;
213 dwSize
= _sntprintf_s(buf
, BUFFER_SIZE
, BUFFER_SIZE
, _T("%d"), dwRegValueTemp
) + sizeof(TCHAR
);
214 lRegResult
= RegSetValueEx(hRegKey
, sRegValueName
.c_str(), 0, REG_SZ
,
215 (LPBYTE
) buf
, dwSize
);
216 if (lRegResult
!= ERROR_SUCCESS
)
220 // Update all windows
221 SendMessageTimeout(HWND_BROADCAST
, WM_SETTINGCHANGE
, SPI_SETNONCLIENTMETRICS
,
222 0, SMTO_ABORTIFHUNG
, 5000, &dwResult
);
224 // Reset registry value
225 dwSize
= _sntprintf_s(buf
, BUFFER_SIZE
, BUFFER_SIZE
, _T("%d"), dwRegValue
) + sizeof(TCHAR
);
226 lRegResult
= RegSetValueEx(hRegKey
, sRegValueName
.c_str(), 0, REG_SZ
,
227 (LPBYTE
) buf
, dwSize
);
228 if(lRegResult
!= ERROR_SUCCESS
)
231 // Update all windows
232 SendMessageTimeout(HWND_BROADCAST
, WM_SETTINGCHANGE
, SPI_SETNONCLIENTMETRICS
,
233 0, SMTO_ABORTIFHUNG
, 5000, &dwResult
);
240 RegCloseKey(hRegKey
);