Also use correct handle
[TortoiseGit.git] / src / TGitCache / TGITCache.cpp
blob110315007fef5f0be91c67adedfd3413ed1112cb
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.
21 #include "stdafx.h"
22 #include <ShellAPI.h>
23 #include "TGITCache.h"
24 #include "GitStatusCache.h"
25 #include "CacheInterface.h"
26 #include "Resource.h"
27 #include "registry.h"
28 #include "CrashReport.h"
29 #include "GitAdminDir.h"
30 #include <Dbt.h>
31 #include <InitGuid.h>
32 #include <Ioevent.h>
33 #include "..\version.h"
34 #include "SmartHandle.h"
35 #include "DllVersion.h"
37 #ifndef GET_X_LPARAM
38 #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
39 #endif
40 #ifndef GET_Y_LPARAM
41 #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
42 #endif
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);
49 #endif
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);
56 bool bRun = true;
57 NOTIFYICONDATA niData;
58 HWND hWnd;
59 HWND hTrayWnd;
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()
70 LPVOID lpMsgBuf;
71 if (!FormatMessage(
72 FORMAT_MESSAGE_ALLOCATE_BUFFER |
73 FORMAT_MESSAGE_FROM_SYSTEM |
74 FORMAT_MESSAGE_IGNORE_INSERTS,
75 NULL,
76 GetLastError(),
77 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
78 (LPTSTR) &lpMsgBuf,
80 NULL ))
82 return;
85 // Display the string.
86 OutputDebugStringA("TGitCache GetLastError(): ");
87 OutputDebugString((LPCTSTR)lpMsgBuf);
88 OutputDebugStringA("\n");
90 // Free the buffer.
91 LocalFree( lpMsgBuf );
94 int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*cmdShow*/)
96 SetDllDirectory(L"");
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");
103 return 0;
106 CGitStatusCache::Create();
107 CGitStatusCache::Instance().Init();
109 SecureZeroMemory(szCurrentCrawledPath, sizeof(szCurrentCrawledPath));
111 DWORD dwThreadId;
112 MSG msg;
113 TCHAR szWindowClass[] = {TGIT_CACHE_WINDOW_NAME};
115 // create a hidden window to receive window messages.
116 WNDCLASSEX wcex;
117 wcex.cbSize = sizeof(WNDCLASSEX);
118 wcex.style = CS_HREDRAW | CS_VREDRAW;
119 wcex.lpfnWndProc = (WNDPROC)WndProc;
120 wcex.cbClsExtra = 0;
121 wcex.cbWndExtra = 0;
122 wcex.hInstance = hInstance;
123 wcex.hIcon = 0;
124 wcex.hCursor = 0;
125 wcex.hbrBackground = 0;
126 wcex.lpszMenuName = NULL;
127 wcex.lpszClassName = szWindowClass;
128 wcex.hIconSm = 0;
129 RegisterClassEx(&wcex);
130 hWnd = CreateWindow(TGIT_CACHE_WINDOW_NAME, TGIT_CACHE_WINDOW_NAME, WS_CAPTION, 0, 0, 800, 300, NULL, 0, hInstance, 0);
131 hTrayWnd = hWnd;
132 if (hWnd == NULL)
134 return 0;
136 if (CRegStdDWORD(_T("Software\\TortoiseGit\\CacheTrayIcon"), FALSE)==TRUE)
138 SecureZeroMemory(&niData,sizeof(NOTIFYICONDATA));
140 DWORD dwMajor = 0;
141 DWORD dwMinor = 0;
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;
148 else
149 niData.cbSize = NOTIFYICONDATA_V1_SIZE;
151 niData.uID = TRAY_ID; // own tray icon ID
152 niData.hWnd = hWnd;
153 niData.uFlags = NIF_ICON|NIF_MESSAGE;
155 // load the icon
156 niData.hIcon =
157 (HICON)LoadImage(hInstance,
158 MAKEINTRESOURCE(IDI_TGITCACHE),
159 IMAGE_ICON,
160 GetSystemMetrics(SM_CXSMICON),
161 GetSystemMetrics(SM_CYSMICON),
162 LR_DEFAULTCOLOR);
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);
169 // free icon handle
170 if(niData.hIcon && DestroyIcon(niData.hIcon))
171 niData.hIcon = NULL;
174 // Create a thread which waits for incoming pipe connections
175 CAutoGeneralHandle hPipeThread = CreateThread(
176 NULL, // no security attribute
177 0, // default stack size
178 PipeThread,
179 (LPVOID) &bRun, // thread parameter
180 0, // not suspended
181 &dwThreadId); // returns thread ID
183 if (!hPipeThread)
185 //OutputDebugStringA("TGitCache: Could not create pipe thread\n");
186 //DebugOutputLastError();
187 return 0;
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
195 CommandWaitThread,
196 (LPVOID) &bRun, // thread parameter
197 0, // not suspended
198 &dwThreadId); // returns thread ID
200 if (!hCommandWaitThread)
202 //OutputDebugStringA("TGitCache: Could not create command wait thread\n");
203 //DebugOutputLastError();
204 return 0;
208 // loop to handle window messages.
209 while (bRun)
211 BOOL bLoopRet = GetMessage(&msg, NULL, 0, 0);
212 if ((bLoopRet != -1)&&(bLoopRet != 0))
214 DispatchMessage(&msg);
218 bRun = false;
220 Shell_NotifyIcon(NIM_DELETE,&niData);
221 CGitStatusCache::Destroy();
223 return 0;
226 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
228 switch (message)
230 case TRAY_CALLBACK:
232 switch(lParam)
234 case WM_LBUTTONDBLCLK:
235 if (IsWindowVisible(hWnd))
236 ShowWindow(hWnd, SW_HIDE);
237 else
238 ShowWindow(hWnd, SW_RESTORE);
239 break;
240 case WM_MOUSEMOVE:
242 CString sInfoTip;
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);
255 break;
256 case WM_RBUTTONUP:
257 case WM_CONTEXTMENU:
259 POINT pt;
260 GetCursorPos(&pt);
261 HMENU hMenu = CreatePopupMenu();
262 if(hMenu)
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);
267 DestroyMenu(hMenu);
270 break;
273 break;
274 case WM_PAINT:
276 PAINTSTRUCT ps;
277 HDC hdc = BeginPaint(hWnd, &ps);
278 RECT rect;
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);
285 int line = 0;
286 SIZE fontsize = {0};
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]));
292 ++line;
294 for (int i=0; i<nCurrentCrawledpathIndex; ++i)
296 TextOut(hdc, 0, line*fontsize.cy, szCurrentCrawledPath[i], (int)_tcslen(szCurrentCrawledPath[i]));
297 ++line;
300 SelectObject(hdc,oldbrush);
301 EndPaint(hWnd, &ps);
302 DeleteObject(background);
303 return 0L;
305 break;
306 case WM_COMMAND:
308 WORD wmId = LOWORD(wParam);
310 switch (wmId)
312 case TRAYPOP_EXIT:
313 DestroyWindow(hWnd);
314 break;
316 return 1;
318 case WM_QUERYENDSESSION:
320 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": WM_QUERYENDSESSION\n");
321 CAutoWriteWeakLock writeLock(CGitStatusCache::Instance().GetGuard(), 200);
322 CGitStatusCache::Instance().Stop();
323 return TRUE;
325 break;
326 case WM_CLOSE:
327 case WM_ENDSESSION:
328 case WM_DESTROY:
329 case WM_QUIT:
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)
336 PostQuitMessage(0);
337 bRun = false;
338 return 1;
340 break;
341 case WM_DEVICECHANGE:
343 DEV_BROADCAST_HDR * phdr = (DEV_BROADCAST_HDR*)lParam;
344 switch (wParam)
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);
366 break;
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);
387 drive += L":\\";
388 CGitStatusCache::Instance().CloseWatcherHandles(CTGitPath(drive));
392 else
394 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
395 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
397 break;
400 break;
401 default:
402 break;
404 return DefWindowProc(hWnd, message, wParam, lParam);
407 //////////////////////////////////////////////////////////////////////////
409 VOID GetAnswerToRequest(const TGITCacheRequest* pRequest, TGITCacheResponse* pReply, DWORD* pResponseLength)
411 CTGitPath path;
412 *pResponseLength = 0;
413 if(pRequest->flags & TGITCACHE_FLAGS_FOLDERISKNOWN)
415 path.SetFromWin(pRequest->path, !!(pRequest->flags & TGITCACHE_FLAGS_ISFOLDER));
417 else
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);
427 else
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.
442 DWORD dwThreadId;
443 BOOL fConnected;
444 CAutoFile hPipe;
446 while (*bRun)
448 hPipe = CreateNamedPipe(
449 GetCachePipeName(),
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
458 NULL); // NULL DACL
460 if (!hPipe)
462 //OutputDebugStringA("TGitCache: CreatePipe failed\n");
463 //DebugOutputLastError();
464 if (*bRun)
465 Sleep(200);
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);
473 if (fConnected)
475 // Create a thread for this client.
476 CAutoGeneralHandle hInstanceThread = CreateThread(
477 NULL, // no security attribute
478 0, // default stack size
479 InstanceThread,
480 (HANDLE) hPipe, // thread parameter
481 0, // not suspended
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);
493 return 1;
495 // detach the handle, since we passed it to the thread
496 hPipe.Detach();
498 else
500 // The client could not connect, so close the pipe.
501 //OutputDebugStringA("TGitCache: ConnectNamedPipe failed\n");
502 //DebugOutputLastError();
503 hPipe.CloseHandle();
504 if (*bRun)
505 Sleep(200);
506 continue; // don't end the thread!
509 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Pipe thread exited\n");
510 return 0;
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.
521 DWORD dwThreadId;
522 BOOL fConnected;
523 CAutoFile hPipe;
525 while (*bRun)
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
537 NULL); // NULL DACL
539 if (!hPipe)
541 //OutputDebugStringA("TGitCache: CreatePipe failed\n");
542 //DebugOutputLastError();
543 if (*bRun)
544 Sleep(200);
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);
552 if (fConnected)
554 // Create a thread for this client.
555 CAutoGeneralHandle hCommandThread = CreateThread(
556 NULL, // no security attribute
557 0, // default stack size
558 CommandThread,
559 (HANDLE) hPipe, // thread parameter
560 0, // not suspended
561 &dwThreadId); // returns thread ID
563 if (!hCommandThread)
565 //OutputDebugStringA("TGitCache: Could not create Command thread\n");
566 //DebugOutputLastError();
567 DisconnectNamedPipe(hPipe);
568 hPipe.CloseHandle();
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);
573 return 1;
575 // detach the handle, since we passed it to the thread
576 hPipe.Detach();
578 else
580 // The client could not connect, so close the pipe.
581 //OutputDebugStringA("TGitCache: ConnectNamedPipe failed\n");
582 //DebugOutputLastError();
583 hPipe.CloseHandle();
584 if (*bRun)
585 Sleep(200);
586 continue; // don't end the thread!
589 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": CommandWait thread exited\n");
590 return 0;
593 DWORD WINAPI InstanceThread(LPVOID lpvParam)
595 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": InstanceThread started\n");
596 TGITCacheResponse response;
597 DWORD cbBytesRead, cbWritten;
598 CAutoFile hPipe;
600 // The thread's parameter is a handle to a pipe instance.
602 hPipe = lpvParam;
603 InterlockedIncrement(&nThreadCount);
604 while (bRun)
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);
622 return 1;
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);
643 return 1;
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);
657 return 0;
660 DWORD WINAPI CommandThread(LPVOID lpvParam)
662 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": CommandThread started\n");
663 DWORD cbBytesRead;
664 CAutoFile hPipe;
666 // The thread's parameter is a handle to a pipe instance.
668 hPipe = lpvParam;
670 while (bRun)
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");
685 return 1;
688 switch (command.command)
690 case TGITCACHECOMMAND_END:
691 FlushFileBuffers(hPipe);
692 DisconnectNamedPipe(hPipe);
693 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Command thread exited\n");
694 return 0;
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());
706 break;
707 case TGITCACHECOMMAND_REFRESHALL:
709 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
710 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": refresh all\n"));
711 CGitStatusCache::Instance().Refresh();
713 break;
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);
723 break;
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);
731 break;
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);
739 break;
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");
750 return 0;