OSDN Git Service

initial TGitCache support added
[tortoisegit/TortoiseGitJp.git] / src / TGitCache / TSVNCache.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // External Cache Copyright (C) 2005 - 2006 - Will Dean, Stefan Kueng\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 \r
20 #include "stdafx.h"\r
21 #include "shellapi.h"\r
22 #include "TSVNCache.h"\r
23 #include "GitStatusCache.h"\r
24 #include "CacheInterface.h"\r
25 #include "Resource.h"\r
26 #include "registry.h"\r
27 #include "..\crashrpt\CrashReport.h"\r
28 #include "GitAdminDir.h"\r
29 #include "Dbt.h"\r
30 #include <initguid.h>\r
31 #include "ioevent.h"\r
32 #include "..\version.h"\r
33 //#include "svn_dso.h"\r
34 \r
35 #include <ShellAPI.h>\r
36 \r
37 #ifndef GET_X_LPARAM\r
38 #define GET_X_LPARAM(lp)                        ((int)(short)LOWORD(lp))\r
39 #endif\r
40 #ifndef GET_Y_LPARAM\r
41 #define GET_Y_LPARAM(lp)                        ((int)(short)HIWORD(lp))\r
42 #endif\r
43 \r
44 \r
45 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")\r
46 \r
47 CCrashReport crasher("tortoisegit-bug@googlegroups.com", "Crash Report for TGitCache " APP_X64_STRING " : " STRPRODUCTVER, TRUE);// crash\r
48 \r
49 DWORD WINAPI            InstanceThread(LPVOID); \r
50 DWORD WINAPI            PipeThread(LPVOID);\r
51 DWORD WINAPI            CommandWaitThread(LPVOID);\r
52 DWORD WINAPI            CommandThread(LPVOID);\r
53 LRESULT CALLBACK        WndProc(HWND, UINT, WPARAM, LPARAM);\r
54 bool                            bRun = true;\r
55 NOTIFYICONDATA          niData; \r
56 HWND                            hWnd;\r
57 HWND                            hTrayWnd;\r
58 TCHAR                           szCurrentCrawledPath[MAX_CRAWLEDPATHS][MAX_CRAWLEDPATHSLEN];\r
59 int                                     nCurrentCrawledpathIndex = 0;\r
60 CComAutoCriticalSection critSec;\r
61 \r
62 volatile LONG           nThreadCount = 0;\r
63 \r
64 #define PACKVERSION(major,minor) MAKELONG(minor,major)\r
65 DWORD GetDllVersion(LPCTSTR lpszDllName)\r
66 {\r
67         HINSTANCE hinstDll;\r
68         DWORD dwVersion = 0;\r
69 \r
70         /* For security purposes, LoadLibrary should be provided with a \r
71         fully-qualified path to the DLL. The lpszDllName variable should be\r
72         tested to ensure that it is a fully qualified path before it is used. */\r
73         hinstDll = LoadLibrary(lpszDllName);\r
74 \r
75         if(hinstDll)\r
76         {\r
77                 DLLGETVERSIONPROC pDllGetVersion;\r
78                 pDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinstDll, \r
79                         "DllGetVersion");\r
80 \r
81                 /* Because some DLLs might not implement this function, you\r
82                 must test for it explicitly. Depending on the particular \r
83                 DLL, the lack of a DllGetVersion function can be a useful\r
84                 indicator of the version. */\r
85 \r
86                 if(pDllGetVersion)\r
87                 {\r
88                         DLLVERSIONINFO dvi;\r
89                         HRESULT hr;\r
90 \r
91                         SecureZeroMemory(&dvi, sizeof(dvi));\r
92                         dvi.cbSize = sizeof(dvi);\r
93 \r
94                         hr = (*pDllGetVersion)(&dvi);\r
95 \r
96                         if(SUCCEEDED(hr))\r
97                         {\r
98                                 dwVersion = PACKVERSION(dvi.dwMajorVersion, dvi.dwMinorVersion);\r
99                         }\r
100                 }\r
101 \r
102                 FreeLibrary(hinstDll);\r
103         }\r
104         return dwVersion;\r
105 }\r
106 \r
107 void DebugOutputLastError()\r
108 {\r
109         LPVOID lpMsgBuf;\r
110         if (!FormatMessage( \r
111                 FORMAT_MESSAGE_ALLOCATE_BUFFER | \r
112                 FORMAT_MESSAGE_FROM_SYSTEM | \r
113                 FORMAT_MESSAGE_IGNORE_INSERTS,\r
114                 NULL,\r
115                 GetLastError(),\r
116                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
117                 (LPTSTR) &lpMsgBuf,\r
118                 0,\r
119                 NULL ))\r
120         {\r
121                 return;\r
122         }\r
123 \r
124         // Display the string.\r
125         OutputDebugStringA("TGitCache GetLastError(): ");\r
126         OutputDebugString((LPCTSTR)lpMsgBuf);\r
127         OutputDebugStringA("\n");\r
128 \r
129         // Free the buffer.\r
130         LocalFree( lpMsgBuf );\r
131 }\r
132 \r
133 int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*cmdShow*/)\r
134 {\r
135         HANDLE hReloadProtection = ::CreateMutex(NULL, FALSE, GetCacheMutexName());\r
136 \r
137         if (hReloadProtection == 0 || GetLastError() == ERROR_ALREADY_EXISTS)\r
138         {\r
139                 // An instance of TGitCache is already running\r
140                 ATLTRACE("TGitCache ignoring restart\n");\r
141                 return 0;\r
142         }\r
143 \r
144 //      apr_initialize();\r
145 //      svn_dso_initialize2();\r
146         g_GitAdminDir.Init();\r
147         CGitStatusCache::Create();\r
148         CGitStatusCache::Instance().Init();\r
149 \r
150         SecureZeroMemory(szCurrentCrawledPath, sizeof(szCurrentCrawledPath));\r
151         \r
152         DWORD dwThreadId; \r
153         HANDLE hPipeThread; \r
154         HANDLE hCommandWaitThread;\r
155         MSG msg;\r
156         TCHAR szWindowClass[] = {TSVN_CACHE_WINDOW_NAME};\r
157 \r
158         // create a hidden window to receive window messages.\r
159         WNDCLASSEX wcex;\r
160         wcex.cbSize = sizeof(WNDCLASSEX); \r
161         wcex.style                      = CS_HREDRAW | CS_VREDRAW;\r
162         wcex.lpfnWndProc        = (WNDPROC)WndProc;\r
163         wcex.cbClsExtra         = 0;\r
164         wcex.cbWndExtra         = 0;\r
165         wcex.hInstance          = hInstance;\r
166         wcex.hIcon                      = 0;\r
167         wcex.hCursor            = 0;\r
168         wcex.hbrBackground      = 0;\r
169         wcex.lpszMenuName       = NULL;\r
170         wcex.lpszClassName      = szWindowClass;\r
171         wcex.hIconSm            = 0;\r
172         RegisterClassEx(&wcex);\r
173         hWnd = CreateWindow(TSVN_CACHE_WINDOW_NAME, TSVN_CACHE_WINDOW_NAME, WS_CAPTION, 0, 0, 800, 300, NULL, 0, hInstance, 0);\r
174         hTrayWnd = hWnd;\r
175         if (hWnd == NULL)\r
176         {\r
177                 return 0;\r
178         }\r
179         if (CRegStdWORD(_T("Software\\TortoiseGit\\CacheTrayIcon"), FALSE)==TRUE)\r
180         {\r
181                 SecureZeroMemory(&niData,sizeof(NOTIFYICONDATA));\r
182 \r
183                 DWORD dwVersion = GetDllVersion(_T("Shell32.dll"));\r
184 \r
185                 if (dwVersion >= PACKVERSION(6,0))\r
186                         niData.cbSize = sizeof(NOTIFYICONDATA);\r
187                 else if (dwVersion >= PACKVERSION(5,0))\r
188                         niData.cbSize = NOTIFYICONDATA_V2_SIZE;\r
189                 else \r
190                         niData.cbSize = NOTIFYICONDATA_V1_SIZE;\r
191 \r
192                 niData.uID = TRAY_ID;           // own tray icon ID\r
193                 niData.hWnd      = hWnd;\r
194                 niData.uFlags = NIF_ICON|NIF_MESSAGE;\r
195 \r
196                 // load the icon\r
197                 niData.hIcon =\r
198                         (HICON)LoadImage(hInstance,\r
199                         MAKEINTRESOURCE(IDI_TSVNCACHE),\r
200                         IMAGE_ICON,\r
201                         GetSystemMetrics(SM_CXSMICON),\r
202                         GetSystemMetrics(SM_CYSMICON),\r
203                         LR_DEFAULTCOLOR);\r
204 \r
205                 // set the message to send\r
206                 // note: the message value should be in the\r
207                 // range of WM_APP through 0xBFFF\r
208                 niData.uCallbackMessage = TRAY_CALLBACK;\r
209                 Shell_NotifyIcon(NIM_ADD,&niData);\r
210                 // free icon handle\r
211                 if(niData.hIcon && DestroyIcon(niData.hIcon))\r
212                         niData.hIcon = NULL;\r
213         }\r
214         \r
215         // Create a thread which waits for incoming pipe connections \r
216         hPipeThread = CreateThread( \r
217                 NULL,              // no security attribute \r
218                 0,                 // default stack size \r
219                 PipeThread, \r
220                 (LPVOID) &bRun,    // thread parameter \r
221                 0,                 // not suspended \r
222                 &dwThreadId);      // returns thread ID \r
223 \r
224         if (hPipeThread == NULL) \r
225         {\r
226                 //OutputDebugStringA("TSVNCache: Could not create pipe thread\n");\r
227                 //DebugOutputLastError();\r
228                 return 0;\r
229         }\r
230         else CloseHandle(hPipeThread); \r
231 \r
232         // Create a thread which waits for incoming pipe connections \r
233         hCommandWaitThread = CreateThread( \r
234                 NULL,              // no security attribute \r
235                 0,                 // default stack size \r
236                 CommandWaitThread, \r
237                 (LPVOID) &bRun,    // thread parameter \r
238                 0,                 // not suspended \r
239                 &dwThreadId);      // returns thread ID \r
240 \r
241         if (hCommandWaitThread == NULL) \r
242         {\r
243                 //OutputDebugStringA("TSVNCache: Could not create command wait thread\n");\r
244                 //DebugOutputLastError();\r
245                 return 0;\r
246         }\r
247         else CloseHandle(hCommandWaitThread); \r
248 \r
249 \r
250         // loop to handle window messages.\r
251         BOOL bLoopRet;\r
252         while (bRun)\r
253         {\r
254                 bLoopRet = GetMessage(&msg, NULL, 0, 0);\r
255                 if ((bLoopRet != -1)&&(bLoopRet != 0))\r
256                 {\r
257                         DispatchMessage(&msg);\r
258                 }\r
259         }\r
260 \r
261         bRun = false;\r
262 \r
263         Shell_NotifyIcon(NIM_DELETE,&niData);\r
264         CGitStatusCache::Destroy();\r
265         g_GitAdminDir.Close();\r
266 //      apr_terminate();\r
267 \r
268         return 0;\r
269 }\r
270 \r
271 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)\r
272 {\r
273         switch (message) \r
274         {\r
275         case TRAY_CALLBACK:\r
276         {\r
277                 switch(lParam)\r
278                 {\r
279                 case WM_LBUTTONDBLCLK:\r
280                         if (IsWindowVisible(hWnd))\r
281                                 ShowWindow(hWnd, SW_HIDE);\r
282                         else\r
283                                 ShowWindow(hWnd, SW_RESTORE);\r
284                         break;\r
285                 case WM_MOUSEMOVE:\r
286                         {\r
287                                 CString sInfoTip;\r
288                                 NOTIFYICONDATA SystemTray;\r
289                                 sInfoTip.Format(_T("Cached Directories : %ld\nWatched paths : %ld"), \r
290                                         CGitStatusCache::Instance().GetCacheSize(),\r
291                                         CGitStatusCache::Instance().GetNumberOfWatchedPaths());\r
292 \r
293                                 SystemTray.cbSize = sizeof(NOTIFYICONDATA);\r
294                                 SystemTray.hWnd   = hTrayWnd;\r
295                                 SystemTray.uID    = TRAY_ID;\r
296                                 SystemTray.uFlags = NIF_TIP;\r
297                                 _tcscpy_s(SystemTray.szTip, sInfoTip);\r
298                                 Shell_NotifyIcon(NIM_MODIFY, &SystemTray);\r
299                         }\r
300                         break;\r
301                 case WM_RBUTTONUP:\r
302                 case WM_CONTEXTMENU:\r
303                         {\r
304                                 POINT pt;\r
305                                 DWORD ptW = GetMessagePos();\r
306                                 pt.x = GET_X_LPARAM(ptW);\r
307                                 pt.y = GET_Y_LPARAM(ptW);\r
308                                 HMENU hMenu = CreatePopupMenu();\r
309                                 if(hMenu)\r
310                                 {\r
311                                         InsertMenu(hMenu, (UINT)-1, MF_BYPOSITION, TRAYPOP_EXIT, _T("Exit"));\r
312                                         SetForegroundWindow(hWnd);\r
313                                         TrackPopupMenu(hMenu, TPM_BOTTOMALIGN, pt.x, pt.y, 0, hWnd, NULL);\r
314                                         DestroyMenu(hMenu);\r
315                                 }\r
316                         }\r
317                         break;\r
318                 }\r
319         }\r
320         break;\r
321         case WM_PAINT:\r
322                 {\r
323                         PAINTSTRUCT ps;\r
324                         HDC hdc = BeginPaint(hWnd, &ps);\r
325                         RECT rect;\r
326                         GetClientRect(hWnd, &rect);\r
327                         // clear the background\r
328                         HBRUSH background = CreateSolidBrush(::GetSysColor(COLOR_WINDOW));\r
329                         HGDIOBJ oldbrush = SelectObject(hdc, background);\r
330                         FillRect(hdc, &rect, background);\r
331 \r
332                         int line = 0;\r
333                         SIZE fontsize = {0};\r
334                         AutoLocker print(critSec);\r
335                         GetTextExtentPoint32( hdc, szCurrentCrawledPath[0], (int)_tcslen(szCurrentCrawledPath[0]), &fontsize );\r
336                         for (int i=nCurrentCrawledpathIndex; i<MAX_CRAWLEDPATHS; ++i)\r
337                         {\r
338                                 TextOut(hdc, 0, line*fontsize.cy, szCurrentCrawledPath[i], (int)_tcslen(szCurrentCrawledPath[i]));\r
339                                 line++;\r
340                         }\r
341                         for (int i=0; i<nCurrentCrawledpathIndex; ++i)\r
342                         {\r
343                                 TextOut(hdc, 0, line*fontsize.cy, szCurrentCrawledPath[i], (int)_tcslen(szCurrentCrawledPath[i]));\r
344                                 line++;\r
345                         }\r
346                         \r
347                         \r
348                         SelectObject(hdc,oldbrush);\r
349                         EndPaint(hWnd, &ps); \r
350                         DeleteObject(background);\r
351                         return 0L; \r
352                 }\r
353                 break;\r
354         case WM_COMMAND:\r
355                 {\r
356                         WORD wmId    = LOWORD(wParam);\r
357 \r
358                         switch (wmId)\r
359                         {\r
360                         case TRAYPOP_EXIT:\r
361                                 DestroyWindow(hWnd);\r
362                                 break;\r
363                         }\r
364                         return 1;\r
365                 }\r
366         case WM_QUERYENDSESSION:\r
367                 {\r
368                         ATLTRACE("WM_QUERYENDSESSION\n");\r
369                         if (CGitStatusCache::Instance().WaitToWrite(200))\r
370                         {\r
371                                 CGitStatusCache::Instance().Stop();\r
372                                 CGitStatusCache::Instance().Done();\r
373                         }\r
374                         return TRUE;\r
375                 }\r
376                 break;\r
377         case WM_CLOSE:\r
378         case WM_ENDSESSION:\r
379         case WM_DESTROY:\r
380         case WM_QUIT:\r
381                 {\r
382                         ATLTRACE("WM_CLOSE/DESTROY/ENDSESSION/QUIT\n");\r
383                         CGitStatusCache::Instance().WaitToWrite();\r
384                         CGitStatusCache::Instance().Stop();\r
385                         CGitStatusCache::Instance().SaveCache();\r
386                         if (message != WM_QUIT)\r
387                                 PostQuitMessage(0);\r
388                         bRun = false;\r
389                         return 1;\r
390                 }\r
391                 break;\r
392         case WM_DEVICECHANGE:\r
393                 {\r
394                         DEV_BROADCAST_HDR * phdr = (DEV_BROADCAST_HDR*)lParam;\r
395                         switch (wParam)\r
396                         {\r
397                         case DBT_CUSTOMEVENT:\r
398                                 {\r
399                                         ATLTRACE("WM_DEVICECHANGE with DBT_CUSTOMEVENT\n");\r
400                                         if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)\r
401                                         {\r
402                                                 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;\r
403                                                 if (IsEqualGUID(phandle->dbch_eventguid, GUID_IO_VOLUME_DISMOUNT))\r
404                                                 {\r
405                                                         ATLTRACE("Device to be dismounted\n");\r
406                                                         CGitStatusCache::Instance().WaitToWrite();\r
407                                                         CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);\r
408                                                         CGitStatusCache::Instance().Done();\r
409                                                 }\r
410                                                 if (IsEqualGUID(phandle->dbch_eventguid, GUID_IO_VOLUME_LOCK))\r
411                                                 {\r
412                                                         ATLTRACE("Device lock event\n");\r
413                                                         CGitStatusCache::Instance().WaitToWrite();\r
414                                                         CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);\r
415                                                         CGitStatusCache::Instance().Done();\r
416                                                 }\r
417                                         }\r
418                                 }\r
419                                 break;\r
420                         case DBT_DEVICEREMOVEPENDING:\r
421                                 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVEPENDING\n");\r
422                                 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)\r
423                                 {\r
424                                         DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;\r
425                                         CGitStatusCache::Instance().WaitToWrite();\r
426                                         CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);\r
427                                         CGitStatusCache::Instance().Done();\r
428                                 }\r
429                                 else\r
430                                 {\r
431                                         CGitStatusCache::Instance().WaitToWrite();\r
432                                         CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);\r
433                                         CGitStatusCache::Instance().Done();\r
434                                 }\r
435                                 break;\r
436                         case DBT_DEVICEQUERYREMOVE:\r
437                                 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEQUERYREMOVE\n");\r
438                                 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)\r
439                                 {\r
440                                         DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;\r
441                                         CGitStatusCache::Instance().WaitToWrite();\r
442                                         CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);\r
443                                         CGitStatusCache::Instance().Done();\r
444                                 }\r
445                                 else\r
446                                 {\r
447                                         CGitStatusCache::Instance().WaitToWrite();\r
448                                         CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);\r
449                                         CGitStatusCache::Instance().Done();\r
450                                 }\r
451                                 break;\r
452                         case DBT_DEVICEREMOVECOMPLETE:\r
453                                 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVECOMPLETE\n");\r
454                                 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)\r
455                                 {\r
456                                         DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;\r
457                                         CGitStatusCache::Instance().WaitToWrite();\r
458                                         CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);\r
459                                         CGitStatusCache::Instance().Done();\r
460                                 }\r
461                                 else\r
462                                 {\r
463                                         CGitStatusCache::Instance().WaitToWrite();\r
464                                         CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);\r
465                                         CGitStatusCache::Instance().Done();\r
466                                 }\r
467                                 break;\r
468                         }\r
469                 }\r
470                 break;\r
471         default:\r
472                 break;\r
473         }\r
474         return DefWindowProc(hWnd, message, wParam, lParam);\r
475 }\r
476 \r
477 //////////////////////////////////////////////////////////////////////////\r
478 \r
479 VOID GetAnswerToRequest(const TSVNCacheRequest* pRequest, TSVNCacheResponse* pReply, DWORD* pResponseLength)\r
480 {\r
481         CTGitPath path;\r
482         *pResponseLength = 0;\r
483         if(pRequest->flags & TSVNCACHE_FLAGS_FOLDERISKNOWN)\r
484         {\r
485                 path.SetFromWin(pRequest->path, !!(pRequest->flags & TSVNCACHE_FLAGS_ISFOLDER));\r
486         }\r
487         else\r
488         {\r
489                 path.SetFromWin(pRequest->path);\r
490         }\r
491 \r
492         if (CGitStatusCache::Instance().WaitToRead(2000))\r
493         {\r
494                 CGitStatusCache::Instance().GetStatusForPath(path, pRequest->flags, false).BuildCacheResponse(*pReply, *pResponseLength);\r
495                 CGitStatusCache::Instance().Done();\r
496         }\r
497         else\r
498         {\r
499                 CStatusCacheEntry entry;\r
500                 entry.BuildCacheResponse(*pReply, *pResponseLength);\r
501         }\r
502 }\r
503 \r
504 DWORD WINAPI PipeThread(LPVOID lpvParam)\r
505 {\r
506         ATLTRACE("PipeThread started\n");\r
507         bool * bRun = (bool *)lpvParam;\r
508         // The main loop creates an instance of the named pipe and \r
509         // then waits for a client to connect to it. When the client \r
510         // connects, a thread is created to handle communications \r
511         // with that client, and the loop is repeated. \r
512         DWORD dwThreadId; \r
513         BOOL fConnected;\r
514         HANDLE hPipe = INVALID_HANDLE_VALUE;\r
515         HANDLE hInstanceThread = INVALID_HANDLE_VALUE;\r
516 \r
517         while (*bRun) \r
518         { \r
519                 hPipe = CreateNamedPipe( \r
520                         GetCachePipeName(),\r
521                         PIPE_ACCESS_DUPLEX,       // read/write access \r
522                         PIPE_TYPE_MESSAGE |       // message type pipe \r
523                         PIPE_READMODE_MESSAGE |   // message-read mode \r
524                         PIPE_WAIT,                // blocking mode \r
525                         PIPE_UNLIMITED_INSTANCES, // max. instances  \r
526                         BUFSIZE,                  // output buffer size \r
527                         BUFSIZE,                  // input buffer size \r
528                         NMPWAIT_USE_DEFAULT_WAIT, // client time-out \r
529                         NULL);                                    // NULL DACL\r
530 \r
531                 if (hPipe == INVALID_HANDLE_VALUE) \r
532                 {\r
533                         //OutputDebugStringA("TSVNCache: CreatePipe failed\n");\r
534                         //DebugOutputLastError();\r
535                         if (*bRun)\r
536                                 Sleep(200);\r
537                         continue; // never leave the thread!\r
538                 }\r
539 \r
540                 // Wait for the client to connect; if it succeeds, \r
541                 // the function returns a nonzero value. If the function returns \r
542                 // zero, GetLastError returns ERROR_PIPE_CONNECTED. \r
543                 fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); \r
544                 if (fConnected) \r
545                 { \r
546                         // Create a thread for this client. \r
547                         hInstanceThread = CreateThread( \r
548                                 NULL,              // no security attribute \r
549                                 0,                 // default stack size \r
550                                 InstanceThread, \r
551                                 (LPVOID) hPipe,    // thread parameter \r
552                                 0,                 // not suspended \r
553                                 &dwThreadId);      // returns thread ID \r
554 \r
555                         if (hInstanceThread == NULL) \r
556                         {\r
557                                 //OutputDebugStringA("TSVNCache: Could not create Instance thread\n");\r
558                                 //DebugOutputLastError();\r
559                                 DisconnectNamedPipe(hPipe);\r
560                                 CloseHandle(hPipe);\r
561                                 // since we're now closing this thread, we also have to close the whole application!\r
562                                 // otherwise the thread is dead, but the app is still running, refusing new instances\r
563                                 // but no pipe will be available anymore.\r
564                                 PostMessage(hWnd, WM_CLOSE, 0, 0);\r
565                                 return 1;\r
566                         }\r
567                         else CloseHandle(hInstanceThread); \r
568                 } \r
569                 else\r
570                 {\r
571                         // The client could not connect, so close the pipe. \r
572                         //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");\r
573                         //DebugOutputLastError();\r
574                         CloseHandle(hPipe); \r
575                         if (*bRun)\r
576                                 Sleep(200);\r
577                         continue;       // don't end the thread!\r
578                 }\r
579         }\r
580         ATLTRACE("Pipe thread exited\n");\r
581         return 0;\r
582 }\r
583 \r
584 DWORD WINAPI CommandWaitThread(LPVOID lpvParam)\r
585 {\r
586         ATLTRACE("CommandWaitThread started\n");\r
587         bool * bRun = (bool *)lpvParam;\r
588         // The main loop creates an instance of the named pipe and \r
589         // then waits for a client to connect to it. When the client \r
590         // connects, a thread is created to handle communications \r
591         // with that client, and the loop is repeated. \r
592         DWORD dwThreadId; \r
593         BOOL fConnected;\r
594         HANDLE hPipe = INVALID_HANDLE_VALUE;\r
595         HANDLE hCommandThread = INVALID_HANDLE_VALUE;\r
596 \r
597         while (*bRun) \r
598         { \r
599                 hPipe = CreateNamedPipe( \r
600                         GetCacheCommandPipeName(),\r
601                         PIPE_ACCESS_DUPLEX,       // read/write access \r
602                         PIPE_TYPE_MESSAGE |       // message type pipe \r
603                         PIPE_READMODE_MESSAGE |   // message-read mode \r
604                         PIPE_WAIT,                // blocking mode \r
605                         PIPE_UNLIMITED_INSTANCES, // max. instances  \r
606                         BUFSIZE,                  // output buffer size \r
607                         BUFSIZE,                  // input buffer size \r
608                         NMPWAIT_USE_DEFAULT_WAIT, // client time-out \r
609                         NULL);                // NULL DACL\r
610 \r
611                 if (hPipe == INVALID_HANDLE_VALUE) \r
612                 {\r
613                         //OutputDebugStringA("TSVNCache: CreatePipe failed\n");\r
614                         //DebugOutputLastError();\r
615                         if (*bRun)\r
616                                 Sleep(200);\r
617                         continue; // never leave the thread!\r
618                 }\r
619 \r
620                 // Wait for the client to connect; if it succeeds, \r
621                 // the function returns a nonzero value. If the function returns \r
622                 // zero, GetLastError returns ERROR_PIPE_CONNECTED. \r
623                 fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); \r
624                 if (fConnected) \r
625                 { \r
626                         // Create a thread for this client. \r
627                         hCommandThread = CreateThread( \r
628                                 NULL,              // no security attribute \r
629                                 0,                 // default stack size \r
630                                 CommandThread, \r
631                                 (LPVOID) hPipe,    // thread parameter \r
632                                 0,                 // not suspended \r
633                                 &dwThreadId);      // returns thread ID \r
634 \r
635                         if (hCommandThread == NULL) \r
636                         {\r
637                                 //OutputDebugStringA("TSVNCache: Could not create Command thread\n");\r
638                                 //DebugOutputLastError();\r
639                                 DisconnectNamedPipe(hPipe);\r
640                                 CloseHandle(hPipe);\r
641                                 // since we're now closing this thread, we also have to close the whole application!\r
642                                 // otherwise the thread is dead, but the app is still running, refusing new instances\r
643                                 // but no pipe will be available anymore.\r
644                                 PostMessage(hWnd, WM_CLOSE, 0, 0);\r
645                                 return 1;\r
646                         }\r
647                         else CloseHandle(hCommandThread); \r
648                 } \r
649                 else\r
650                 {\r
651                         // The client could not connect, so close the pipe. \r
652                         //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");\r
653                         //DebugOutputLastError();\r
654                         CloseHandle(hPipe); \r
655                         if (*bRun)\r
656                                 Sleep(200);\r
657                         continue;       // don't end the thread!\r
658                 }\r
659         }\r
660         ATLTRACE("CommandWait thread exited\n");\r
661         return 0;\r
662 }\r
663 \r
664 DWORD WINAPI InstanceThread(LPVOID lpvParam) \r
665\r
666         ATLTRACE("InstanceThread started\n");\r
667         TSVNCacheResponse response; \r
668         DWORD cbBytesRead, cbWritten; \r
669         BOOL fSuccess; \r
670         HANDLE hPipe; \r
671 \r
672         // The thread's parameter is a handle to a pipe instance. \r
673 \r
674         hPipe = (HANDLE) lpvParam; \r
675         InterlockedIncrement(&nThreadCount);\r
676         while (bRun) \r
677         { \r
678                 // Read client requests from the pipe. \r
679                 TSVNCacheRequest request;\r
680                 fSuccess = ReadFile( \r
681                         hPipe,        // handle to pipe \r
682                         &request,    // buffer to receive data \r
683                         sizeof(request), // size of buffer \r
684                         &cbBytesRead, // number of bytes read \r
685                         NULL);        // not overlapped I/O \r
686 \r
687                 if (! fSuccess || cbBytesRead == 0)\r
688                 {\r
689                         DisconnectNamedPipe(hPipe); \r
690                         CloseHandle(hPipe); \r
691                         ATLTRACE("Instance thread exited\n");\r
692                         InterlockedDecrement(&nThreadCount);\r
693                         if (nThreadCount == 0)\r
694                                 PostMessage(hWnd, WM_CLOSE, 0, 0);\r
695                         return 1;\r
696                 }\r
697 \r
698                 DWORD responseLength;\r
699                 GetAnswerToRequest(&request, &response, &responseLength); \r
700 \r
701                 // Write the reply to the pipe. \r
702                 fSuccess = WriteFile( \r
703                         hPipe,        // handle to pipe \r
704                         &response,      // buffer to write from \r
705                         responseLength, // number of bytes to write \r
706                         &cbWritten,   // number of bytes written \r
707                         NULL);        // not overlapped I/O \r
708 \r
709                 if (! fSuccess || responseLength != cbWritten)\r
710                 {\r
711                         DisconnectNamedPipe(hPipe); \r
712                         CloseHandle(hPipe); \r
713                         ATLTRACE("Instance thread exited\n");\r
714                         InterlockedDecrement(&nThreadCount);\r
715                         if (nThreadCount == 0)\r
716                                 PostMessage(hWnd, WM_CLOSE, 0, 0);\r
717                         return 1;\r
718                 }\r
719         } \r
720 \r
721         // Flush the pipe to allow the client to read the pipe's contents \r
722         // before disconnecting. Then disconnect the pipe, and close the \r
723         // handle to this pipe instance. \r
724 \r
725         FlushFileBuffers(hPipe); \r
726         DisconnectNamedPipe(hPipe); \r
727         CloseHandle(hPipe); \r
728         ATLTRACE("Instance thread exited\n");\r
729         InterlockedDecrement(&nThreadCount);\r
730         if (nThreadCount == 0)\r
731                 PostMessage(hWnd, WM_CLOSE, 0, 0);\r
732         return 0;\r
733 }\r
734 \r
735 DWORD WINAPI CommandThread(LPVOID lpvParam) \r
736\r
737         ATLTRACE("CommandThread started\n");\r
738         DWORD cbBytesRead; \r
739         BOOL fSuccess; \r
740         HANDLE hPipe; \r
741 \r
742         // The thread's parameter is a handle to a pipe instance. \r
743 \r
744         hPipe = (HANDLE) lpvParam; \r
745 \r
746         while (bRun) \r
747         { \r
748                 // Read client requests from the pipe. \r
749                 TSVNCacheCommand command;\r
750                 fSuccess = ReadFile( \r
751                         hPipe,                          // handle to pipe \r
752                         &command,                       // buffer to receive data \r
753                         sizeof(command),        // size of buffer \r
754                         &cbBytesRead,           // number of bytes read \r
755                         NULL);                          // not overlapped I/O \r
756 \r
757                 if (! fSuccess || cbBytesRead == 0)\r
758                 {\r
759                         DisconnectNamedPipe(hPipe); \r
760                         CloseHandle(hPipe); \r
761                         ATLTRACE("Command thread exited\n");\r
762                         return 1;\r
763                 }\r
764                 \r
765                 switch (command.command)\r
766                 {\r
767                         case TSVNCACHECOMMAND_END:\r
768                                 FlushFileBuffers(hPipe); \r
769                                 DisconnectNamedPipe(hPipe); \r
770                                 CloseHandle(hPipe); \r
771                                 ATLTRACE("Command thread exited\n");\r
772                                 return 0;\r
773                         case TSVNCACHECOMMAND_CRAWL:\r
774                                 {\r
775                                         CTGitPath changedpath;\r
776                                         changedpath.SetFromWin(CString(command.path), true);\r
777                                         // remove the path from our cache - that will 'invalidate' it.\r
778                                         CGitStatusCache::Instance().WaitToWrite();\r
779                                         CGitStatusCache::Instance().RemoveCacheForPath(changedpath);\r
780                                         CGitStatusCache::Instance().Done();\r
781                                         CGitStatusCache::Instance().AddFolderForCrawling(changedpath.GetDirectory());\r
782                                 }\r
783                                 break;\r
784                         case TSVNCACHECOMMAND_REFRESHALL:\r
785                                 CGitStatusCache::Instance().WaitToWrite();\r
786                                 CGitStatusCache::Instance().Refresh();\r
787                                 CGitStatusCache::Instance().Done();\r
788                                 break;\r
789                         case TSVNCACHECOMMAND_RELEASE:\r
790                                 {\r
791                                         CTGitPath changedpath;\r
792                                         changedpath.SetFromWin(CString(command.path), true);\r
793                                         ATLTRACE(_T("release handle for path %s\n"), changedpath.GetWinPath());\r
794                                         CGitStatusCache::Instance().WaitToWrite();\r
795                                         CGitStatusCache::Instance().CloseWatcherHandles(changedpath);\r
796                                         CGitStatusCache::Instance().RemoveCacheForPath(changedpath);\r
797                                         CGitStatusCache::Instance().Done();\r
798                                 }\r
799                                 break;\r
800 \r
801                 }\r
802         } \r
803 \r
804         // Flush the pipe to allow the client to read the pipe's contents \r
805         // before disconnecting. Then disconnect the pipe, and close the \r
806         // handle to this pipe instance. \r
807 \r
808         FlushFileBuffers(hPipe); \r
809         DisconnectNamedPipe(hPipe); \r
810         CloseHandle(hPipe); \r
811         ATLTRACE("Command thread exited\n");\r
812         return 0;\r
813 }\r