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-2013 - 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 "svn_dso.h"
35 #include "SmartHandle.h"
36 #include "DllVersion.h"
41 #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
44 #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
48 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
50 CCrashReportTGit
crasher(L
"TGitCache " _T(APP_X64_STRING
), TGIT_VERMAJOR
, TGIT_VERMINOR
, TGIT_VERMICRO
, TGIT_VERBUILD
, TGIT_VERDATE
);
52 DWORD WINAPI
InstanceThread(LPVOID
);
53 DWORD WINAPI
PipeThread(LPVOID
);
54 DWORD WINAPI
CommandWaitThread(LPVOID
);
55 DWORD WINAPI
CommandThread(LPVOID
);
56 LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
58 NOTIFYICONDATA niData
;
61 TCHAR szCurrentCrawledPath
[MAX_CRAWLEDPATHS
][MAX_CRAWLEDPATHSLEN
];
62 int nCurrentCrawledpathIndex
= 0;
63 CComAutoCriticalSection critSec
;
65 volatile LONG nThreadCount
= 0;
67 #define PACKVERSION(major,minor) MAKELONG(minor,major)
69 void DebugOutputLastError()
73 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
74 FORMAT_MESSAGE_FROM_SYSTEM
|
75 FORMAT_MESSAGE_IGNORE_INSERTS
,
78 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
86 // Display the string.
87 OutputDebugStringA("TGitCache GetLastError(): ");
88 OutputDebugString((LPCTSTR
)lpMsgBuf
);
89 OutputDebugStringA("\n");
92 LocalFree( lpMsgBuf
);
95 int __stdcall
WinMain(HINSTANCE hInstance
, HINSTANCE
/*hPrevInstance*/, LPSTR
/*lpCmdLine*/, int /*cmdShow*/)
98 CAutoGeneralHandle hReloadProtection
= ::CreateMutex(NULL
, FALSE
, GetCacheMutexName());
100 if ((!hReloadProtection
) || (GetLastError() == ERROR_ALREADY_EXISTS
))
102 // An instance of TGitCache is already running
103 ATLTRACE("TGitCache ignoring restart\n");
107 CGitStatusCache::Create();
108 CGitStatusCache::Instance().Init();
110 SecureZeroMemory(szCurrentCrawledPath
, sizeof(szCurrentCrawledPath
));
114 TCHAR szWindowClass
[] = {TGIT_CACHE_WINDOW_NAME
};
116 // create a hidden window to receive window messages.
118 wcex
.cbSize
= sizeof(WNDCLASSEX
);
119 wcex
.style
= CS_HREDRAW
| CS_VREDRAW
;
120 wcex
.lpfnWndProc
= (WNDPROC
)WndProc
;
123 wcex
.hInstance
= hInstance
;
126 wcex
.hbrBackground
= 0;
127 wcex
.lpszMenuName
= NULL
;
128 wcex
.lpszClassName
= szWindowClass
;
130 RegisterClassEx(&wcex
);
131 hWnd
= CreateWindow(TGIT_CACHE_WINDOW_NAME
, TGIT_CACHE_WINDOW_NAME
, WS_CAPTION
, 0, 0, 800, 300, NULL
, 0, hInstance
, 0);
137 if (CRegStdDWORD(_T("Software\\TortoiseGit\\CacheTrayIcon"), FALSE
)==TRUE
)
139 SecureZeroMemory(&niData
,sizeof(NOTIFYICONDATA
));
143 GetShellVersion(&dwMajor
, &dwMinor
);
144 DWORD dwVersion
= PACKVERSION(dwMajor
, dwMinor
);
145 if (dwVersion
>= PACKVERSION(6,0))
146 niData
.cbSize
= sizeof(NOTIFYICONDATA
);
147 else if (dwVersion
>= PACKVERSION(5,0))
148 niData
.cbSize
= NOTIFYICONDATA_V2_SIZE
;
150 niData
.cbSize
= NOTIFYICONDATA_V1_SIZE
;
152 niData
.uID
= TRAY_ID
; // own tray icon ID
154 niData
.uFlags
= NIF_ICON
|NIF_MESSAGE
;
158 (HICON
)LoadImage(hInstance
,
159 MAKEINTRESOURCE(IDI_TGITCACHE
),
161 GetSystemMetrics(SM_CXSMICON
),
162 GetSystemMetrics(SM_CYSMICON
),
165 // set the message to send
166 // note: the message value should be in the
167 // range of WM_APP through 0xBFFF
168 niData
.uCallbackMessage
= TRAY_CALLBACK
;
169 Shell_NotifyIcon(NIM_ADD
,&niData
);
171 if(niData
.hIcon
&& DestroyIcon(niData
.hIcon
))
175 // Create a thread which waits for incoming pipe connections
176 CAutoGeneralHandle hPipeThread
= CreateThread(
177 NULL
, // no security attribute
178 0, // default stack size
180 (LPVOID
) &bRun
, // thread parameter
182 &dwThreadId
); // returns thread ID
186 //OutputDebugStringA("TSVNCache: Could not create pipe thread\n");
187 //DebugOutputLastError();
190 else hPipeThread
.CloseHandle();
192 // Create a thread which waits for incoming pipe connections
193 CAutoGeneralHandle hCommandWaitThread
= CreateThread(
194 NULL
, // no security attribute
195 0, // default stack size
197 (LPVOID
) &bRun
, // thread parameter
199 &dwThreadId
); // returns thread ID
201 if (!hCommandWaitThread
)
203 //OutputDebugStringA("TSVNCache: Could not create command wait thread\n");
204 //DebugOutputLastError();
209 // loop to handle window messages.
213 bLoopRet
= GetMessage(&msg
, NULL
, 0, 0);
214 if ((bLoopRet
!= -1)&&(bLoopRet
!= 0))
216 DispatchMessage(&msg
);
222 Shell_NotifyIcon(NIM_DELETE
,&niData
);
223 CGitStatusCache::Destroy();
228 LRESULT CALLBACK
WndProc(HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
236 case WM_LBUTTONDBLCLK
:
237 if (IsWindowVisible(hWnd
))
238 ShowWindow(hWnd
, SW_HIDE
);
240 ShowWindow(hWnd
, SW_RESTORE
);
245 NOTIFYICONDATA SystemTray
;
246 sInfoTip
.Format(_T("Cached Directories : %ld\nWatched paths : %ld"),
247 CGitStatusCache::Instance().GetCacheSize(),
248 CGitStatusCache::Instance().GetNumberOfWatchedPaths());
250 SystemTray
.cbSize
= sizeof(NOTIFYICONDATA
);
251 SystemTray
.hWnd
= hTrayWnd
;
252 SystemTray
.uID
= TRAY_ID
;
253 SystemTray
.uFlags
= NIF_TIP
;
254 _tcscpy_s(SystemTray
.szTip
, sInfoTip
);
255 Shell_NotifyIcon(NIM_MODIFY
, &SystemTray
);
262 DWORD ptW
= GetMessagePos();
263 pt
.x
= GET_X_LPARAM(ptW
);
264 pt
.y
= GET_Y_LPARAM(ptW
);
265 HMENU hMenu
= CreatePopupMenu();
268 InsertMenu(hMenu
, (UINT
)-1, MF_BYPOSITION
, TRAYPOP_EXIT
, _T("Exit"));
269 SetForegroundWindow(hWnd
);
270 TrackPopupMenu(hMenu
, TPM_BOTTOMALIGN
, pt
.x
, pt
.y
, 0, hWnd
, NULL
);
281 HDC hdc
= BeginPaint(hWnd
, &ps
);
283 GetClientRect(hWnd
, &rect
);
284 // clear the background
285 HBRUSH background
= CreateSolidBrush(::GetSysColor(COLOR_WINDOW
));
286 HGDIOBJ oldbrush
= SelectObject(hdc
, background
);
287 FillRect(hdc
, &rect
, background
);
291 AutoLocker
print(critSec
);
292 GetTextExtentPoint32( hdc
, szCurrentCrawledPath
[0], (int)_tcslen(szCurrentCrawledPath
[0]), &fontsize
);
293 for (int i
=nCurrentCrawledpathIndex
; i
<MAX_CRAWLEDPATHS
; ++i
)
295 TextOut(hdc
, 0, line
*fontsize
.cy
, szCurrentCrawledPath
[i
], (int)_tcslen(szCurrentCrawledPath
[i
]));
298 for (int i
=0; i
<nCurrentCrawledpathIndex
; ++i
)
300 TextOut(hdc
, 0, line
*fontsize
.cy
, szCurrentCrawledPath
[i
], (int)_tcslen(szCurrentCrawledPath
[i
]));
304 SelectObject(hdc
,oldbrush
);
306 DeleteObject(background
);
312 WORD wmId
= LOWORD(wParam
);
322 case WM_QUERYENDSESSION
:
324 ATLTRACE("WM_QUERYENDSESSION\n");
325 CAutoWriteWeakLock
writeLock(CGitStatusCache::Instance().GetGuard(), 200);
326 CGitStatusCache::Instance().Stop();
335 ATLTRACE("WM_CLOSE/DESTROY/ENDSESSION/QUIT\n");
336 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
337 CGitStatusCache::Instance().Stop();
338 CGitStatusCache::Instance().SaveCache();
339 if (message
!= WM_QUIT
)
345 case WM_DEVICECHANGE
:
347 DEV_BROADCAST_HDR
* phdr
= (DEV_BROADCAST_HDR
*)lParam
;
350 case DBT_CUSTOMEVENT
:
352 ATLTRACE("WM_DEVICECHANGE with DBT_CUSTOMEVENT\n");
353 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
355 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
356 if (IsEqualGUID(phandle
->dbch_eventguid
, GUID_IO_VOLUME_DISMOUNT
))
358 ATLTRACE("Device to be dismounted\n");
359 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
360 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
362 if (IsEqualGUID(phandle
->dbch_eventguid
, GUID_IO_VOLUME_LOCK
))
364 ATLTRACE("Device lock event\n");
365 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
366 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
371 case DBT_DEVICEREMOVEPENDING
:
372 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVEPENDING\n");
373 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
375 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
376 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
377 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
381 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
382 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
385 case DBT_DEVICEQUERYREMOVE
:
386 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEQUERYREMOVE\n");
387 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
389 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
390 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
391 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
395 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
396 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
399 case DBT_DEVICEREMOVECOMPLETE
:
400 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVECOMPLETE\n");
401 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
403 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
404 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
405 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_hdevnotify
);
409 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
410 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
419 return DefWindowProc(hWnd
, message
, wParam
, lParam
);
422 //////////////////////////////////////////////////////////////////////////
424 VOID
GetAnswerToRequest(const TGITCacheRequest
* pRequest
, TGITCacheResponse
* pReply
, DWORD
* pResponseLength
)
427 *pResponseLength
= 0;
428 if(pRequest
->flags
& TGITCACHE_FLAGS_FOLDERISKNOWN
)
430 path
.SetFromWin(pRequest
->path
, !!(pRequest
->flags
& TGITCACHE_FLAGS_ISFOLDER
));
434 path
.SetFromWin(pRequest
->path
);
437 CAutoReadWeakLock
readLock(CGitStatusCache::Instance().GetGuard(), 2000);
438 if (readLock
.IsAcquired())
440 CGitStatusCache::Instance().GetStatusForPath(path
, pRequest
->flags
, false).BuildCacheResponse(*pReply
, *pResponseLength
);
444 CStatusCacheEntry entry
;
445 entry
.BuildCacheResponse(*pReply
, *pResponseLength
);
449 DWORD WINAPI
PipeThread(LPVOID lpvParam
)
451 ATLTRACE("PipeThread started\n");
452 bool * bRun
= (bool *)lpvParam
;
453 // The main loop creates an instance of the named pipe and
454 // then waits for a client to connect to it. When the client
455 // connects, a thread is created to handle communications
456 // with that client, and the loop is repeated.
463 hPipe
= CreateNamedPipe(
465 PIPE_ACCESS_DUPLEX
, // read/write access
466 PIPE_TYPE_MESSAGE
| // message type pipe
467 PIPE_READMODE_MESSAGE
| // message-read mode
468 PIPE_WAIT
, // blocking mode
469 PIPE_UNLIMITED_INSTANCES
, // max. instances
470 BUFSIZE
, // output buffer size
471 BUFSIZE
, // input buffer size
472 NMPWAIT_USE_DEFAULT_WAIT
, // client time-out
477 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
478 //DebugOutputLastError();
481 continue; // never leave the thread!
484 // Wait for the client to connect; if it succeeds,
485 // the function returns a nonzero value. If the function returns
486 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
487 fConnected
= ConnectNamedPipe(hPipe
, NULL
) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED
);
490 // Create a thread for this client.
491 CAutoGeneralHandle hInstanceThread
= CreateThread(
492 NULL
, // no security attribute
493 0, // default stack size
495 (HANDLE
) hPipe
, // thread parameter
497 &dwThreadId
); // returns thread ID
499 if (!hInstanceThread
)
501 //OutputDebugStringA("TSVNCache: Could not create Instance thread\n");
502 //DebugOutputLastError();
503 DisconnectNamedPipe(hPipe
);
504 // since we're now closing this thread, we also have to close the whole application!
505 // otherwise the thread is dead, but the app is still running, refusing new instances
506 // but no pipe will be available anymore.
507 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
510 // detach the handle, since we passed it to the thread
515 // The client could not connect, so close the pipe.
516 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
517 //DebugOutputLastError();
521 continue; // don't end the thread!
524 ATLTRACE("Pipe thread exited\n");
528 DWORD WINAPI
CommandWaitThread(LPVOID lpvParam
)
530 ATLTRACE("CommandWaitThread started\n");
531 bool * bRun
= (bool *)lpvParam
;
532 // The main loop creates an instance of the named pipe and
533 // then waits for a client to connect to it. When the client
534 // connects, a thread is created to handle communications
535 // with that client, and the loop is repeated.
542 hPipe
= CreateNamedPipe(
543 GetCacheCommandPipeName(),
544 PIPE_ACCESS_DUPLEX
, // read/write access
545 PIPE_TYPE_MESSAGE
| // message type pipe
546 PIPE_READMODE_MESSAGE
| // message-read mode
547 PIPE_WAIT
, // blocking mode
548 PIPE_UNLIMITED_INSTANCES
, // max. instances
549 BUFSIZE
, // output buffer size
550 BUFSIZE
, // input buffer size
551 NMPWAIT_USE_DEFAULT_WAIT
, // client time-out
556 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
557 //DebugOutputLastError();
560 continue; // never leave the thread!
563 // Wait for the client to connect; if it succeeds,
564 // the function returns a nonzero value. If the function returns
565 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
566 fConnected
= ConnectNamedPipe(hPipe
, NULL
) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED
);
569 // Create a thread for this client.
570 CAutoGeneralHandle hCommandThread
= CreateThread(
571 NULL
, // no security attribute
572 0, // default stack size
574 (HANDLE
) hPipe
, // thread parameter
576 &dwThreadId
); // returns thread ID
580 //OutputDebugStringA("TSVNCache: Could not create Command thread\n");
581 //DebugOutputLastError();
582 DisconnectNamedPipe(hPipe
);
584 // since we're now closing this thread, we also have to close the whole application!
585 // otherwise the thread is dead, but the app is still running, refusing new instances
586 // but no pipe will be available anymore.
587 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
590 // detach the handle, since we passed it to the thread
595 // The client could not connect, so close the pipe.
596 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
597 //DebugOutputLastError();
601 continue; // don't end the thread!
604 ATLTRACE("CommandWait thread exited\n");
608 DWORD WINAPI
InstanceThread(LPVOID lpvParam
)
610 ATLTRACE("InstanceThread started\n");
611 TGITCacheResponse response
;
612 DWORD cbBytesRead
, cbWritten
;
616 // The thread's parameter is a handle to a pipe instance.
619 InterlockedIncrement(&nThreadCount
);
622 // Read client requests from the pipe.
623 TGITCacheRequest request
;
625 hPipe
, // handle to pipe
626 &request
, // buffer to receive data
627 sizeof(request
), // size of buffer
628 &cbBytesRead
, // number of bytes read
629 NULL
); // not overlapped I/O
631 if (! fSuccess
|| cbBytesRead
== 0)
633 DisconnectNamedPipe(hPipe
);
634 ATLTRACE("Instance thread exited\n");
635 InterlockedDecrement(&nThreadCount
);
636 if (nThreadCount
== 0)
637 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
641 DWORD responseLength
;
642 GetAnswerToRequest(&request
, &response
, &responseLength
);
644 // Write the reply to the pipe.
645 fSuccess
= WriteFile(
646 hPipe
, // handle to pipe
647 &response
, // buffer to write from
648 responseLength
, // number of bytes to write
649 &cbWritten
, // number of bytes written
650 NULL
); // not overlapped I/O
652 if (! fSuccess
|| responseLength
!= cbWritten
)
654 DisconnectNamedPipe(hPipe
);
655 ATLTRACE("Instance thread exited\n");
656 InterlockedDecrement(&nThreadCount
);
657 if (nThreadCount
== 0)
658 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
663 // Flush the pipe to allow the client to read the pipe's contents
664 // before disconnecting. Then disconnect the pipe, and close the
665 // handle to this pipe instance.
667 FlushFileBuffers(hPipe
);
668 DisconnectNamedPipe(hPipe
);
669 ATLTRACE("Instance thread exited\n");
670 InterlockedDecrement(&nThreadCount
);
671 if (nThreadCount
== 0)
672 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
676 DWORD WINAPI
CommandThread(LPVOID lpvParam
)
678 ATLTRACE("CommandThread started\n");
683 // The thread's parameter is a handle to a pipe instance.
689 // Read client requests from the pipe.
690 TGITCacheCommand command
;
692 hPipe
, // handle to pipe
693 &command
, // buffer to receive data
694 sizeof(command
), // size of buffer
695 &cbBytesRead
, // number of bytes read
696 NULL
); // not overlapped I/O
698 if (! fSuccess
|| cbBytesRead
== 0)
700 DisconnectNamedPipe(hPipe
);
701 ATLTRACE("Command thread exited\n");
705 switch (command
.command
)
707 case TGITCACHECOMMAND_END
:
708 FlushFileBuffers(hPipe
);
709 DisconnectNamedPipe(hPipe
);
710 ATLTRACE("Command thread exited\n");
712 case TGITCACHECOMMAND_CRAWL
:
714 CTGitPath changedpath
;
715 changedpath
.SetFromWin(command
.path
, true);
716 // remove the path from our cache - that will 'invalidate' it.
718 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
719 CGitStatusCache::Instance().RemoveCacheForPath(changedpath
);
721 CGitStatusCache::Instance().AddFolderForCrawling(changedpath
.GetDirectory());
724 case TGITCACHECOMMAND_REFRESHALL
:
726 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
727 CGitStatusCache::Instance().Refresh();
730 case TGITCACHECOMMAND_RELEASE
:
732 CTGitPath changedpath
;
733 changedpath
.SetFromWin(command
.path
, true);
734 ATLTRACE(_T("release handle for path %s\n"), changedpath
.GetWinPath());
735 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
736 CGitStatusCache::Instance().CloseWatcherHandles(changedpath
);
737 CGitStatusCache::Instance().RemoveCacheForPath(changedpath
);
740 case TGITCACHECOMMAND_BLOCK
:
742 CTGitPath changedpath
;
743 changedpath
.SetFromWin(command
.path
);
744 ATLTRACE(_T("block path %s\n"), changedpath
.GetWinPath());
745 CGitStatusCache::Instance().BlockPath(changedpath
);
748 case TGITCACHECOMMAND_UNBLOCK
:
750 CTGitPath changedpath
;
751 changedpath
.SetFromWin(command
.path
);
752 ATLTRACE(_T("block path %s\n"), changedpath
.GetWinPath());
753 CGitStatusCache::Instance().UnBlockPath(changedpath
);
759 // Flush the pipe to allow the client to read the pipe's contents
760 // before disconnecting. Then disconnect the pipe, and close the
761 // handle to this pipe instance.
763 FlushFileBuffers(hPipe
);
764 DisconnectNamedPipe(hPipe
);
765 ATLTRACE("Command thread exited\n");