1 // TortoiseGit - a Windows shell extension for easy version control
3 // External Cache Copyright (C) 2005 - 2006,2010 - 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 "TGITCache.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"
35 #include "SmartHandle.h"
40 #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
43 #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
47 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
49 CCrashReport
crasher("tortoisegit-bug@googlegroups.com", "Crash Report for TGitCache " APP_X64_STRING
" : " STRPRODUCTVER
, TRUE
);// crash
51 DWORD WINAPI
InstanceThread(LPVOID
);
52 DWORD WINAPI
PipeThread(LPVOID
);
53 DWORD WINAPI
CommandWaitThread(LPVOID
);
54 DWORD WINAPI
CommandThread(LPVOID
);
55 LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
57 NOTIFYICONDATA niData
;
60 TCHAR szCurrentCrawledPath
[MAX_CRAWLEDPATHS
][MAX_CRAWLEDPATHSLEN
];
61 int nCurrentCrawledpathIndex
= 0;
62 CComAutoCriticalSection critSec
;
64 volatile LONG nThreadCount
= 0;
66 #define PACKVERSION(major,minor) MAKELONG(minor,major)
67 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 CAutoLibrary 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
);
106 void DebugOutputLastError()
110 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
111 FORMAT_MESSAGE_FROM_SYSTEM
|
112 FORMAT_MESSAGE_IGNORE_INSERTS
,
115 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
123 // Display the string.
124 OutputDebugStringA("TGitCache GetLastError(): ");
125 OutputDebugString((LPCTSTR
)lpMsgBuf
);
126 OutputDebugStringA("\n");
129 LocalFree( lpMsgBuf
);
132 int __stdcall
WinMain(HINSTANCE hInstance
, HINSTANCE
/*hPrevInstance*/, LPSTR
/*lpCmdLine*/, int /*cmdShow*/)
134 SetDllDirectory(L
"");
135 CAutoGeneralHandle hReloadProtection
= ::CreateMutex(NULL
, FALSE
, GetCacheMutexName());
137 if ((!hReloadProtection
) || (GetLastError() == ERROR_ALREADY_EXISTS
))
139 // An instance of TGitCache is already running
140 ATLTRACE("TGitCache ignoring restart\n");
145 // svn_dso_initialize2();
146 g_GitAdminDir
.Init();
147 CGitStatusCache::Create();
148 CGitStatusCache::Instance().Init();
150 SecureZeroMemory(szCurrentCrawledPath
, sizeof(szCurrentCrawledPath
));
154 TCHAR szWindowClass
[] = {TGIT_CACHE_WINDOW_NAME
};
156 // create a hidden window to receive window messages.
158 wcex
.cbSize
= sizeof(WNDCLASSEX
);
159 wcex
.style
= CS_HREDRAW
| CS_VREDRAW
;
160 wcex
.lpfnWndProc
= (WNDPROC
)WndProc
;
163 wcex
.hInstance
= hInstance
;
166 wcex
.hbrBackground
= 0;
167 wcex
.lpszMenuName
= NULL
;
168 wcex
.lpszClassName
= szWindowClass
;
170 RegisterClassEx(&wcex
);
171 hWnd
= CreateWindow(TGIT_CACHE_WINDOW_NAME
, TGIT_CACHE_WINDOW_NAME
, WS_CAPTION
, 0, 0, 800, 300, NULL
, 0, hInstance
, 0);
177 if (CRegStdDWORD(_T("Software\\TortoiseGit\\CacheTrayIcon"), FALSE
)==TRUE
)
179 SecureZeroMemory(&niData
,sizeof(NOTIFYICONDATA
));
181 DWORD dwVersion
= GetDllVersion(_T("Shell32.dll"));
183 if (dwVersion
>= PACKVERSION(6,0))
184 niData
.cbSize
= sizeof(NOTIFYICONDATA
);
185 else if (dwVersion
>= PACKVERSION(5,0))
186 niData
.cbSize
= NOTIFYICONDATA_V2_SIZE
;
188 niData
.cbSize
= NOTIFYICONDATA_V1_SIZE
;
190 niData
.uID
= TRAY_ID
; // own tray icon ID
192 niData
.uFlags
= NIF_ICON
|NIF_MESSAGE
;
196 (HICON
)LoadImage(hInstance
,
197 MAKEINTRESOURCE(IDI_TGITCACHE
),
199 GetSystemMetrics(SM_CXSMICON
),
200 GetSystemMetrics(SM_CYSMICON
),
203 // set the message to send
204 // note: the message value should be in the
205 // range of WM_APP through 0xBFFF
206 niData
.uCallbackMessage
= TRAY_CALLBACK
;
207 Shell_NotifyIcon(NIM_ADD
,&niData
);
209 if(niData
.hIcon
&& DestroyIcon(niData
.hIcon
))
213 // Create a thread which waits for incoming pipe connections
214 CAutoGeneralHandle hPipeThread
= CreateThread(
215 NULL
, // no security attribute
216 0, // default stack size
218 (LPVOID
) &bRun
, // thread parameter
220 &dwThreadId
); // returns thread ID
224 //OutputDebugStringA("TSVNCache: Could not create pipe thread\n");
225 //DebugOutputLastError();
228 else hPipeThread
.CloseHandle();
230 // Create a thread which waits for incoming pipe connections
231 CAutoGeneralHandle hCommandWaitThread
= CreateThread(
232 NULL
, // no security attribute
233 0, // default stack size
235 (LPVOID
) &bRun
, // thread parameter
237 &dwThreadId
); // returns thread ID
239 if (!hCommandWaitThread
)
241 //OutputDebugStringA("TSVNCache: Could not create command wait thread\n");
242 //DebugOutputLastError();
247 // loop to handle window messages.
251 bLoopRet
= GetMessage(&msg
, NULL
, 0, 0);
252 if ((bLoopRet
!= -1)&&(bLoopRet
!= 0))
254 DispatchMessage(&msg
);
260 Shell_NotifyIcon(NIM_DELETE
,&niData
);
261 CGitStatusCache::Destroy();
262 g_GitAdminDir
.Close();
268 LRESULT CALLBACK
WndProc(HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
276 case WM_LBUTTONDBLCLK
:
277 if (IsWindowVisible(hWnd
))
278 ShowWindow(hWnd
, SW_HIDE
);
280 ShowWindow(hWnd
, SW_RESTORE
);
285 NOTIFYICONDATA SystemTray
;
286 sInfoTip
.Format(_T("Cached Directories : %ld\nWatched paths : %ld"),
287 CGitStatusCache::Instance().GetCacheSize(),
288 CGitStatusCache::Instance().GetNumberOfWatchedPaths());
290 SystemTray
.cbSize
= sizeof(NOTIFYICONDATA
);
291 SystemTray
.hWnd
= hTrayWnd
;
292 SystemTray
.uID
= TRAY_ID
;
293 SystemTray
.uFlags
= NIF_TIP
;
294 _tcscpy_s(SystemTray
.szTip
, sInfoTip
);
295 Shell_NotifyIcon(NIM_MODIFY
, &SystemTray
);
302 DWORD ptW
= GetMessagePos();
303 pt
.x
= GET_X_LPARAM(ptW
);
304 pt
.y
= GET_Y_LPARAM(ptW
);
305 HMENU hMenu
= CreatePopupMenu();
308 InsertMenu(hMenu
, (UINT
)-1, MF_BYPOSITION
, TRAYPOP_EXIT
, _T("Exit"));
309 SetForegroundWindow(hWnd
);
310 TrackPopupMenu(hMenu
, TPM_BOTTOMALIGN
, pt
.x
, pt
.y
, 0, hWnd
, NULL
);
321 HDC hdc
= BeginPaint(hWnd
, &ps
);
323 GetClientRect(hWnd
, &rect
);
324 // clear the background
325 HBRUSH background
= CreateSolidBrush(::GetSysColor(COLOR_WINDOW
));
326 HGDIOBJ oldbrush
= SelectObject(hdc
, background
);
327 FillRect(hdc
, &rect
, background
);
331 AutoLocker
print(critSec
);
332 GetTextExtentPoint32( hdc
, szCurrentCrawledPath
[0], (int)_tcslen(szCurrentCrawledPath
[0]), &fontsize
);
333 for (int i
=nCurrentCrawledpathIndex
; i
<MAX_CRAWLEDPATHS
; ++i
)
335 TextOut(hdc
, 0, line
*fontsize
.cy
, szCurrentCrawledPath
[i
], (int)_tcslen(szCurrentCrawledPath
[i
]));
338 for (int i
=0; i
<nCurrentCrawledpathIndex
; ++i
)
340 TextOut(hdc
, 0, line
*fontsize
.cy
, szCurrentCrawledPath
[i
], (int)_tcslen(szCurrentCrawledPath
[i
]));
345 SelectObject(hdc
,oldbrush
);
347 DeleteObject(background
);
353 WORD wmId
= LOWORD(wParam
);
363 case WM_QUERYENDSESSION
:
365 ATLTRACE("WM_QUERYENDSESSION\n");
366 if (CGitStatusCache::Instance().WaitToWrite(200))
368 CGitStatusCache::Instance().Stop();
369 CGitStatusCache::Instance().Done();
379 ATLTRACE("WM_CLOSE/DESTROY/ENDSESSION/QUIT\n");
380 CGitStatusCache::Instance().WaitToWrite();
381 CGitStatusCache::Instance().Stop();
382 CGitStatusCache::Instance().SaveCache();
383 if (message
!= WM_QUIT
)
389 case WM_DEVICECHANGE
:
391 DEV_BROADCAST_HDR
* phdr
= (DEV_BROADCAST_HDR
*)lParam
;
394 case DBT_CUSTOMEVENT
:
396 ATLTRACE("WM_DEVICECHANGE with DBT_CUSTOMEVENT\n");
397 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
399 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
400 if (IsEqualGUID(phandle
->dbch_eventguid
, GUID_IO_VOLUME_DISMOUNT
))
402 ATLTRACE("Device to be dismounted\n");
403 CGitStatusCache::Instance().WaitToWrite();
404 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
405 CGitStatusCache::Instance().Done();
407 if (IsEqualGUID(phandle
->dbch_eventguid
, GUID_IO_VOLUME_LOCK
))
409 ATLTRACE("Device lock event\n");
410 CGitStatusCache::Instance().WaitToWrite();
411 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
412 CGitStatusCache::Instance().Done();
417 case DBT_DEVICEREMOVEPENDING
:
418 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVEPENDING\n");
419 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
421 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
422 CGitStatusCache::Instance().WaitToWrite();
423 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
424 CGitStatusCache::Instance().Done();
428 CGitStatusCache::Instance().WaitToWrite();
429 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
430 CGitStatusCache::Instance().Done();
433 case DBT_DEVICEQUERYREMOVE
:
434 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEQUERYREMOVE\n");
435 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
437 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
438 CGitStatusCache::Instance().WaitToWrite();
439 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
440 CGitStatusCache::Instance().Done();
444 CGitStatusCache::Instance().WaitToWrite();
445 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
446 CGitStatusCache::Instance().Done();
449 case DBT_DEVICEREMOVECOMPLETE
:
450 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVECOMPLETE\n");
451 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
453 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
454 CGitStatusCache::Instance().WaitToWrite();
455 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
456 CGitStatusCache::Instance().Done();
460 CGitStatusCache::Instance().WaitToWrite();
461 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
462 CGitStatusCache::Instance().Done();
471 return DefWindowProc(hWnd
, message
, wParam
, lParam
);
474 //////////////////////////////////////////////////////////////////////////
476 VOID
GetAnswerToRequest(const TGITCacheRequest
* pRequest
, TGITCacheResponse
* pReply
, DWORD
* pResponseLength
)
479 *pResponseLength
= 0;
480 if(pRequest
->flags
& TGITCACHE_FLAGS_FOLDERISKNOWN
)
482 path
.SetFromWin(pRequest
->path
, !!(pRequest
->flags
& TGITCACHE_FLAGS_ISFOLDER
));
486 path
.SetFromWin(pRequest
->path
);
489 if (CGitStatusCache::Instance().WaitToRead(2000))
491 CGitStatusCache::Instance().GetStatusForPath(path
, pRequest
->flags
, false).BuildCacheResponse(*pReply
, *pResponseLength
);
492 CGitStatusCache::Instance().Done();
496 CStatusCacheEntry entry
;
497 entry
.BuildCacheResponse(*pReply
, *pResponseLength
);
501 DWORD WINAPI
PipeThread(LPVOID lpvParam
)
503 ATLTRACE("PipeThread started\n");
504 bool * bRun
= (bool *)lpvParam
;
505 // The main loop creates an instance of the named pipe and
506 // then waits for a client to connect to it. When the client
507 // connects, a thread is created to handle communications
508 // with that client, and the loop is repeated.
515 hPipe
= CreateNamedPipe(
517 PIPE_ACCESS_DUPLEX
, // read/write access
518 PIPE_TYPE_MESSAGE
| // message type pipe
519 PIPE_READMODE_MESSAGE
| // message-read mode
520 PIPE_WAIT
, // blocking mode
521 PIPE_UNLIMITED_INSTANCES
, // max. instances
522 BUFSIZE
, // output buffer size
523 BUFSIZE
, // input buffer size
524 NMPWAIT_USE_DEFAULT_WAIT
, // client time-out
529 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
530 //DebugOutputLastError();
533 continue; // never leave the thread!
536 // Wait for the client to connect; if it succeeds,
537 // the function returns a nonzero value. If the function returns
538 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
539 fConnected
= ConnectNamedPipe(hPipe
, NULL
) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED
);
542 // Create a thread for this client.
543 CAutoGeneralHandle hInstanceThread
= CreateThread(
544 NULL
, // no security attribute
545 0, // default stack size
547 (HANDLE
) hPipe
, // thread parameter
549 &dwThreadId
); // returns thread ID
551 if (!hInstanceThread
)
553 //OutputDebugStringA("TSVNCache: Could not create Instance thread\n");
554 //DebugOutputLastError();
555 DisconnectNamedPipe(hPipe
);
556 // since we're now closing this thread, we also have to close the whole application!
557 // otherwise the thread is dead, but the app is still running, refusing new instances
558 // but no pipe will be available anymore.
559 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
562 // detach the handle, since we passed it to the thread
567 // The client could not connect, so close the pipe.
568 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
569 //DebugOutputLastError();
573 continue; // don't end the thread!
576 ATLTRACE("Pipe thread exited\n");
580 DWORD WINAPI
CommandWaitThread(LPVOID lpvParam
)
582 ATLTRACE("CommandWaitThread started\n");
583 bool * bRun
= (bool *)lpvParam
;
584 // The main loop creates an instance of the named pipe and
585 // then waits for a client to connect to it. When the client
586 // connects, a thread is created to handle communications
587 // with that client, and the loop is repeated.
594 hPipe
= CreateNamedPipe(
595 GetCacheCommandPipeName(),
596 PIPE_ACCESS_DUPLEX
, // read/write access
597 PIPE_TYPE_MESSAGE
| // message type pipe
598 PIPE_READMODE_MESSAGE
| // message-read mode
599 PIPE_WAIT
, // blocking mode
600 PIPE_UNLIMITED_INSTANCES
, // max. instances
601 BUFSIZE
, // output buffer size
602 BUFSIZE
, // input buffer size
603 NMPWAIT_USE_DEFAULT_WAIT
, // client time-out
608 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
609 //DebugOutputLastError();
612 continue; // never leave the thread!
615 // Wait for the client to connect; if it succeeds,
616 // the function returns a nonzero value. If the function returns
617 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
618 fConnected
= ConnectNamedPipe(hPipe
, NULL
) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED
);
621 // Create a thread for this client.
622 CAutoGeneralHandle hCommandThread
= CreateThread(
623 NULL
, // no security attribute
624 0, // default stack size
626 (HANDLE
) hPipe
, // thread parameter
628 &dwThreadId
); // returns thread ID
632 //OutputDebugStringA("TSVNCache: Could not create Command thread\n");
633 //DebugOutputLastError();
634 DisconnectNamedPipe(hPipe
);
636 // since we're now closing this thread, we also have to close the whole application!
637 // otherwise the thread is dead, but the app is still running, refusing new instances
638 // but no pipe will be available anymore.
639 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
642 // detach the handle, since we passed it to the thread
647 // The client could not connect, so close the pipe.
648 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
649 //DebugOutputLastError();
653 continue; // don't end the thread!
656 ATLTRACE("CommandWait thread exited\n");
660 DWORD WINAPI
InstanceThread(LPVOID lpvParam
)
662 ATLTRACE("InstanceThread started\n");
663 TGITCacheResponse response
;
664 DWORD cbBytesRead
, cbWritten
;
668 // The thread's parameter is a handle to a pipe instance.
671 InterlockedIncrement(&nThreadCount
);
674 // Read client requests from the pipe.
675 TGITCacheRequest request
;
677 hPipe
, // handle to pipe
678 &request
, // buffer to receive data
679 sizeof(request
), // size of buffer
680 &cbBytesRead
, // number of bytes read
681 NULL
); // not overlapped I/O
683 if (! fSuccess
|| cbBytesRead
== 0)
685 DisconnectNamedPipe(hPipe
);
686 ATLTRACE("Instance thread exited\n");
687 InterlockedDecrement(&nThreadCount
);
688 if (nThreadCount
== 0)
689 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
693 DWORD responseLength
;
694 GetAnswerToRequest(&request
, &response
, &responseLength
);
696 // Write the reply to the pipe.
697 fSuccess
= WriteFile(
698 hPipe
, // handle to pipe
699 &response
, // buffer to write from
700 responseLength
, // number of bytes to write
701 &cbWritten
, // number of bytes written
702 NULL
); // not overlapped I/O
704 if (! fSuccess
|| responseLength
!= cbWritten
)
706 DisconnectNamedPipe(hPipe
);
707 ATLTRACE("Instance thread exited\n");
708 InterlockedDecrement(&nThreadCount
);
709 if (nThreadCount
== 0)
710 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
715 // Flush the pipe to allow the client to read the pipe's contents
716 // before disconnecting. Then disconnect the pipe, and close the
717 // handle to this pipe instance.
719 FlushFileBuffers(hPipe
);
720 DisconnectNamedPipe(hPipe
);
721 ATLTRACE("Instance thread exited\n");
722 InterlockedDecrement(&nThreadCount
);
723 if (nThreadCount
== 0)
724 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
728 DWORD WINAPI
CommandThread(LPVOID lpvParam
)
730 ATLTRACE("CommandThread started\n");
735 // The thread's parameter is a handle to a pipe instance.
741 // Read client requests from the pipe.
742 TGITCacheCommand command
;
744 hPipe
, // handle to pipe
745 &command
, // buffer to receive data
746 sizeof(command
), // size of buffer
747 &cbBytesRead
, // number of bytes read
748 NULL
); // not overlapped I/O
750 if (! fSuccess
|| cbBytesRead
== 0)
752 DisconnectNamedPipe(hPipe
);
753 ATLTRACE("Command thread exited\n");
757 switch (command
.command
)
759 case TGITCACHECOMMAND_END
:
760 FlushFileBuffers(hPipe
);
761 DisconnectNamedPipe(hPipe
);
762 ATLTRACE("Command thread exited\n");
764 case TGITCACHECOMMAND_CRAWL
:
766 CTGitPath changedpath
;
767 changedpath
.SetFromWin(CString(command
.path
), true);
768 // remove the path from our cache - that will 'invalidate' it.
769 CGitStatusCache::Instance().WaitToWrite();
770 CGitStatusCache::Instance().RemoveCacheForPath(changedpath
);
771 CGitStatusCache::Instance().Done();
772 CGitStatusCache::Instance().AddFolderForCrawling(changedpath
.GetDirectory());
775 case TGITCACHECOMMAND_REFRESHALL
:
776 CGitStatusCache::Instance().WaitToWrite();
777 CGitStatusCache::Instance().Refresh();
778 CGitStatusCache::Instance().Done();
780 case TGITCACHECOMMAND_RELEASE
:
782 CTGitPath changedpath
;
783 changedpath
.SetFromWin(CString(command
.path
), true);
784 ATLTRACE(_T("release handle for path %s\n"), changedpath
.GetWinPath());
785 CGitStatusCache::Instance().WaitToWrite();
786 CGitStatusCache::Instance().CloseWatcherHandles(changedpath
);
787 CGitStatusCache::Instance().RemoveCacheForPath(changedpath
);
788 CGitStatusCache::Instance().Done();
791 case TGITCACHECOMMAND_BLOCK
:
793 CTGitPath changedpath
;
794 changedpath
.SetFromWin(CString(command
.path
));
795 ATLTRACE(_T("block path %s\n"), changedpath
.GetWinPath());
796 CGitStatusCache::Instance().BlockPath(changedpath
);
799 case TGITCACHECOMMAND_UNBLOCK
:
801 CTGitPath changedpath
;
802 changedpath
.SetFromWin(CString(command
.path
));
803 ATLTRACE(_T("block path %s\n"), changedpath
.GetWinPath());
804 CGitStatusCache::Instance().UnBlockPath(changedpath
);
810 // Flush the pipe to allow the client to read the pipe's contents
811 // before disconnecting. Then disconnect the pipe, and close the
812 // handle to this pipe instance.
814 FlushFileBuffers(hPipe
);
815 DisconnectNamedPipe(hPipe
);
816 ATLTRACE("Command thread exited\n");