1 // TortoiseGit - a Windows shell extension for easy version control
3 // External Cache Copyright (C) 2005 - 2006 - Will Dean, Stefan Kueng
4 // Copyright (C) 2008-2011 - TortoiseGit
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.
23 #include "TSVNCache.h"
24 #include "GitStatusCache.h"
25 #include "CacheInterface.h"
28 #include "..\crashrpt\CrashReport.h"
29 #include "GitAdminDir.h"
33 #include "..\version.h"
34 //#include "svn_dso.h"
39 #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
42 #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
46 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
48 CCrashReport
crasher("tortoisegit-bug@googlegroups.com", "Crash Report for TGitCache " APP_X64_STRING
" : " STRPRODUCTVER
, TRUE
);// crash
50 DWORD WINAPI
InstanceThread(LPVOID
);
51 DWORD WINAPI
PipeThread(LPVOID
);
52 DWORD WINAPI
CommandWaitThread(LPVOID
);
53 DWORD WINAPI
CommandThread(LPVOID
);
54 LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
56 NOTIFYICONDATA niData
;
59 TCHAR szCurrentCrawledPath
[MAX_CRAWLEDPATHS
][MAX_CRAWLEDPATHSLEN
];
60 int nCurrentCrawledpathIndex
= 0;
61 CComAutoCriticalSection critSec
;
63 volatile LONG nThreadCount
= 0;
65 #define PACKVERSION(major,minor) MAKELONG(minor,major)
66 DWORD
GetDllVersion(LPCTSTR lpszDllName
)
71 /* For security purposes, LoadLibrary should be provided with a
72 fully-qualified path to the DLL. The lpszDllName variable should be
73 tested to ensure that it is a fully qualified path before it is used. */
74 hinstDll
= LoadLibrary(lpszDllName
);
78 DLLGETVERSIONPROC pDllGetVersion
;
79 pDllGetVersion
= (DLLGETVERSIONPROC
)GetProcAddress(hinstDll
,
82 /* Because some DLLs might not implement this function, you
83 must test for it explicitly. Depending on the particular
84 DLL, the lack of a DllGetVersion function can be a useful
85 indicator of the version. */
92 SecureZeroMemory(&dvi
, sizeof(dvi
));
93 dvi
.cbSize
= sizeof(dvi
);
95 hr
= (*pDllGetVersion
)(&dvi
);
99 dwVersion
= PACKVERSION(dvi
.dwMajorVersion
, dvi
.dwMinorVersion
);
103 FreeLibrary(hinstDll
);
108 void DebugOutputLastError()
112 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
113 FORMAT_MESSAGE_FROM_SYSTEM
|
114 FORMAT_MESSAGE_IGNORE_INSERTS
,
117 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
125 // Display the string.
126 OutputDebugStringA("TGitCache GetLastError(): ");
127 OutputDebugString((LPCTSTR
)lpMsgBuf
);
128 OutputDebugStringA("\n");
131 LocalFree( lpMsgBuf
);
134 int __stdcall
WinMain(HINSTANCE hInstance
, HINSTANCE
/*hPrevInstance*/, LPSTR
/*lpCmdLine*/, int /*cmdShow*/)
136 SetDllDirectory(L
"");
137 HANDLE hReloadProtection
= ::CreateMutex(NULL
, FALSE
, GetCacheMutexName());
139 if (hReloadProtection
== 0 || GetLastError() == ERROR_ALREADY_EXISTS
)
141 // An instance of TGitCache is already running
142 ATLTRACE("TGitCache ignoring restart\n");
147 // svn_dso_initialize2();
148 g_GitAdminDir
.Init();
149 CGitStatusCache::Create();
150 CGitStatusCache::Instance().Init();
152 SecureZeroMemory(szCurrentCrawledPath
, sizeof(szCurrentCrawledPath
));
156 HANDLE hCommandWaitThread
;
158 TCHAR szWindowClass
[] = {TSVN_CACHE_WINDOW_NAME
};
160 // create a hidden window to receive window messages.
162 wcex
.cbSize
= sizeof(WNDCLASSEX
);
163 wcex
.style
= CS_HREDRAW
| CS_VREDRAW
;
164 wcex
.lpfnWndProc
= (WNDPROC
)WndProc
;
167 wcex
.hInstance
= hInstance
;
170 wcex
.hbrBackground
= 0;
171 wcex
.lpszMenuName
= NULL
;
172 wcex
.lpszClassName
= szWindowClass
;
174 RegisterClassEx(&wcex
);
175 hWnd
= CreateWindow(TSVN_CACHE_WINDOW_NAME
, TSVN_CACHE_WINDOW_NAME
, WS_CAPTION
, 0, 0, 800, 300, NULL
, 0, hInstance
, 0);
181 if (CRegStdDWORD(_T("Software\\TortoiseGit\\CacheTrayIcon"), FALSE
)==TRUE
)
183 SecureZeroMemory(&niData
,sizeof(NOTIFYICONDATA
));
185 DWORD dwVersion
= GetDllVersion(_T("Shell32.dll"));
187 if (dwVersion
>= PACKVERSION(6,0))
188 niData
.cbSize
= sizeof(NOTIFYICONDATA
);
189 else if (dwVersion
>= PACKVERSION(5,0))
190 niData
.cbSize
= NOTIFYICONDATA_V2_SIZE
;
192 niData
.cbSize
= NOTIFYICONDATA_V1_SIZE
;
194 niData
.uID
= TRAY_ID
; // own tray icon ID
196 niData
.uFlags
= NIF_ICON
|NIF_MESSAGE
;
200 (HICON
)LoadImage(hInstance
,
201 MAKEINTRESOURCE(IDI_TSVNCACHE
),
203 GetSystemMetrics(SM_CXSMICON
),
204 GetSystemMetrics(SM_CYSMICON
),
207 // set the message to send
208 // note: the message value should be in the
209 // range of WM_APP through 0xBFFF
210 niData
.uCallbackMessage
= TRAY_CALLBACK
;
211 Shell_NotifyIcon(NIM_ADD
,&niData
);
213 if(niData
.hIcon
&& DestroyIcon(niData
.hIcon
))
217 // Create a thread which waits for incoming pipe connections
218 hPipeThread
= CreateThread(
219 NULL
, // no security attribute
220 0, // default stack size
222 (LPVOID
) &bRun
, // thread parameter
224 &dwThreadId
); // returns thread ID
226 if (hPipeThread
== NULL
)
228 //OutputDebugStringA("TSVNCache: Could not create pipe thread\n");
229 //DebugOutputLastError();
232 else CloseHandle(hPipeThread
);
234 // Create a thread which waits for incoming pipe connections
235 hCommandWaitThread
= CreateThread(
236 NULL
, // no security attribute
237 0, // default stack size
239 (LPVOID
) &bRun
, // thread parameter
241 &dwThreadId
); // returns thread ID
243 if (hCommandWaitThread
== NULL
)
245 //OutputDebugStringA("TSVNCache: Could not create command wait thread\n");
246 //DebugOutputLastError();
249 else CloseHandle(hCommandWaitThread
);
252 // loop to handle window messages.
256 bLoopRet
= GetMessage(&msg
, NULL
, 0, 0);
257 if ((bLoopRet
!= -1)&&(bLoopRet
!= 0))
259 DispatchMessage(&msg
);
265 Shell_NotifyIcon(NIM_DELETE
,&niData
);
266 CGitStatusCache::Destroy();
267 g_GitAdminDir
.Close();
273 LRESULT CALLBACK
WndProc(HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
281 case WM_LBUTTONDBLCLK
:
282 if (IsWindowVisible(hWnd
))
283 ShowWindow(hWnd
, SW_HIDE
);
285 ShowWindow(hWnd
, SW_RESTORE
);
290 NOTIFYICONDATA SystemTray
;
291 sInfoTip
.Format(_T("Cached Directories : %ld\nWatched paths : %ld"),
292 CGitStatusCache::Instance().GetCacheSize(),
293 CGitStatusCache::Instance().GetNumberOfWatchedPaths());
295 SystemTray
.cbSize
= sizeof(NOTIFYICONDATA
);
296 SystemTray
.hWnd
= hTrayWnd
;
297 SystemTray
.uID
= TRAY_ID
;
298 SystemTray
.uFlags
= NIF_TIP
;
299 _tcscpy_s(SystemTray
.szTip
, sInfoTip
);
300 Shell_NotifyIcon(NIM_MODIFY
, &SystemTray
);
307 DWORD ptW
= GetMessagePos();
308 pt
.x
= GET_X_LPARAM(ptW
);
309 pt
.y
= GET_Y_LPARAM(ptW
);
310 HMENU hMenu
= CreatePopupMenu();
313 InsertMenu(hMenu
, (UINT
)-1, MF_BYPOSITION
, TRAYPOP_EXIT
, _T("Exit"));
314 SetForegroundWindow(hWnd
);
315 TrackPopupMenu(hMenu
, TPM_BOTTOMALIGN
, pt
.x
, pt
.y
, 0, hWnd
, NULL
);
326 HDC hdc
= BeginPaint(hWnd
, &ps
);
328 GetClientRect(hWnd
, &rect
);
329 // clear the background
330 HBRUSH background
= CreateSolidBrush(::GetSysColor(COLOR_WINDOW
));
331 HGDIOBJ oldbrush
= SelectObject(hdc
, background
);
332 FillRect(hdc
, &rect
, background
);
336 AutoLocker
print(critSec
);
337 GetTextExtentPoint32( hdc
, szCurrentCrawledPath
[0], (int)_tcslen(szCurrentCrawledPath
[0]), &fontsize
);
338 for (int i
=nCurrentCrawledpathIndex
; i
<MAX_CRAWLEDPATHS
; ++i
)
340 TextOut(hdc
, 0, line
*fontsize
.cy
, szCurrentCrawledPath
[i
], (int)_tcslen(szCurrentCrawledPath
[i
]));
343 for (int i
=0; i
<nCurrentCrawledpathIndex
; ++i
)
345 TextOut(hdc
, 0, line
*fontsize
.cy
, szCurrentCrawledPath
[i
], (int)_tcslen(szCurrentCrawledPath
[i
]));
350 SelectObject(hdc
,oldbrush
);
352 DeleteObject(background
);
358 WORD wmId
= LOWORD(wParam
);
368 case WM_QUERYENDSESSION
:
370 ATLTRACE("WM_QUERYENDSESSION\n");
371 if (CGitStatusCache::Instance().WaitToWrite(200))
373 CGitStatusCache::Instance().Stop();
374 CGitStatusCache::Instance().Done();
384 ATLTRACE("WM_CLOSE/DESTROY/ENDSESSION/QUIT\n");
385 CGitStatusCache::Instance().WaitToWrite();
386 CGitStatusCache::Instance().Stop();
387 CGitStatusCache::Instance().SaveCache();
388 if (message
!= WM_QUIT
)
394 case WM_DEVICECHANGE
:
396 DEV_BROADCAST_HDR
* phdr
= (DEV_BROADCAST_HDR
*)lParam
;
399 case DBT_CUSTOMEVENT
:
401 ATLTRACE("WM_DEVICECHANGE with DBT_CUSTOMEVENT\n");
402 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
404 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
405 if (IsEqualGUID(phandle
->dbch_eventguid
, GUID_IO_VOLUME_DISMOUNT
))
407 ATLTRACE("Device to be dismounted\n");
408 CGitStatusCache::Instance().WaitToWrite();
409 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
410 CGitStatusCache::Instance().Done();
412 if (IsEqualGUID(phandle
->dbch_eventguid
, GUID_IO_VOLUME_LOCK
))
414 ATLTRACE("Device lock event\n");
415 CGitStatusCache::Instance().WaitToWrite();
416 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
417 CGitStatusCache::Instance().Done();
422 case DBT_DEVICEREMOVEPENDING
:
423 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVEPENDING\n");
424 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
426 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
427 CGitStatusCache::Instance().WaitToWrite();
428 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
429 CGitStatusCache::Instance().Done();
433 CGitStatusCache::Instance().WaitToWrite();
434 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
435 CGitStatusCache::Instance().Done();
438 case DBT_DEVICEQUERYREMOVE
:
439 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEQUERYREMOVE\n");
440 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
442 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
443 CGitStatusCache::Instance().WaitToWrite();
444 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
445 CGitStatusCache::Instance().Done();
449 CGitStatusCache::Instance().WaitToWrite();
450 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
451 CGitStatusCache::Instance().Done();
454 case DBT_DEVICEREMOVECOMPLETE
:
455 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVECOMPLETE\n");
456 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
458 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
459 CGitStatusCache::Instance().WaitToWrite();
460 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
461 CGitStatusCache::Instance().Done();
465 CGitStatusCache::Instance().WaitToWrite();
466 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
467 CGitStatusCache::Instance().Done();
476 return DefWindowProc(hWnd
, message
, wParam
, lParam
);
479 //////////////////////////////////////////////////////////////////////////
481 VOID
GetAnswerToRequest(const TSVNCacheRequest
* pRequest
, TSVNCacheResponse
* pReply
, DWORD
* pResponseLength
)
484 *pResponseLength
= 0;
485 if(pRequest
->flags
& TSVNCACHE_FLAGS_FOLDERISKNOWN
)
487 path
.SetFromWin(pRequest
->path
, !!(pRequest
->flags
& TSVNCACHE_FLAGS_ISFOLDER
));
491 path
.SetFromWin(pRequest
->path
);
494 if (CGitStatusCache::Instance().WaitToRead(2000))
496 CGitStatusCache::Instance().GetStatusForPath(path
, pRequest
->flags
, false).BuildCacheResponse(*pReply
, *pResponseLength
);
497 CGitStatusCache::Instance().Done();
501 CStatusCacheEntry entry
;
502 entry
.BuildCacheResponse(*pReply
, *pResponseLength
);
506 DWORD WINAPI
PipeThread(LPVOID lpvParam
)
508 ATLTRACE("PipeThread started\n");
509 bool * bRun
= (bool *)lpvParam
;
510 // The main loop creates an instance of the named pipe and
511 // then waits for a client to connect to it. When the client
512 // connects, a thread is created to handle communications
513 // with that client, and the loop is repeated.
516 HANDLE hPipe
= INVALID_HANDLE_VALUE
;
517 HANDLE hInstanceThread
= INVALID_HANDLE_VALUE
;
521 hPipe
= CreateNamedPipe(
523 PIPE_ACCESS_DUPLEX
, // read/write access
524 PIPE_TYPE_MESSAGE
| // message type pipe
525 PIPE_READMODE_MESSAGE
| // message-read mode
526 PIPE_WAIT
, // blocking mode
527 PIPE_UNLIMITED_INSTANCES
, // max. instances
528 BUFSIZE
, // output buffer size
529 BUFSIZE
, // input buffer size
530 NMPWAIT_USE_DEFAULT_WAIT
, // client time-out
533 if (hPipe
== INVALID_HANDLE_VALUE
)
535 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
536 //DebugOutputLastError();
539 continue; // never leave the thread!
542 // Wait for the client to connect; if it succeeds,
543 // the function returns a nonzero value. If the function returns
544 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
545 fConnected
= ConnectNamedPipe(hPipe
, NULL
) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED
);
548 // Create a thread for this client.
549 hInstanceThread
= CreateThread(
550 NULL
, // no security attribute
551 0, // default stack size
553 (LPVOID
) hPipe
, // thread parameter
555 &dwThreadId
); // returns thread ID
557 if (hInstanceThread
== NULL
)
559 //OutputDebugStringA("TSVNCache: Could not create Instance thread\n");
560 //DebugOutputLastError();
561 DisconnectNamedPipe(hPipe
);
563 // since we're now closing this thread, we also have to close the whole application!
564 // otherwise the thread is dead, but the app is still running, refusing new instances
565 // but no pipe will be available anymore.
566 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
569 else CloseHandle(hInstanceThread
);
573 // The client could not connect, so close the pipe.
574 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
575 //DebugOutputLastError();
579 continue; // don't end the thread!
582 ATLTRACE("Pipe thread exited\n");
586 DWORD WINAPI
CommandWaitThread(LPVOID lpvParam
)
588 ATLTRACE("CommandWaitThread started\n");
589 bool * bRun
= (bool *)lpvParam
;
590 // The main loop creates an instance of the named pipe and
591 // then waits for a client to connect to it. When the client
592 // connects, a thread is created to handle communications
593 // with that client, and the loop is repeated.
596 HANDLE hPipe
= INVALID_HANDLE_VALUE
;
597 HANDLE hCommandThread
= INVALID_HANDLE_VALUE
;
601 hPipe
= CreateNamedPipe(
602 GetCacheCommandPipeName(),
603 PIPE_ACCESS_DUPLEX
, // read/write access
604 PIPE_TYPE_MESSAGE
| // message type pipe
605 PIPE_READMODE_MESSAGE
| // message-read mode
606 PIPE_WAIT
, // blocking mode
607 PIPE_UNLIMITED_INSTANCES
, // max. instances
608 BUFSIZE
, // output buffer size
609 BUFSIZE
, // input buffer size
610 NMPWAIT_USE_DEFAULT_WAIT
, // client time-out
613 if (hPipe
== INVALID_HANDLE_VALUE
)
615 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
616 //DebugOutputLastError();
619 continue; // never leave the thread!
622 // Wait for the client to connect; if it succeeds,
623 // the function returns a nonzero value. If the function returns
624 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
625 fConnected
= ConnectNamedPipe(hPipe
, NULL
) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED
);
628 // Create a thread for this client.
629 hCommandThread
= CreateThread(
630 NULL
, // no security attribute
631 0, // default stack size
633 (LPVOID
) hPipe
, // thread parameter
635 &dwThreadId
); // returns thread ID
637 if (hCommandThread
== NULL
)
639 //OutputDebugStringA("TSVNCache: Could not create Command thread\n");
640 //DebugOutputLastError();
641 DisconnectNamedPipe(hPipe
);
643 // since we're now closing this thread, we also have to close the whole application!
644 // otherwise the thread is dead, but the app is still running, refusing new instances
645 // but no pipe will be available anymore.
646 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
649 else CloseHandle(hCommandThread
);
653 // The client could not connect, so close the pipe.
654 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
655 //DebugOutputLastError();
659 continue; // don't end the thread!
662 ATLTRACE("CommandWait thread exited\n");
666 DWORD WINAPI
InstanceThread(LPVOID lpvParam
)
668 ATLTRACE("InstanceThread started\n");
669 TSVNCacheResponse response
;
670 DWORD cbBytesRead
, cbWritten
;
674 // The thread's parameter is a handle to a pipe instance.
676 hPipe
= (HANDLE
) lpvParam
;
677 InterlockedIncrement(&nThreadCount
);
680 // Read client requests from the pipe.
681 TSVNCacheRequest request
;
683 hPipe
, // handle to pipe
684 &request
, // buffer to receive data
685 sizeof(request
), // size of buffer
686 &cbBytesRead
, // number of bytes read
687 NULL
); // not overlapped I/O
689 if (! fSuccess
|| cbBytesRead
== 0)
691 DisconnectNamedPipe(hPipe
);
693 ATLTRACE("Instance thread exited\n");
694 InterlockedDecrement(&nThreadCount
);
695 if (nThreadCount
== 0)
696 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
700 DWORD responseLength
;
701 GetAnswerToRequest(&request
, &response
, &responseLength
);
703 // Write the reply to the pipe.
704 fSuccess
= WriteFile(
705 hPipe
, // handle to pipe
706 &response
, // buffer to write from
707 responseLength
, // number of bytes to write
708 &cbWritten
, // number of bytes written
709 NULL
); // not overlapped I/O
711 if (! fSuccess
|| responseLength
!= cbWritten
)
713 DisconnectNamedPipe(hPipe
);
715 ATLTRACE("Instance thread exited\n");
716 InterlockedDecrement(&nThreadCount
);
717 if (nThreadCount
== 0)
718 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
723 // Flush the pipe to allow the client to read the pipe's contents
724 // before disconnecting. Then disconnect the pipe, and close the
725 // handle to this pipe instance.
727 FlushFileBuffers(hPipe
);
728 DisconnectNamedPipe(hPipe
);
730 ATLTRACE("Instance thread exited\n");
731 InterlockedDecrement(&nThreadCount
);
732 if (nThreadCount
== 0)
733 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
737 DWORD WINAPI
CommandThread(LPVOID lpvParam
)
739 ATLTRACE("CommandThread started\n");
744 // The thread's parameter is a handle to a pipe instance.
746 hPipe
= (HANDLE
) lpvParam
;
750 // Read client requests from the pipe.
751 TSVNCacheCommand command
;
753 hPipe
, // handle to pipe
754 &command
, // buffer to receive data
755 sizeof(command
), // size of buffer
756 &cbBytesRead
, // number of bytes read
757 NULL
); // not overlapped I/O
759 if (! fSuccess
|| cbBytesRead
== 0)
761 DisconnectNamedPipe(hPipe
);
763 ATLTRACE("Command thread exited\n");
767 switch (command
.command
)
769 case TSVNCACHECOMMAND_END
:
770 FlushFileBuffers(hPipe
);
771 DisconnectNamedPipe(hPipe
);
773 ATLTRACE("Command thread exited\n");
775 case TSVNCACHECOMMAND_CRAWL
:
777 CTGitPath changedpath
;
778 changedpath
.SetFromWin(CString(command
.path
), true);
779 // remove the path from our cache - that will 'invalidate' it.
780 CGitStatusCache::Instance().WaitToWrite();
781 CGitStatusCache::Instance().RemoveCacheForPath(changedpath
);
782 CGitStatusCache::Instance().Done();
783 CGitStatusCache::Instance().AddFolderForCrawling(changedpath
.GetDirectory());
786 case TSVNCACHECOMMAND_REFRESHALL
:
787 CGitStatusCache::Instance().WaitToWrite();
788 CGitStatusCache::Instance().Refresh();
789 CGitStatusCache::Instance().Done();
791 case TSVNCACHECOMMAND_RELEASE
:
793 CTGitPath changedpath
;
794 changedpath
.SetFromWin(CString(command
.path
), true);
795 ATLTRACE(_T("release handle for path %s\n"), changedpath
.GetWinPath());
796 CGitStatusCache::Instance().WaitToWrite();
797 CGitStatusCache::Instance().CloseWatcherHandles(changedpath
);
798 CGitStatusCache::Instance().RemoveCacheForPath(changedpath
);
799 CGitStatusCache::Instance().Done();
806 // Flush the pipe to allow the client to read the pipe's contents
807 // before disconnecting. Then disconnect the pipe, and close the
808 // handle to this pipe instance.
810 FlushFileBuffers(hPipe
);
811 DisconnectNamedPipe(hPipe
);
813 ATLTRACE("Command thread exited\n");