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"
36 #include "CreateProcessHelper.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 #if ENABLE_CRASHHANLDER
49 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 bool bRestart
= false;
59 NOTIFYICONDATA niData
;
62 TCHAR szCurrentCrawledPath
[MAX_CRAWLEDPATHS
][MAX_CRAWLEDPATHSLEN
];
63 int nCurrentCrawledpathIndex
= 0;
64 CComAutoCriticalSection critSec
;
66 volatile LONG nThreadCount
= 0;
68 #define PACKVERSION(major,minor) MAKELONG(minor,major)
70 void DebugOutputLastError()
74 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
75 FORMAT_MESSAGE_FROM_SYSTEM
|
76 FORMAT_MESSAGE_IGNORE_INSERTS
,
79 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
87 // Display the string.
88 OutputDebugStringA("TGitCache GetLastError(): ");
89 OutputDebugString((LPCTSTR
)lpMsgBuf
);
90 OutputDebugStringA("\n");
93 LocalFree( lpMsgBuf
);
96 void HandleCommandLine(LPSTR lpCmdLine
)
98 char *ptr
= strstr(lpCmdLine
, "/kill:");
101 DWORD pid
= (DWORD
)atoi(ptr
+ strlen("/kill:"));
102 HANDLE hProcess
= ::OpenProcess(PROCESS_TERMINATE
, FALSE
, pid
);
105 if (::WaitForSingleObject(hProcess
, 5000) != WAIT_OBJECT_0
)
107 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Killing previous TGitCache PID %d\n", pid
);
108 if (!::TerminateProcess(hProcess
, (UINT
)-1))
109 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Kill previous TGitCache PID %d failed\n", pid
);
110 ::WaitForSingleObject(hProcess
, 5000);
112 ::CloseHandle(hProcess
);
113 for (int i
= 0; i
< 5; i
++)
115 HANDLE hMutex
= ::OpenMutex(MUTEX_ALL_ACCESS
, FALSE
, GetCacheMutexName());
118 ::CloseHandle(hMutex
);
129 TCHAR exeName
[MAX_PATH
] = { 0 };
130 ::GetModuleFileName(nullptr, exeName
, _countof(exeName
));
131 TCHAR cmdLine
[20] = { 0 };
132 _stprintf_s(cmdLine
, _T(" /kill:%d"), GetCurrentProcessId());
133 if (!CCreateProcessHelper::CreateProcessDetached(exeName
, cmdLine
))
134 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Failed to start cache\n");
138 int __stdcall
WinMain(HINSTANCE hInstance
, HINSTANCE
/*hPrevInstance*/, LPSTR lpCmdLine
, int /*cmdShow*/)
140 SetDllDirectory(L
"");
141 HandleCommandLine(lpCmdLine
);
142 CAutoGeneralHandle hReloadProtection
= ::CreateMutex(NULL
, FALSE
, GetCacheMutexName());
144 if ((!hReloadProtection
) || (GetLastError() == ERROR_ALREADY_EXISTS
))
146 // An instance of TGitCache is already running
147 CTraceToOutputDebugString::Instance()(__FUNCTION__
": TGitCache ignoring restart\n");
151 CGitStatusCache::Create();
152 CGitStatusCache::Instance().Init();
154 SecureZeroMemory(szCurrentCrawledPath
, sizeof(szCurrentCrawledPath
));
158 TCHAR szWindowClass
[] = {TGIT_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(TGIT_CACHE_WINDOW_NAME
, TGIT_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
));
187 GetShellVersion(&dwMajor
, &dwMinor
);
188 DWORD dwVersion
= PACKVERSION(dwMajor
, dwMinor
);
189 if (dwVersion
>= PACKVERSION(6,0))
190 niData
.cbSize
= sizeof(NOTIFYICONDATA
);
191 else if (dwVersion
>= PACKVERSION(5,0))
192 niData
.cbSize
= NOTIFYICONDATA_V2_SIZE
;
194 niData
.cbSize
= NOTIFYICONDATA_V1_SIZE
;
196 niData
.uID
= TRAY_ID
; // own tray icon ID
198 niData
.uFlags
= NIF_ICON
|NIF_MESSAGE
;
202 (HICON
)LoadImage(hInstance
,
203 MAKEINTRESOURCE(IDI_TGITCACHE
),
205 GetSystemMetrics(SM_CXSMICON
),
206 GetSystemMetrics(SM_CYSMICON
),
209 // set the message to send
210 // note: the message value should be in the
211 // range of WM_APP through 0xBFFF
212 niData
.uCallbackMessage
= TRAY_CALLBACK
;
213 Shell_NotifyIcon(NIM_ADD
,&niData
);
215 if(niData
.hIcon
&& DestroyIcon(niData
.hIcon
))
219 // Create a thread which waits for incoming pipe connections
220 CAutoGeneralHandle hPipeThread
= CreateThread(
221 NULL
, // no security attribute
222 0, // default stack size
224 (LPVOID
) &bRun
, // thread parameter
226 &dwThreadId
); // returns thread ID
230 //OutputDebugStringA("TGitCache: Could not create pipe thread\n");
231 //DebugOutputLastError();
234 else hPipeThread
.CloseHandle();
236 // Create a thread which waits for incoming pipe connections
237 CAutoGeneralHandle hCommandWaitThread
= CreateThread(
238 NULL
, // no security attribute
239 0, // default stack size
241 (LPVOID
) &bRun
, // thread parameter
243 &dwThreadId
); // returns thread ID
245 if (!hCommandWaitThread
)
247 //OutputDebugStringA("TGitCache: Could not create command wait thread\n");
248 //DebugOutputLastError();
253 // loop to handle window messages.
256 BOOL bLoopRet
= GetMessage(&msg
, NULL
, 0, 0);
257 if ((bLoopRet
!= -1)&&(bLoopRet
!= 0))
259 DispatchMessage(&msg
);
265 Shell_NotifyIcon(NIM_DELETE
,&niData
);
266 CGitStatusCache::Destroy();
272 LRESULT CALLBACK
WndProc(HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
280 case WM_LBUTTONDBLCLK
:
281 if (IsWindowVisible(hWnd
))
282 ShowWindow(hWnd
, SW_HIDE
);
284 ShowWindow(hWnd
, SW_RESTORE
);
289 NOTIFYICONDATA SystemTray
;
290 sInfoTip
.Format(_T("TortoiseGit Overlay Icon Server\nCached Directories: %Id\nWatched paths: %d"),
291 CGitStatusCache::Instance().GetCacheSize(),
292 CGitStatusCache::Instance().GetNumberOfWatchedPaths());
294 SystemTray
.cbSize
= sizeof(NOTIFYICONDATA
);
295 SystemTray
.hWnd
= hTrayWnd
;
296 SystemTray
.uID
= TRAY_ID
;
297 SystemTray
.uFlags
= NIF_TIP
;
298 _tcscpy_s(SystemTray
.szTip
, sInfoTip
);
299 Shell_NotifyIcon(NIM_MODIFY
, &SystemTray
);
307 HMENU hMenu
= CreatePopupMenu();
310 bool enabled
= (DWORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"), GetSystemMetrics(SM_REMOTESESSION
) ? ShellCache::dll
: ShellCache::exe
) != ShellCache::none
;
311 InsertMenu(hMenu
, (UINT
)-1, MF_BYPOSITION
, TRAYPOP_ENABLE
, enabled
? _T("Disable Status Cache") : _T("Enable Status Cache"));
312 InsertMenu(hMenu
, (UINT
)-1, MF_BYPOSITION
, TRAYPOP_EXIT
, _T("Exit"));
313 SetForegroundWindow(hWnd
);
314 TrackPopupMenu(hMenu
, TPM_BOTTOMALIGN
, pt
.x
, pt
.y
, 0, hWnd
, NULL
);
325 HDC hdc
= BeginPaint(hWnd
, &ps
);
327 GetClientRect(hWnd
, &rect
);
328 // clear the background
329 HBRUSH background
= CreateSolidBrush(::GetSysColor(COLOR_WINDOW
));
330 HGDIOBJ oldbrush
= SelectObject(hdc
, background
);
331 FillRect(hdc
, &rect
, background
);
335 AutoLocker
print(critSec
);
336 GetTextExtentPoint32( hdc
, szCurrentCrawledPath
[0], (int)_tcslen(szCurrentCrawledPath
[0]), &fontsize
);
337 for (int i
=nCurrentCrawledpathIndex
; i
<MAX_CRAWLEDPATHS
; ++i
)
339 TextOut(hdc
, 0, line
*fontsize
.cy
, szCurrentCrawledPath
[i
], (int)_tcslen(szCurrentCrawledPath
[i
]));
342 for (int i
=0; i
<nCurrentCrawledpathIndex
; ++i
)
344 TextOut(hdc
, 0, line
*fontsize
.cy
, szCurrentCrawledPath
[i
], (int)_tcslen(szCurrentCrawledPath
[i
]));
348 SelectObject(hdc
,oldbrush
);
350 DeleteObject(background
);
356 WORD wmId
= LOWORD(wParam
);
362 CRegStdDWORD reg
= CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"), GetSystemMetrics(SM_REMOTESESSION
) ? ShellCache::dll
: ShellCache::exe
);
363 bool enabled
= (DWORD
)reg
!= ShellCache::none
;
364 reg
= enabled
? ShellCache::none
: ShellCache::exe
;
378 case WM_QUERYENDSESSION
:
380 CTraceToOutputDebugString::Instance()(__FUNCTION__
": WM_QUERYENDSESSION\n");
381 CAutoWriteWeakLock
writeLock(CGitStatusCache::Instance().GetGuard(), 200);
382 CGitStatusCache::Instance().Stop();
391 CTraceToOutputDebugString::Instance()(__FUNCTION__
": WM_CLOSE/DESTROY/ENDSESSION/QUIT\n");
392 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
393 CGitStatusCache::Instance().Stop();
394 CGitStatusCache::Instance().SaveCache();
395 if (message
!= WM_QUIT
)
401 case WM_DEVICECHANGE
:
403 DEV_BROADCAST_HDR
* phdr
= (DEV_BROADCAST_HDR
*)lParam
;
406 case DBT_CUSTOMEVENT
:
408 CTraceToOutputDebugString::Instance()(__FUNCTION__
": WM_DEVICECHANGE with DBT_CUSTOMEVENT\n");
409 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
411 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
412 if (IsEqualGUID(phandle
->dbch_eventguid
, GUID_IO_VOLUME_DISMOUNT
))
414 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Device to be dismounted\n");
415 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
416 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_handle
);
418 if (IsEqualGUID(phandle
->dbch_eventguid
, GUID_IO_VOLUME_LOCK
))
420 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Device lock event\n");
421 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
422 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_handle
);
427 case DBT_DEVICEREMOVEPENDING
:
428 case DBT_DEVICEQUERYREMOVE
:
429 case DBT_DEVICEREMOVECOMPLETE
:
430 CTraceToOutputDebugString::Instance()(__FUNCTION__
": WM_DEVICECHANGE with DBT_DEVICEREMOVEPENDING/DBT_DEVICEQUERYREMOVE/DBT_DEVICEREMOVECOMPLETE\n");
431 if (phdr
->dbch_devicetype
== DBT_DEVTYP_HANDLE
)
433 DEV_BROADCAST_HANDLE
* phandle
= (DEV_BROADCAST_HANDLE
*)lParam
;
434 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
435 CGitStatusCache::Instance().CloseWatcherHandles(phandle
->dbch_handle
);
437 else if (phdr
->dbch_devicetype
== DBT_DEVTYP_VOLUME
)
439 DEV_BROADCAST_VOLUME
* pVolume
= (DEV_BROADCAST_VOLUME
*)lParam
;
440 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
441 for (BYTE i
= 0; i
< 26; ++i
)
443 if (pVolume
->dbcv_unitmask
& (1 << i
))
445 TCHAR driveletter
= 'A' + i
;
446 CString drive
= CString(driveletter
);
448 CGitStatusCache::Instance().CloseWatcherHandles(CTGitPath(drive
));
454 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
455 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE
);
464 return DefWindowProc(hWnd
, message
, wParam
, lParam
);
467 //////////////////////////////////////////////////////////////////////////
469 VOID
GetAnswerToRequest(const TGITCacheRequest
* pRequest
, TGITCacheResponse
* pReply
, DWORD
* pResponseLength
)
472 *pResponseLength
= 0;
473 if(pRequest
->flags
& TGITCACHE_FLAGS_FOLDERISKNOWN
)
475 path
.SetFromWin(pRequest
->path
, !!(pRequest
->flags
& TGITCACHE_FLAGS_ISFOLDER
));
479 path
.SetFromWin(pRequest
->path
);
482 CAutoReadWeakLock
readLock(CGitStatusCache::Instance().GetGuard(), 2000);
483 if (readLock
.IsAcquired())
485 CGitStatusCache::Instance().GetStatusForPath(path
, pRequest
->flags
, false).BuildCacheResponse(*pReply
, *pResponseLength
);
489 CStatusCacheEntry entry
;
490 entry
.BuildCacheResponse(*pReply
, *pResponseLength
);
494 DWORD WINAPI
PipeThread(LPVOID lpvParam
)
496 CTraceToOutputDebugString::Instance()(__FUNCTION__
": PipeThread started\n");
497 bool * bRun
= (bool *)lpvParam
;
498 // The main loop creates an instance of the named pipe and
499 // then waits for a client to connect to it. When the client
500 // connects, a thread is created to handle communications
501 // with that client, and the loop is repeated.
508 hPipe
= CreateNamedPipe(
510 PIPE_ACCESS_DUPLEX
, // read/write access
511 PIPE_TYPE_MESSAGE
| // message type pipe
512 PIPE_READMODE_MESSAGE
| // message-read mode
513 PIPE_WAIT
, // blocking mode
514 PIPE_UNLIMITED_INSTANCES
, // max. instances
515 BUFSIZE
, // output buffer size
516 BUFSIZE
, // input buffer size
517 NMPWAIT_USE_DEFAULT_WAIT
, // client time-out
522 //OutputDebugStringA("TGitCache: CreatePipe failed\n");
523 //DebugOutputLastError();
526 continue; // never leave the thread!
529 // Wait for the client to connect; if it succeeds,
530 // the function returns a nonzero value. If the function returns
531 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
532 fConnected
= ConnectNamedPipe(hPipe
, NULL
) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED
);
535 // Create a thread for this client.
536 CAutoGeneralHandle hInstanceThread
= CreateThread(
537 NULL
, // no security attribute
538 0, // default stack size
540 (HANDLE
) hPipe
, // thread parameter
542 &dwThreadId
); // returns thread ID
544 if (!hInstanceThread
)
546 //OutputDebugStringA("TGitCache: Could not create Instance thread\n");
547 //DebugOutputLastError();
548 DisconnectNamedPipe(hPipe
);
549 // since we're now closing this thread, we also have to close the whole application!
550 // otherwise the thread is dead, but the app is still running, refusing new instances
551 // but no pipe will be available anymore.
552 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
555 // detach the handle, since we passed it to the thread
560 // The client could not connect, so close the pipe.
561 //OutputDebugStringA("TGitCache: ConnectNamedPipe failed\n");
562 //DebugOutputLastError();
566 continue; // don't end the thread!
569 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Pipe thread exited\n");
573 DWORD WINAPI
CommandWaitThread(LPVOID lpvParam
)
575 CTraceToOutputDebugString::Instance()(__FUNCTION__
": CommandWaitThread started\n");
576 bool * bRun
= (bool *)lpvParam
;
577 // The main loop creates an instance of the named pipe and
578 // then waits for a client to connect to it. When the client
579 // connects, a thread is created to handle communications
580 // with that client, and the loop is repeated.
587 hPipe
= CreateNamedPipe(
588 GetCacheCommandPipeName(),
589 PIPE_ACCESS_DUPLEX
, // read/write access
590 PIPE_TYPE_MESSAGE
| // message type pipe
591 PIPE_READMODE_MESSAGE
| // message-read mode
592 PIPE_WAIT
, // blocking mode
593 PIPE_UNLIMITED_INSTANCES
, // max. instances
594 BUFSIZE
, // output buffer size
595 BUFSIZE
, // input buffer size
596 NMPWAIT_USE_DEFAULT_WAIT
, // client time-out
601 //OutputDebugStringA("TGitCache: CreatePipe failed\n");
602 //DebugOutputLastError();
605 continue; // never leave the thread!
608 // Wait for the client to connect; if it succeeds,
609 // the function returns a nonzero value. If the function returns
610 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
611 fConnected
= ConnectNamedPipe(hPipe
, NULL
) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED
);
614 // Create a thread for this client.
615 CAutoGeneralHandle hCommandThread
= CreateThread(
616 NULL
, // no security attribute
617 0, // default stack size
619 (HANDLE
) hPipe
, // thread parameter
621 &dwThreadId
); // returns thread ID
625 //OutputDebugStringA("TGitCache: Could not create Command thread\n");
626 //DebugOutputLastError();
627 DisconnectNamedPipe(hPipe
);
629 // since we're now closing this thread, we also have to close the whole application!
630 // otherwise the thread is dead, but the app is still running, refusing new instances
631 // but no pipe will be available anymore.
632 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
635 // detach the handle, since we passed it to the thread
640 // The client could not connect, so close the pipe.
641 //OutputDebugStringA("TGitCache: ConnectNamedPipe failed\n");
642 //DebugOutputLastError();
646 continue; // don't end the thread!
649 CTraceToOutputDebugString::Instance()(__FUNCTION__
": CommandWait thread exited\n");
653 DWORD WINAPI
InstanceThread(LPVOID lpvParam
)
655 CTraceToOutputDebugString::Instance()(__FUNCTION__
": InstanceThread started\n");
656 TGITCacheResponse response
;
657 DWORD cbBytesRead
, cbWritten
;
660 // The thread's parameter is a handle to a pipe instance.
663 InterlockedIncrement(&nThreadCount
);
666 // Read client requests from the pipe.
667 TGITCacheRequest request
;
668 BOOL fSuccess
= ReadFile(
669 hPipe
, // handle to pipe
670 &request
, // buffer to receive data
671 sizeof(request
), // size of buffer
672 &cbBytesRead
, // number of bytes read
673 NULL
); // not overlapped I/O
675 if (! fSuccess
|| cbBytesRead
== 0)
677 DisconnectNamedPipe(hPipe
);
678 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Instance thread exited\n");
679 InterlockedDecrement(&nThreadCount
);
680 if (nThreadCount
== 0)
681 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
685 DWORD responseLength
;
686 GetAnswerToRequest(&request
, &response
, &responseLength
);
688 // Write the reply to the pipe.
689 fSuccess
= WriteFile(
690 hPipe
, // handle to pipe
691 &response
, // buffer to write from
692 responseLength
, // number of bytes to write
693 &cbWritten
, // number of bytes written
694 NULL
); // not overlapped I/O
696 if (! fSuccess
|| responseLength
!= cbWritten
)
698 DisconnectNamedPipe(hPipe
);
699 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Instance thread exited\n");
700 InterlockedDecrement(&nThreadCount
);
701 if (nThreadCount
== 0)
702 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
707 // Flush the pipe to allow the client to read the pipe's contents
708 // before disconnecting. Then disconnect the pipe, and close the
709 // handle to this pipe instance.
711 FlushFileBuffers(hPipe
);
712 DisconnectNamedPipe(hPipe
);
713 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Instance thread exited\n");
714 InterlockedDecrement(&nThreadCount
);
715 if (nThreadCount
== 0)
716 PostMessage(hWnd
, WM_CLOSE
, 0, 0);
720 DWORD WINAPI
CommandThread(LPVOID lpvParam
)
722 CTraceToOutputDebugString::Instance()(__FUNCTION__
": CommandThread started\n");
726 // The thread's parameter is a handle to a pipe instance.
732 // Read client requests from the pipe.
733 TGITCacheCommand command
;
734 BOOL fSuccess
= ReadFile(
735 hPipe
, // handle to pipe
736 &command
, // buffer to receive data
737 sizeof(command
), // size of buffer
738 &cbBytesRead
, // number of bytes read
739 NULL
); // not overlapped I/O
741 if (! fSuccess
|| cbBytesRead
== 0)
743 DisconnectNamedPipe(hPipe
);
744 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Command thread exited\n");
748 switch (command
.command
)
750 case TGITCACHECOMMAND_END
:
751 FlushFileBuffers(hPipe
);
752 DisconnectNamedPipe(hPipe
);
753 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Command thread exited\n");
755 case TGITCACHECOMMAND_CRAWL
:
757 CTGitPath changedpath
;
758 changedpath
.SetFromWin(command
.path
, true);
759 // remove the path from our cache - that will 'invalidate' it.
761 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
762 CGitStatusCache::Instance().RemoveCacheForPath(changedpath
);
764 CGitStatusCache::Instance().AddFolderForCrawling(changedpath
.GetDirectory());
767 case TGITCACHECOMMAND_REFRESHALL
:
769 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
770 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": refresh all\n"));
771 CGitStatusCache::Instance().Refresh();
774 case TGITCACHECOMMAND_RELEASE
:
776 CTGitPath changedpath
;
777 changedpath
.SetFromWin(command
.path
, true);
778 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": release handle for path %s\n"), changedpath
.GetWinPath());
779 CAutoWriteLock
writeLock(CGitStatusCache::Instance().GetGuard());
780 CGitStatusCache::Instance().CloseWatcherHandles(changedpath
);
781 CGitStatusCache::Instance().RemoveCacheForPath(changedpath
);
784 case TGITCACHECOMMAND_BLOCK
:
786 CTGitPath changedpath
;
787 changedpath
.SetFromWin(command
.path
);
788 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": block path %s\n"), changedpath
.GetWinPath());
789 CGitStatusCache::Instance().BlockPath(changedpath
);
792 case TGITCACHECOMMAND_UNBLOCK
:
794 CTGitPath changedpath
;
795 changedpath
.SetFromWin(command
.path
);
796 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": unblock path %s\n"), changedpath
.GetWinPath());
797 CGitStatusCache::Instance().UnBlockPath(changedpath
);
803 // Flush the pipe to allow the client to read the pipe's contents
804 // before disconnecting. Then disconnect the pipe, and close the
805 // handle to this pipe instance.
807 FlushFileBuffers(hPipe
);
808 DisconnectNamedPipe(hPipe
);
809 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Command thread exited\n");