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, 2016-2018 - 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 "CreateProcessHelper.h"
37 #include "LoadIconEx.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 #if ENABLE_CRASHHANLDER
50 CCrashReportTGit
crasher(L
"TGitCache " _T(APP_X64_STRING
), TGIT_VERMAJOR
, TGIT_VERMINOR
, TGIT_VERMICRO
, TGIT_VERBUILD
, TGIT_VERDATE
);
53 DWORD WINAPI
InstanceThread(LPVOID
);
54 DWORD WINAPI
PipeThread(LPVOID
);
55 DWORD WINAPI
CommandWaitThread(LPVOID
);
56 DWORD WINAPI
CommandThread(LPVOID
);
57 LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
59 bool bRestart
= false;
60 NOTIFYICONDATA niData
;
63 TCHAR szCurrentCrawledPath
[MAX_CRAWLEDPATHS
][MAX_CRAWLEDPATHSLEN
];
64 int nCurrentCrawledpathIndex
= 0;
65 CComAutoCriticalSection critSec
;
67 // must put this before any global variables that auto free git objects,
68 // so this destructor is called after freeing git objects
74 git_libgit2_shutdown();
76 Shell_NotifyIcon(NIM_DELETE
, &niData
);
80 volatile LONG nThreadCount
= 0;
82 #define PACKVERSION(major,minor) MAKELONG(minor,major)
84 void HandleCommandLine(LPSTR lpCmdLine
)
86 char *ptr
= strstr(lpCmdLine
, "/kill:");
89 DWORD pid
= (DWORD
)atoi(ptr
+ strlen("/kill:"));
90 HANDLE hProcess
= ::OpenProcess(PROCESS_TERMINATE
, FALSE
, pid
);
93 if (::WaitForSingleObject(hProcess
, 5000) != WAIT_OBJECT_0
)
95 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Killing previous TGitCache PID %d\n", pid
);
96 if (!::TerminateProcess(hProcess
, (UINT
)-1))
97 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Kill previous TGitCache PID %d failed\n", pid
);
98 ::WaitForSingleObject(hProcess
, 5000);
100 ::CloseHandle(hProcess
);
101 for (int i
= 0; i
< 5; i
++)
103 HANDLE hMutex
= ::OpenMutex(MUTEX_ALL_ACCESS
, FALSE
, GetCacheMutexName());
106 ::CloseHandle(hMutex
);
117 TCHAR exeName
[MAX_PATH
] = { 0 };
118 ::GetModuleFileName(nullptr, exeName
, _countof(exeName
));
119 TCHAR cmdLine
[20] = { 0 };
120 swprintf_s(cmdLine
, L
" /kill:%d", GetCurrentProcessId());
121 if (!CCreateProcessHelper::CreateProcessDetached(exeName
, cmdLine
))
122 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Failed to start cache\n");
126 static void AddSystrayIcon()
128 if (CRegStdDWORD(L
"Software\\TortoiseGit\\CacheTrayIcon", FALSE
) != TRUE
)
131 SecureZeroMemory(&niData
, sizeof(NOTIFYICONDATA
));
132 niData
.cbSize
= sizeof(NOTIFYICONDATA
);
133 niData
.uID
= TRAY_ID
; // own tray icon ID
134 niData
.hWnd
= hWndHidden
;
135 niData
.uFlags
= NIF_ICON
| NIF_MESSAGE
;
138 niData
.hIcon
= LoadIconEx(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_TGITCACHE
));
140 // set the message to send
141 // note: the message value should be in the
142 // range of WM_APP through 0xBFFF
143 niData
.uCallbackMessage
= TRAY_CALLBACK
;
144 Shell_NotifyIcon(NIM_ADD
, &niData
);
146 if (niData
.hIcon
&& DestroyIcon(niData
.hIcon
))
147 niData
.hIcon
= nullptr;
150 int __stdcall
WinMain(HINSTANCE hInstance
, HINSTANCE
/*hPrevInstance*/, LPSTR lpCmdLine
, int /*cmdShow*/)
152 SetDllDirectory(L
"");
154 git_libgit2_opts(GIT_OPT_SET_WINDOWS_SHAREMODE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
);
155 HandleCommandLine(lpCmdLine
);
156 CAutoGeneralHandle hReloadProtection
= ::CreateMutex(nullptr, FALSE
, GetCacheMutexName());
158 if ((!hReloadProtection
) || (GetLastError() == ERROR_ALREADY_EXISTS
))
160 // An instance of TGitCache is already running
161 CTraceToOutputDebugString::Instance()(__FUNCTION__
": TGitCache ignoring restart\n");
165 CGitStatusCache::Create();
166 CGitStatusCache::Instance().Init();
168 SecureZeroMemory(szCurrentCrawledPath
, sizeof(szCurrentCrawledPath
));
172 TCHAR szWindowClass
[] = {TGIT_CACHE_WINDOW_NAME
};
174 // create a hidden window to receive window messages.
175 WNDCLASSEX wcex
= { 0 };
176 wcex
.cbSize
= sizeof(WNDCLASSEX
);
177 wcex
.style
= CS_HREDRAW
| CS_VREDRAW
;
178 wcex
.lpfnWndProc
= (WNDPROC
)WndProc
;
179 wcex
.hInstance
= hInstance
;
180 wcex
.lpszClassName
= szWindowClass
;
181 RegisterClassEx(&wcex
);
182 hWndHidden
= CreateWindow(TGIT_CACHE_WINDOW_NAME
, TGIT_CACHE_WINDOW_NAME
, WS_CAPTION
, 0, 0, 800, 300, nullptr, 0, hInstance
, 0);
183 hTrayWnd
= hWndHidden
;
187 // Create a thread which waits for incoming pipe connections
188 CAutoGeneralHandle hPipeThread
= CreateThread(
189 nullptr, // no security attribute
190 0, // default stack size
192 (LPVOID
) &bRun
, // thread parameter
194 &dwThreadId
); // returns thread ID
198 else hPipeThread
.CloseHandle();
200 // Create a thread which waits for incoming pipe connections
201 CAutoGeneralHandle hCommandWaitThread
= CreateThread(
202 nullptr, // no security attribute
203 0, // default stack size
205 (LPVOID
) &bRun
, // thread parameter
207 &dwThreadId
); // returns thread ID
209 if (!hCommandWaitThread
)
214 // loop to handle window messages.
217 BOOL bLoopRet
= GetMessage(&msg
, nullptr, 0, 0);
218 if ((bLoopRet
!= -1)&&(bLoopRet
!= 0))
219 DispatchMessage(&msg
);
224 CGitStatusCache::Destroy();
229 LRESULT CALLBACK
WndProc(HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
231 static UINT s_uTaskbarRestart
= RegisterWindowMessage(L
"TaskbarCreated");
238 case WM_LBUTTONDBLCLK
:
239 if (IsWindowVisible(hWnd
))
240 ShowWindow(hWnd
, SW_HIDE
);
242 ShowWindow(hWnd
, SW_RESTORE
);
247 NOTIFYICONDATA SystemTray
;
248 sInfoTip
.Format(L
"TortoiseGit Overlay Icon Server\nCached Directories: %Id\nWatched paths: %d",
249 CGitStatusCache::Instance().GetCacheSize(),
250 CGitStatusCache::Instance().GetNumberOfWatchedPaths());
252 SystemTray
.cbSize
= sizeof(NOTIFYICONDATA
);
253 SystemTray
.hWnd
= hTrayWnd
;
254 SystemTray
.uID
= TRAY_ID
;
255 SystemTray
.uFlags
= NIF_TIP
;
256 wcscpy_s(SystemTray
.szTip
, sInfoTip
);
257 Shell_NotifyIcon(NIM_MODIFY
, &SystemTray
);
265 HMENU hMenu
= CreatePopupMenu();
268 bool enabled
= (DWORD
)CRegStdDWORD(L
"Software\\TortoiseGit\\CacheType", GetSystemMetrics(SM_REMOTESESSION
) ? ShellCache::dll
: ShellCache::exe
) != ShellCache::none
;
269 InsertMenu(hMenu
, (UINT
)-1, MF_BYPOSITION
, TRAYPOP_ENABLE
, enabled
? L
"Disable Status Cache" : L
"Enable Status Cache");
270 InsertMenu(hMenu
, (UINT
)-1, MF_BYPOSITION
, TRAYPOP_EXIT
, L
"Exit");
271 SetForegroundWindow(hWnd
);
272 TrackPopupMenu(hMenu
, TPM_BOTTOMALIGN
, pt
.x
, pt
.y
, 0, hWnd
, nullptr);
283 HDC hdc
= BeginPaint(hWnd
, &ps
);
285 GetClientRect(hWnd
, &rect
);
286 // clear the background
287 HBRUSH background
= CreateSolidBrush(::GetSysColor(COLOR_WINDOW
));
288 HGDIOBJ oldbrush
= SelectObject(hdc
, background
);
289 FillRect(hdc
, &rect
, background
);
293 AutoLocker
print(critSec
);
294 GetTextExtentPoint32(hdc
, szCurrentCrawledPath
[0], (int)wcslen(szCurrentCrawledPath
[0]), &fontsize
);
295 for (int i
=nCurrentCrawledpathIndex
; i
<MAX_CRAWLEDPATHS
; ++i
)
297 TextOut(hdc
, 0, line
*fontsize
.cy
, szCurrentCrawledPath
[i
], (int)wcslen(szCurrentCrawledPath
[i
]));
300 for (int i
=0; i
<nCurrentCrawledpathIndex
; ++i
)
302 TextOut(hdc
, 0, line
*fontsize
.cy
, szCurrentCrawledPath
[i
], (int)wcslen(szCurrentCrawledPath
[i
]));
306 SelectObject(hdc
,oldbrush
);
308 DeleteObject(background
);
314 WORD wmId
= LOWORD(wParam
);
320 CRegStdDWORD reg
= CRegStdDWORD(L
"Software\\TortoiseGit\\CacheType", GetSystemMetrics(SM_REMOTESESSION
) ? ShellCache::dll
: ShellCache::exe
);
321 bool enabled
= (DWORD
)reg
!= ShellCache::none
;
322 reg
= enabled
? ShellCache::none
: ShellCache::exe
;
336 case WM_QUERYENDSESSION
:
338 CTraceToOutputDebugString::Instance()(__FUNCTION__
": WM_QUERYENDSESSION\n");
339 CAutoWriteWeakLock
writeLock(CGitStatusCache::Instance().GetGuard(), 200);
340 CGitStatusCache::Instance().Stop();
349 CTraceToOutputDebugString::Instance()(__FUNCTION__
": WM_CLOSE/DESTROY/ENDSESSION/QUIT\n");
352 niData
.hIcon
= LoadIconEx(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_TGITCACHE_STOPPING
));
353 Shell_NotifyIcon(NIM_MODIFY
, &niData
);
355 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
356 CGitStatusCache::Instance().Stop();
357 CGitStatusCache::Instance().SaveCache();
358 if (message
!= WM_QUIT
)
364 case WM_DEVICECHANGE
:
366 DEV_BROADCAST_HDR
* phdr
= (DEV_BROADCAST_HDR
*)lParam
;
369 case DBT_CUSTOMEVENT
:
371 CTraceToOutputDebugString::Instance()(__FUNCTION__
": WM_DEVICECHANGE with DBT_CUSTOMEVENT\n");
372 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
374 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
375 if (IsEqualGUID(phandle
->dbch_eventguid
, GUID_IO_VOLUME_DISMOUNT
))
377 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Device to be dismounted\n");
378 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
379 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_handle
);
381 if (IsEqualGUID(phandle
->dbch_eventguid
, GUID_IO_VOLUME_LOCK
))
383 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Device lock event\n");
384 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
385 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_handle
);
390 case DBT_DEVICEREMOVEPENDING
:
391 case DBT_DEVICEQUERYREMOVE
:
392 case DBT_DEVICEREMOVECOMPLETE
:
393 CTraceToOutputDebugString::Instance()(__FUNCTION__
": WM_DEVICECHANGE with DBT_DEVICEREMOVEPENDING/DBT_DEVICEQUERYREMOVE/DBT_DEVICEREMOVECOMPLETE\n");
394 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
396 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
397 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
398 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_handle
);
400 else if (phdr
->dbch_devicetype
== DBT_DEVTYP_VOLUME
)
402 DEV_BROADCAST_VOLUME
* pVolume
= (DEV_BROADCAST_VOLUME
*)lParam
;
403 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
404 for (BYTE i
= 0; i
< 26; ++i
)
406 if (pVolume
->dbcv_unitmask
& (1 << i
))
408 TCHAR driveletter
= 'A' + i
;
409 CString drive
= CString(driveletter
);
411 CGitStatusCache::Instance().CloseWatcherHandles(CTGitPath(drive
));
417 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
418 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
425 if (message
== s_uTaskbarRestart
)
429 return DefWindowProc(hWnd
, message
, wParam
, lParam
);
432 //////////////////////////////////////////////////////////////////////////
434 VOID
GetAnswerToRequest(const TGITCacheRequest
* pRequest
, TGITCacheResponse
* pReply
, DWORD
* pResponseLength
)
437 *pResponseLength
= 0;
438 if(pRequest
->flags
& TGITCACHE_FLAGS_FOLDERISKNOWN
)
439 path
.SetFromWin(pRequest
->path
, !!(pRequest
->flags
& TGITCACHE_FLAGS_ISFOLDER
));
441 path
.SetFromWin(pRequest
->path
);
445 CStatusCacheEntry entry
;
446 entry
.BuildCacheResponse(*pReply
, *pResponseLength
);
450 CAutoReadWeakLock
readLock(CGitStatusCache::Instance().GetGuard(), 2000);
451 if (readLock
.IsAcquired())
453 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": app asked for status of %s\n", pRequest
->path
);
454 CGitStatusCache::Instance().GetStatusForPath(path
, pRequest
->flags
).BuildCacheResponse(*pReply
, *pResponseLength
);
458 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": timeout for asked status of %s\n", pRequest
->path
);
459 CStatusCacheEntry entry
;
460 entry
.BuildCacheResponse(*pReply
, *pResponseLength
);
464 DWORD WINAPI
PipeThread(LPVOID lpvParam
)
466 CTraceToOutputDebugString::Instance()(__FUNCTION__
": PipeThread started\n");
467 bool* bThreadRun
= (bool*)lpvParam
;
468 // The main loop creates an instance of the named pipe and
469 // then waits for a client to connect to it. When the client
470 // connects, a thread is created to handle communications
471 // with that client, and the loop is repeated.
478 hPipe
= CreateNamedPipe(
480 PIPE_ACCESS_DUPLEX
, // read/write access
481 PIPE_TYPE_MESSAGE
| // message type pipe
482 PIPE_READMODE_MESSAGE
| // message-read mode
483 PIPE_WAIT
, // blocking mode
484 PIPE_UNLIMITED_INSTANCES
, // max. instances
485 BUFSIZE
, // output buffer size
486 BUFSIZE
, // input buffer size
487 NMPWAIT_USE_DEFAULT_WAIT
, // client time-out
488 nullptr); // nullptr DACL
494 continue; // never leave the thread!
497 // Wait for the client to connect; if it succeeds,
498 // the function returns a nonzero value. If the function returns
499 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
500 fConnected
= ConnectNamedPipe(hPipe
, nullptr) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED
);
503 // Create a thread for this client.
504 CAutoGeneralHandle hInstanceThread
= CreateThread(
505 nullptr, // no security attribute
506 0, // default stack size
508 (HANDLE
) hPipe
, // thread parameter
510 &dwThreadId
); // returns thread ID
512 if (!hInstanceThread
)
514 DisconnectNamedPipe(hPipe
);
515 // since we're now closing this thread, we also have to close the whole application!
516 // otherwise the thread is dead, but the app is still running, refusing new instances
517 // but no pipe will be available anymore.
518 PostMessage(hWndHidden
, WM_CLOSE
, 0, 0);
521 // detach the handle, since we passed it to the thread
526 // The client could not connect, so close the pipe.
530 continue; // don't end the thread!
533 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Pipe thread exited\n");
537 DWORD WINAPI
CommandWaitThread(LPVOID lpvParam
)
539 CTraceToOutputDebugString::Instance()(__FUNCTION__
": CommandWaitThread started\n");
540 bool* bThreadRun
= (bool*)lpvParam
;
541 // The main loop creates an instance of the named pipe and
542 // then waits for a client to connect to it. When the client
543 // connects, a thread is created to handle communications
544 // with that client, and the loop is repeated.
551 hPipe
= CreateNamedPipe(
552 GetCacheCommandPipeName(),
553 PIPE_ACCESS_DUPLEX
, // read/write access
554 PIPE_TYPE_MESSAGE
| // message type pipe
555 PIPE_READMODE_MESSAGE
| // message-read mode
556 PIPE_WAIT
, // blocking mode
557 PIPE_UNLIMITED_INSTANCES
, // max. instances
558 BUFSIZE
, // output buffer size
559 BUFSIZE
, // input buffer size
560 NMPWAIT_USE_DEFAULT_WAIT
, // client time-out
561 nullptr); // nullptr DACL
567 continue; // never leave the thread!
570 // Wait for the client to connect; if it succeeds,
571 // the function returns a nonzero value. If the function returns
572 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
573 fConnected
= ConnectNamedPipe(hPipe
, nullptr) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED
);
576 // Create a thread for this client.
577 CAutoGeneralHandle hCommandThread
= CreateThread(
578 nullptr, // no security attribute
579 0, // default stack size
581 (HANDLE
) hPipe
, // thread parameter
583 &dwThreadId
); // returns thread ID
587 DisconnectNamedPipe(hPipe
);
589 // since we're now closing this thread, we also have to close the whole application!
590 // otherwise the thread is dead, but the app is still running, refusing new instances
591 // but no pipe will be available anymore.
592 PostMessage(hWndHidden
, WM_CLOSE
, 0, 0);
595 // detach the handle, since we passed it to the thread
600 // The client could not connect, so close the pipe.
604 continue; // don't end the thread!
607 CTraceToOutputDebugString::Instance()(__FUNCTION__
": CommandWait thread exited\n");
611 DWORD WINAPI
InstanceThread(LPVOID lpvParam
)
613 CTraceToOutputDebugString::Instance()(__FUNCTION__
": InstanceThread started\n");
614 TGITCacheResponse response
;
615 DWORD cbBytesRead
, cbWritten
;
617 // The thread's parameter is a handle to a pipe instance.
618 CAutoFile
hPipe(std::move(lpvParam
));
620 InterlockedIncrement(&nThreadCount
);
623 // Read client requests from the pipe.
624 TGITCacheRequest request
;
625 BOOL fSuccess
= ReadFile(
626 hPipe
, // handle to pipe
627 &request
, // buffer to receive data
628 sizeof(request
), // size of buffer
629 &cbBytesRead
, // number of bytes read
630 nullptr); // not overlapped I/O
632 if (! fSuccess
|| cbBytesRead
== 0)
634 DisconnectNamedPipe(hPipe
);
635 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Instance thread exited\n");
636 if (InterlockedDecrement(&nThreadCount
) == 0)
637 PostMessage(hWndHidden
, 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 nullptr); // not overlapped I/O
652 if (! fSuccess
|| responseLength
!= cbWritten
)
654 DisconnectNamedPipe(hPipe
);
655 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Instance thread exited\n");
656 if (InterlockedDecrement(&nThreadCount
) == 0)
657 PostMessage(hWndHidden
, WM_CLOSE
, 0, 0);
662 // Flush the pipe to allow the client to read the pipe's contents
663 // before disconnecting. Then disconnect the pipe, and close the
664 // handle to this pipe instance.
666 FlushFileBuffers(hPipe
);
667 DisconnectNamedPipe(hPipe
);
668 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Instance thread exited\n");
669 if (InterlockedDecrement(&nThreadCount
) == 0)
670 PostMessage(hWndHidden
, WM_CLOSE
, 0, 0);
674 DWORD WINAPI
CommandThread(LPVOID lpvParam
)
676 CTraceToOutputDebugString::Instance()(__FUNCTION__
": CommandThread started\n");
679 // The thread's parameter is a handle to a pipe instance.
680 CAutoFile
hPipe(std::move(lpvParam
));
684 // Read client requests from the pipe.
685 TGITCacheCommand command
;
686 BOOL fSuccess
= ReadFile(
687 hPipe
, // handle to pipe
688 &command
, // buffer to receive data
689 sizeof(command
), // size of buffer
690 &cbBytesRead
, // number of bytes read
691 nullptr); // not overlapped I/O
693 if (! fSuccess
|| cbBytesRead
== 0)
695 DisconnectNamedPipe(hPipe
);
696 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Command thread exited\n");
700 switch (command
.command
)
702 case TGITCACHECOMMAND_END
:
703 FlushFileBuffers(hPipe
);
704 DisconnectNamedPipe(hPipe
);
705 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Command thread exited\n");
707 case TGITCACHECOMMAND_CRAWL
:
709 CTGitPath changedpath
;
710 changedpath
.SetFromWin(command
.path
, true);
711 // remove the path from our cache - that will 'invalidate' it.
713 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
714 CGitStatusCache::Instance().RemoveCacheForPath(changedpath
);
716 CGitStatusCache::Instance().AddFolderForCrawling(changedpath
.GetDirectory());
719 case TGITCACHECOMMAND_REFRESHALL
:
721 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
722 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": refresh all\n");
723 CGitStatusCache::Instance().Refresh();
726 case TGITCACHECOMMAND_RELEASE
:
728 CTGitPath changedpath
;
729 changedpath
.SetFromWin(command
.path
, true);
730 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": release handle for path %s\n", changedpath
.GetWinPath());
731 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
732 CGitStatusCache::Instance().CloseWatcherHandles(changedpath
);
733 CGitStatusCache::Instance().RemoveCacheForPath(changedpath
);
734 auto cachedDir
= CGitStatusCache::Instance().GetDirectoryCacheEntryNoCreate(changedpath
.GetContainingDirectory());
736 cachedDir
->Invalidate();
739 case TGITCACHECOMMAND_BLOCK
:
741 CTGitPath changedpath
;
742 changedpath
.SetFromWin(command
.path
);
743 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": block path %s\n", changedpath
.GetWinPath());
744 CGitStatusCache::Instance().BlockPath(changedpath
);
747 case TGITCACHECOMMAND_UNBLOCK
:
749 CTGitPath changedpath
;
750 changedpath
.SetFromWin(command
.path
);
751 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": unblock path %s\n", changedpath
.GetWinPath());
752 CGitStatusCache::Instance().UnBlockPath(changedpath
);
758 // Flush the pipe to allow the client to read the pipe's contents
759 // before disconnecting. Then disconnect the pipe, and close the
760 // handle to this pipe instance.
762 FlushFileBuffers(hPipe
);
763 DisconnectNamedPipe(hPipe
);
764 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Command thread exited\n");