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-2014 - 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 "CrashReport.h"
29 #include "GitAdminDir.h"
33 #include "..\version.h"
34 #include "SmartHandle.h"
35 #include "DllVersion.h"
38 #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
41 #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
45 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
47 #if ENABLE_CRASHHANLDER
48 CCrashReportTGit
crasher(L
"TGitCache " _T(APP_X64_STRING
), TGIT_VERMAJOR
, TGIT_VERMINOR
, TGIT_VERMICRO
, TGIT_VERBUILD
, TGIT_VERDATE
);
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)
68 void DebugOutputLastError()
72 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
73 FORMAT_MESSAGE_FROM_SYSTEM
|
74 FORMAT_MESSAGE_IGNORE_INSERTS
,
77 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
85 // Display the string.
86 OutputDebugStringA("TGitCache GetLastError(): ");
87 OutputDebugString((LPCTSTR
)lpMsgBuf
);
88 OutputDebugStringA("\n");
91 LocalFree( lpMsgBuf
);
94 int __stdcall
WinMain(HINSTANCE hInstance
, HINSTANCE
/*hPrevInstance*/, LPSTR
/*lpCmdLine*/, int /*cmdShow*/)
97 CAutoGeneralHandle hReloadProtection
= ::CreateMutex(NULL
, FALSE
, GetCacheMutexName());
99 if ((!hReloadProtection
) || (GetLastError() == ERROR_ALREADY_EXISTS
))
101 // An instance of TGitCache is already running
102 CTraceToOutputDebugString::Instance()(__FUNCTION__
": TGitCache ignoring restart\n");
106 CGitStatusCache::Create();
107 CGitStatusCache::Instance().Init();
109 SecureZeroMemory(szCurrentCrawledPath
, sizeof(szCurrentCrawledPath
));
113 TCHAR szWindowClass
[] = {TGIT_CACHE_WINDOW_NAME
};
115 // create a hidden window to receive window messages.
117 wcex
.cbSize
= sizeof(WNDCLASSEX
);
118 wcex
.style
= CS_HREDRAW
| CS_VREDRAW
;
119 wcex
.lpfnWndProc
= (WNDPROC
)WndProc
;
122 wcex
.hInstance
= hInstance
;
125 wcex
.hbrBackground
= 0;
126 wcex
.lpszMenuName
= NULL
;
127 wcex
.lpszClassName
= szWindowClass
;
129 RegisterClassEx(&wcex
);
130 hWnd
= CreateWindow(TGIT_CACHE_WINDOW_NAME
, TGIT_CACHE_WINDOW_NAME
, WS_CAPTION
, 0, 0, 800, 300, NULL
, 0, hInstance
, 0);
136 if (CRegStdDWORD(_T("Software\\TortoiseGit\\CacheTrayIcon"), FALSE
)==TRUE
)
138 SecureZeroMemory(&niData
,sizeof(NOTIFYICONDATA
));
142 GetShellVersion(&dwMajor
, &dwMinor
);
143 DWORD dwVersion
= PACKVERSION(dwMajor
, dwMinor
);
144 if (dwVersion
>= PACKVERSION(6,0))
145 niData
.cbSize
= sizeof(NOTIFYICONDATA
);
146 else if (dwVersion
>= PACKVERSION(5,0))
147 niData
.cbSize
= NOTIFYICONDATA_V2_SIZE
;
149 niData
.cbSize
= NOTIFYICONDATA_V1_SIZE
;
151 niData
.uID
= TRAY_ID
; // own tray icon ID
153 niData
.uFlags
= NIF_ICON
|NIF_MESSAGE
;
157 (HICON
)LoadImage(hInstance
,
158 MAKEINTRESOURCE(IDI_TGITCACHE
),
160 GetSystemMetrics(SM_CXSMICON
),
161 GetSystemMetrics(SM_CYSMICON
),
164 // set the message to send
165 // note: the message value should be in the
166 // range of WM_APP through 0xBFFF
167 niData
.uCallbackMessage
= TRAY_CALLBACK
;
168 Shell_NotifyIcon(NIM_ADD
,&niData
);
170 if(niData
.hIcon
&& DestroyIcon(niData
.hIcon
))
174 // Create a thread which waits for incoming pipe connections
175 CAutoGeneralHandle hPipeThread
= CreateThread(
176 NULL
, // no security attribute
177 0, // default stack size
179 (LPVOID
) &bRun
, // thread parameter
181 &dwThreadId
); // returns thread ID
185 //OutputDebugStringA("TGitCache: Could not create pipe thread\n");
186 //DebugOutputLastError();
189 else hPipeThread
.CloseHandle();
191 // Create a thread which waits for incoming pipe connections
192 CAutoGeneralHandle hCommandWaitThread
= CreateThread(
193 NULL
, // no security attribute
194 0, // default stack size
196 (LPVOID
) &bRun
, // thread parameter
198 &dwThreadId
); // returns thread ID
200 if (!hCommandWaitThread
)
202 //OutputDebugStringA("TGitCache: Could not create command wait thread\n");
203 //DebugOutputLastError();
208 // loop to handle window messages.
211 BOOL bLoopRet
= GetMessage(&msg
, NULL
, 0, 0);
212 if ((bLoopRet
!= -1)&&(bLoopRet
!= 0))
214 DispatchMessage(&msg
);
220 Shell_NotifyIcon(NIM_DELETE
,&niData
);
221 CGitStatusCache::Destroy();
226 LRESULT CALLBACK
WndProc(HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
234 case WM_LBUTTONDBLCLK
:
235 if (IsWindowVisible(hWnd
))
236 ShowWindow(hWnd
, SW_HIDE
);
238 ShowWindow(hWnd
, SW_RESTORE
);
243 NOTIFYICONDATA SystemTray
;
244 sInfoTip
.Format(_T("TortoiseGit Overlay Icon Server\nCached Directories: %Id\nWatched paths: %d"),
245 CGitStatusCache::Instance().GetCacheSize(),
246 CGitStatusCache::Instance().GetNumberOfWatchedPaths());
248 SystemTray
.cbSize
= sizeof(NOTIFYICONDATA
);
249 SystemTray
.hWnd
= hTrayWnd
;
250 SystemTray
.uID
= TRAY_ID
;
251 SystemTray
.uFlags
= NIF_TIP
;
252 _tcscpy_s(SystemTray
.szTip
, sInfoTip
);
253 Shell_NotifyIcon(NIM_MODIFY
, &SystemTray
);
261 HMENU hMenu
= CreatePopupMenu();
264 InsertMenu(hMenu
, (UINT
)-1, MF_BYPOSITION
, TRAYPOP_EXIT
, _T("Exit"));
265 SetForegroundWindow(hWnd
);
266 TrackPopupMenu(hMenu
, TPM_BOTTOMALIGN
, pt
.x
, pt
.y
, 0, hWnd
, NULL
);
277 HDC hdc
= BeginPaint(hWnd
, &ps
);
279 GetClientRect(hWnd
, &rect
);
280 // clear the background
281 HBRUSH background
= CreateSolidBrush(::GetSysColor(COLOR_WINDOW
));
282 HGDIOBJ oldbrush
= SelectObject(hdc
, background
);
283 FillRect(hdc
, &rect
, background
);
287 AutoLocker
print(critSec
);
288 GetTextExtentPoint32( hdc
, szCurrentCrawledPath
[0], (int)_tcslen(szCurrentCrawledPath
[0]), &fontsize
);
289 for (int i
=nCurrentCrawledpathIndex
; i
<MAX_CRAWLEDPATHS
; ++i
)
291 TextOut(hdc
, 0, line
*fontsize
.cy
, szCurrentCrawledPath
[i
], (int)_tcslen(szCurrentCrawledPath
[i
]));
294 for (int i
=0; i
<nCurrentCrawledpathIndex
; ++i
)
296 TextOut(hdc
, 0, line
*fontsize
.cy
, szCurrentCrawledPath
[i
], (int)_tcslen(szCurrentCrawledPath
[i
]));
300 SelectObject(hdc
,oldbrush
);
302 DeleteObject(background
);
308 WORD wmId
= LOWORD(wParam
);
318 case WM_QUERYENDSESSION
:
320 CTraceToOutputDebugString::Instance()(__FUNCTION__
": WM_QUERYENDSESSION\n");
321 CAutoWriteWeakLock
writeLock(CGitStatusCache::Instance().GetGuard(), 200);
322 CGitStatusCache::Instance().Stop();
331 CTraceToOutputDebugString::Instance()(__FUNCTION__
": WM_CLOSE/DESTROY/ENDSESSION/QUIT\n");
332 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
333 CGitStatusCache::Instance().Stop();
334 CGitStatusCache::Instance().SaveCache();
335 if (message
!= WM_QUIT
)
341 case WM_DEVICECHANGE
:
343 DEV_BROADCAST_HDR
* phdr
= (DEV_BROADCAST_HDR
*)lParam
;
346 case DBT_CUSTOMEVENT
:
348 CTraceToOutputDebugString::Instance()(__FUNCTION__
": WM_DEVICECHANGE with DBT_CUSTOMEVENT\n");
349 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
351 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
352 if (IsEqualGUID(phandle
->dbch_eventguid
, GUID_IO_VOLUME_DISMOUNT
))
354 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Device to be dismounted\n");
355 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
356 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_handle
);
358 if (IsEqualGUID(phandle
->dbch_eventguid
, GUID_IO_VOLUME_LOCK
))
360 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Device lock event\n");
361 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
362 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_handle
);
367 case DBT_DEVICEREMOVEPENDING
:
368 case DBT_DEVICEQUERYREMOVE
:
369 case DBT_DEVICEREMOVECOMPLETE
:
370 CTraceToOutputDebugString::Instance()(__FUNCTION__
": WM_DEVICECHANGE with DBT_DEVICEREMOVEPENDING/DBT_DEVICEQUERYREMOVE/DBT_DEVICEREMOVECOMPLETE\n");
371 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
373 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
374 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
375 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_handle
);
377 else if (phdr
->dbch_devicetype
== DBT_DEVTYP_VOLUME
)
379 DEV_BROADCAST_VOLUME
* pVolume
= (DEV_BROADCAST_VOLUME
*)lParam
;
380 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
381 for (BYTE i
= 0; i
< 26; ++i
)
383 if (pVolume
->dbcv_unitmask
& (1 << i
))
385 TCHAR driveletter
= 'A' + i
;
386 CString drive
= CString(driveletter
);
388 CGitStatusCache::Instance().CloseWatcherHandles(CTGitPath(drive
));
394 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
395 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
404 return DefWindowProc(hWnd
, message
, wParam
, lParam
);
407 //////////////////////////////////////////////////////////////////////////
409 VOID
GetAnswerToRequest(const TGITCacheRequest
* pRequest
, TGITCacheResponse
* pReply
, DWORD
* pResponseLength
)
412 *pResponseLength
= 0;
413 if(pRequest
->flags
& TGITCACHE_FLAGS_FOLDERISKNOWN
)
415 path
.SetFromWin(pRequest
->path
, !!(pRequest
->flags
& TGITCACHE_FLAGS_ISFOLDER
));
419 path
.SetFromWin(pRequest
->path
);
422 CAutoReadWeakLock
readLock(CGitStatusCache::Instance().GetGuard(), 2000);
423 if (readLock
.IsAcquired())
425 CGitStatusCache::Instance().GetStatusForPath(path
, pRequest
->flags
, false).BuildCacheResponse(*pReply
, *pResponseLength
);
429 CStatusCacheEntry entry
;
430 entry
.BuildCacheResponse(*pReply
, *pResponseLength
);
434 DWORD WINAPI
PipeThread(LPVOID lpvParam
)
436 CTraceToOutputDebugString::Instance()(__FUNCTION__
": PipeThread started\n");
437 bool * bRun
= (bool *)lpvParam
;
438 // The main loop creates an instance of the named pipe and
439 // then waits for a client to connect to it. When the client
440 // connects, a thread is created to handle communications
441 // with that client, and the loop is repeated.
448 hPipe
= CreateNamedPipe(
450 PIPE_ACCESS_DUPLEX
, // read/write access
451 PIPE_TYPE_MESSAGE
| // message type pipe
452 PIPE_READMODE_MESSAGE
| // message-read mode
453 PIPE_WAIT
, // blocking mode
454 PIPE_UNLIMITED_INSTANCES
, // max. instances
455 BUFSIZE
, // output buffer size
456 BUFSIZE
, // input buffer size
457 NMPWAIT_USE_DEFAULT_WAIT
, // client time-out
462 //OutputDebugStringA("TGitCache: CreatePipe failed\n");
463 //DebugOutputLastError();
466 continue; // never leave the thread!
469 // Wait for the client to connect; if it succeeds,
470 // the function returns a nonzero value. If the function returns
471 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
472 fConnected
= ConnectNamedPipe(hPipe
, NULL
) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED
);
475 // Create a thread for this client.
476 CAutoGeneralHandle hInstanceThread
= CreateThread(
477 NULL
, // no security attribute
478 0, // default stack size
480 (HANDLE
) hPipe
, // thread parameter
482 &dwThreadId
); // returns thread ID
484 if (!hInstanceThread
)
486 //OutputDebugStringA("TGitCache: Could not create Instance thread\n");
487 //DebugOutputLastError();
488 DisconnectNamedPipe(hPipe
);
489 // since we're now closing this thread, we also have to close the whole application!
490 // otherwise the thread is dead, but the app is still running, refusing new instances
491 // but no pipe will be available anymore.
492 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
495 // detach the handle, since we passed it to the thread
500 // The client could not connect, so close the pipe.
501 //OutputDebugStringA("TGitCache: ConnectNamedPipe failed\n");
502 //DebugOutputLastError();
506 continue; // don't end the thread!
509 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Pipe thread exited\n");
513 DWORD WINAPI
CommandWaitThread(LPVOID lpvParam
)
515 CTraceToOutputDebugString::Instance()(__FUNCTION__
": CommandWaitThread started\n");
516 bool * bRun
= (bool *)lpvParam
;
517 // The main loop creates an instance of the named pipe and
518 // then waits for a client to connect to it. When the client
519 // connects, a thread is created to handle communications
520 // with that client, and the loop is repeated.
527 hPipe
= CreateNamedPipe(
528 GetCacheCommandPipeName(),
529 PIPE_ACCESS_DUPLEX
, // read/write access
530 PIPE_TYPE_MESSAGE
| // message type pipe
531 PIPE_READMODE_MESSAGE
| // message-read mode
532 PIPE_WAIT
, // blocking mode
533 PIPE_UNLIMITED_INSTANCES
, // max. instances
534 BUFSIZE
, // output buffer size
535 BUFSIZE
, // input buffer size
536 NMPWAIT_USE_DEFAULT_WAIT
, // client time-out
541 //OutputDebugStringA("TGitCache: CreatePipe failed\n");
542 //DebugOutputLastError();
545 continue; // never leave the thread!
548 // Wait for the client to connect; if it succeeds,
549 // the function returns a nonzero value. If the function returns
550 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
551 fConnected
= ConnectNamedPipe(hPipe
, NULL
) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED
);
554 // Create a thread for this client.
555 CAutoGeneralHandle hCommandThread
= CreateThread(
556 NULL
, // no security attribute
557 0, // default stack size
559 (HANDLE
) hPipe
, // thread parameter
561 &dwThreadId
); // returns thread ID
565 //OutputDebugStringA("TGitCache: Could not create Command thread\n");
566 //DebugOutputLastError();
567 DisconnectNamedPipe(hPipe
);
569 // since we're now closing this thread, we also have to close the whole application!
570 // otherwise the thread is dead, but the app is still running, refusing new instances
571 // but no pipe will be available anymore.
572 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
575 // detach the handle, since we passed it to the thread
580 // The client could not connect, so close the pipe.
581 //OutputDebugStringA("TGitCache: ConnectNamedPipe failed\n");
582 //DebugOutputLastError();
586 continue; // don't end the thread!
589 CTraceToOutputDebugString::Instance()(__FUNCTION__
": CommandWait thread exited\n");
593 DWORD WINAPI
InstanceThread(LPVOID lpvParam
)
595 CTraceToOutputDebugString::Instance()(__FUNCTION__
": InstanceThread started\n");
596 TGITCacheResponse response
;
597 DWORD cbBytesRead
, cbWritten
;
600 // The thread's parameter is a handle to a pipe instance.
603 InterlockedIncrement(&nThreadCount
);
606 // Read client requests from the pipe.
607 TGITCacheRequest request
;
608 BOOL fSuccess
= ReadFile(
609 hPipe
, // handle to pipe
610 &request
, // buffer to receive data
611 sizeof(request
), // size of buffer
612 &cbBytesRead
, // number of bytes read
613 NULL
); // not overlapped I/O
615 if (! fSuccess
|| cbBytesRead
== 0)
617 DisconnectNamedPipe(hPipe
);
618 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Instance thread exited\n");
619 InterlockedDecrement(&nThreadCount
);
620 if (nThreadCount
== 0)
621 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
625 DWORD responseLength
;
626 GetAnswerToRequest(&request
, &response
, &responseLength
);
628 // Write the reply to the pipe.
629 fSuccess
= WriteFile(
630 hPipe
, // handle to pipe
631 &response
, // buffer to write from
632 responseLength
, // number of bytes to write
633 &cbWritten
, // number of bytes written
634 NULL
); // not overlapped I/O
636 if (! fSuccess
|| responseLength
!= cbWritten
)
638 DisconnectNamedPipe(hPipe
);
639 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Instance thread exited\n");
640 InterlockedDecrement(&nThreadCount
);
641 if (nThreadCount
== 0)
642 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
647 // Flush the pipe to allow the client to read the pipe's contents
648 // before disconnecting. Then disconnect the pipe, and close the
649 // handle to this pipe instance.
651 FlushFileBuffers(hPipe
);
652 DisconnectNamedPipe(hPipe
);
653 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Instance thread exited\n");
654 InterlockedDecrement(&nThreadCount
);
655 if (nThreadCount
== 0)
656 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
660 DWORD WINAPI
CommandThread(LPVOID lpvParam
)
662 CTraceToOutputDebugString::Instance()(__FUNCTION__
": CommandThread started\n");
666 // The thread's parameter is a handle to a pipe instance.
672 // Read client requests from the pipe.
673 TGITCacheCommand command
;
674 BOOL fSuccess
= ReadFile(
675 hPipe
, // handle to pipe
676 &command
, // buffer to receive data
677 sizeof(command
), // size of buffer
678 &cbBytesRead
, // number of bytes read
679 NULL
); // not overlapped I/O
681 if (! fSuccess
|| cbBytesRead
== 0)
683 DisconnectNamedPipe(hPipe
);
684 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Command thread exited\n");
688 switch (command
.command
)
690 case TGITCACHECOMMAND_END
:
691 FlushFileBuffers(hPipe
);
692 DisconnectNamedPipe(hPipe
);
693 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Command thread exited\n");
695 case TGITCACHECOMMAND_CRAWL
:
697 CTGitPath changedpath
;
698 changedpath
.SetFromWin(command
.path
, true);
699 // remove the path from our cache - that will 'invalidate' it.
701 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
702 CGitStatusCache::Instance().RemoveCacheForPath(changedpath
);
704 CGitStatusCache::Instance().AddFolderForCrawling(changedpath
.GetDirectory());
707 case TGITCACHECOMMAND_REFRESHALL
:
709 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
710 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": refresh all\n"));
711 CGitStatusCache::Instance().Refresh();
714 case TGITCACHECOMMAND_RELEASE
:
716 CTGitPath changedpath
;
717 changedpath
.SetFromWin(command
.path
, true);
718 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": release handle for path %s\n"), changedpath
.GetWinPath());
719 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
720 CGitStatusCache::Instance().CloseWatcherHandles(changedpath
);
721 CGitStatusCache::Instance().RemoveCacheForPath(changedpath
);
724 case TGITCACHECOMMAND_BLOCK
:
726 CTGitPath changedpath
;
727 changedpath
.SetFromWin(command
.path
);
728 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": block path %s\n"), changedpath
.GetWinPath());
729 CGitStatusCache::Instance().BlockPath(changedpath
);
732 case TGITCACHECOMMAND_UNBLOCK
:
734 CTGitPath changedpath
;
735 changedpath
.SetFromWin(command
.path
);
736 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": unblock path %s\n"), changedpath
.GetWinPath());
737 CGitStatusCache::Instance().UnBlockPath(changedpath
);
743 // Flush the pipe to allow the client to read the pipe's contents
744 // before disconnecting. Then disconnect the pipe, and close the
745 // handle to this pipe instance.
747 FlushFileBuffers(hPipe
);
748 DisconnectNamedPipe(hPipe
);
749 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Command thread exited\n");