1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2013, 2015-2016 - TortoiseGit
4 // Copyright (C) 2003-2008,2011,2014 - 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(nullptr, FALSE
, FALSE
, L
"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
.IsEmpty())
75 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Flushing shell update list\n");
78 m_pathsForUpdating
.Clear();
81 void CShellUpdater::UpdateShell()
83 // Tell the shell extension to purge its cache
84 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Setting cache invalidation event %I64u\n", GetTickCount64());
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 nullptr, // default security attributes
102 OPEN_EXISTING
, // opens existing pipe
103 FILE_FLAG_OVERLAPPED
, // default attributes
104 nullptr); // no template file
110 // The pipe connected; change to message-read mode.
111 DWORD dwMode
= PIPE_READMODE_MESSAGE
;
112 if (!SetNamedPipeHandleState(
113 hPipe
, // pipe handle
114 &dwMode
, // new pipe mode
115 nullptr, // don't set maximum bytes
116 nullptr)) // don't set maximum time
118 CTraceToOutputDebugString::Instance()(__FUNCTION__
": SetNamedPipeHandleState failed");
123 for (int nPath
= 0; nPath
< m_pathsForUpdating
.GetCount(); ++nPath
)
125 path
.SetFromWin(g_Git
.CombinePath(m_pathsForUpdating
[nPath
]));
126 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": Cache Item Update for %s (%I64u)\n", (LPCTSTR
)path
.GetWinPathString(), GetTickCount64());
127 if (!path
.IsDirectory())
129 // send notifications to the shell for changed files - folders are updated by the cache itself.
130 SHChangeNotify(SHCNE_UPDATEITEM
, SHCNF_PATH
| SHCNF_FLUSHNOWAIT
, path
.GetWinPath(), nullptr);
133 TGITCacheCommand cmd
;
134 cmd
.command
= TGITCACHECOMMAND_CRAWL
;
135 wcsncpy_s(cmd
.path
, path
.GetDirectory().GetWinPath(), _countof(cmd
.path
) - 1);
136 BOOL fSuccess
= WriteFile(
137 hPipe
, // handle to pipe
138 &cmd
, // buffer to write from
139 sizeof(cmd
), // number of bytes to write
140 &cbWritten
, // number of bytes written
141 nullptr); // not overlapped I/O
143 if (!fSuccess
|| sizeof(cmd
) != cbWritten
)
145 DisconnectNamedPipe(hPipe
);
150 // now tell the cache we don't need it's command thread anymore
152 TGITCacheCommand cmd
;
153 cmd
.command
= TGITCACHECOMMAND_END
;
155 hPipe
, // handle to pipe
156 &cmd
, // buffer to write from
157 sizeof(cmd
), // number of bytes to write
158 &cbWritten
, // number of bytes written
159 nullptr); // not overlapped I/O
160 DisconnectNamedPipe(hPipe
);
163 bool CShellUpdater::RebuildIcons()
165 const int BUFFER_SIZE
= 1024;
166 TCHAR buf
[BUFFER_SIZE
] = { 0 };
167 HKEY hRegKey
= nullptr;
169 DWORD dwRegValueTemp
;
174 lRegResult
= RegOpenKeyEx(HKEY_CURRENT_USER
, L
"Control Panel\\Desktop\\WindowMetrics",
175 0, KEY_READ
| KEY_WRITE
, &hRegKey
);
176 if (lRegResult
!= ERROR_SUCCESS
)
178 SCOPE_EXIT
{ RegCloseKey(hRegKey
); };
180 // we're going to change the Shell Icon Size value
181 const TCHAR
* sRegValueName
= L
"Shell Icon Size";
183 // Read registry value
184 dwSize
= BUFFER_SIZE
;
185 lRegResult
= RegQueryValueEx(hRegKey
, sRegValueName
, nullptr, nullptr,
186 (LPBYTE
) buf
, &dwSize
);
187 if (lRegResult
!= ERROR_FILE_NOT_FOUND
)
189 // If registry key doesn't exist create it using system current setting
190 int iDefaultIconSize
= ::GetSystemMetrics(SM_CXICON
);
191 if (0 == iDefaultIconSize
)
192 iDefaultIconSize
= 32;
193 _sntprintf_s(buf
, BUFFER_SIZE
, BUFFER_SIZE
, L
"%d", iDefaultIconSize
);
195 else if (lRegResult
!= ERROR_SUCCESS
)
198 // Change registry value
199 dwRegValue
= _wtoi(buf
);
200 dwRegValueTemp
= dwRegValue
-1;
202 dwSize
= _sntprintf_s(buf
, BUFFER_SIZE
, BUFFER_SIZE
, L
"%lu", dwRegValueTemp
) + sizeof(TCHAR
);
203 lRegResult
= RegSetValueEx(hRegKey
, sRegValueName
, 0, REG_SZ
,
204 (LPBYTE
) buf
, dwSize
);
205 if (lRegResult
!= ERROR_SUCCESS
)
208 // Update all windows
209 SendMessageTimeout(HWND_BROADCAST
, WM_SETTINGCHANGE
, SPI_SETNONCLIENTMETRICS
,
210 0, SMTO_ABORTIFHUNG
, 5000, &dwResult
);
212 // Reset registry value
213 dwSize
= _sntprintf_s(buf
, BUFFER_SIZE
, BUFFER_SIZE
, L
"%lu", dwRegValue
) + sizeof(TCHAR
);
214 lRegResult
= RegSetValueEx(hRegKey
, sRegValueName
, 0, REG_SZ
,
215 (LPBYTE
) buf
, dwSize
);
216 if (lRegResult
!= ERROR_SUCCESS
)
219 // Update all windows
220 SendMessageTimeout(HWND_BROADCAST
, WM_SETTINGCHANGE
, SPI_SETNONCLIENTMETRICS
,
221 0, SMTO_ABORTIFHUNG
, 5000, &dwResult
);