OSDN Git Service

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