Use TGIT_VERDATE instead of __DATE__ in CrashReport.h
[TortoiseGit.git] / src / TGitCache / TGITCache.cpp
blob66aebb69905b83110da645df31fe44b75b892b59
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.
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 "svn_dso.h"
35 #include "SmartHandle.h"
36 #include "DllVersion.h"
38 #include <ShellAPI.h>
40 #ifndef GET_X_LPARAM
41 #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
42 #endif
43 #ifndef GET_Y_LPARAM
44 #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
45 #endif
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);
57 bool bRun = true;
58 NOTIFYICONDATA niData;
59 HWND hWnd;
60 HWND hTrayWnd;
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()
71 LPVOID lpMsgBuf;
72 if (!FormatMessage(
73 FORMAT_MESSAGE_ALLOCATE_BUFFER |
74 FORMAT_MESSAGE_FROM_SYSTEM |
75 FORMAT_MESSAGE_IGNORE_INSERTS,
76 NULL,
77 GetLastError(),
78 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
79 (LPTSTR) &lpMsgBuf,
81 NULL ))
83 return;
86 // Display the string.
87 OutputDebugStringA("TGitCache GetLastError(): ");
88 OutputDebugString((LPCTSTR)lpMsgBuf);
89 OutputDebugStringA("\n");
91 // Free the buffer.
92 LocalFree( lpMsgBuf );
95 int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*cmdShow*/)
97 SetDllDirectory(L"");
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");
104 return 0;
107 CGitStatusCache::Create();
108 CGitStatusCache::Instance().Init();
110 SecureZeroMemory(szCurrentCrawledPath, sizeof(szCurrentCrawledPath));
112 DWORD dwThreadId;
113 MSG msg;
114 TCHAR szWindowClass[] = {TGIT_CACHE_WINDOW_NAME};
116 // create a hidden window to receive window messages.
117 WNDCLASSEX wcex;
118 wcex.cbSize = sizeof(WNDCLASSEX);
119 wcex.style = CS_HREDRAW | CS_VREDRAW;
120 wcex.lpfnWndProc = (WNDPROC)WndProc;
121 wcex.cbClsExtra = 0;
122 wcex.cbWndExtra = 0;
123 wcex.hInstance = hInstance;
124 wcex.hIcon = 0;
125 wcex.hCursor = 0;
126 wcex.hbrBackground = 0;
127 wcex.lpszMenuName = NULL;
128 wcex.lpszClassName = szWindowClass;
129 wcex.hIconSm = 0;
130 RegisterClassEx(&wcex);
131 hWnd = CreateWindow(TGIT_CACHE_WINDOW_NAME, TGIT_CACHE_WINDOW_NAME, WS_CAPTION, 0, 0, 800, 300, NULL, 0, hInstance, 0);
132 hTrayWnd = hWnd;
133 if (hWnd == NULL)
135 return 0;
137 if (CRegStdDWORD(_T("Software\\TortoiseGit\\CacheTrayIcon"), FALSE)==TRUE)
139 SecureZeroMemory(&niData,sizeof(NOTIFYICONDATA));
141 DWORD dwMajor = 0;
142 DWORD dwMinor = 0;
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;
149 else
150 niData.cbSize = NOTIFYICONDATA_V1_SIZE;
152 niData.uID = TRAY_ID; // own tray icon ID
153 niData.hWnd = hWnd;
154 niData.uFlags = NIF_ICON|NIF_MESSAGE;
156 // load the icon
157 niData.hIcon =
158 (HICON)LoadImage(hInstance,
159 MAKEINTRESOURCE(IDI_TGITCACHE),
160 IMAGE_ICON,
161 GetSystemMetrics(SM_CXSMICON),
162 GetSystemMetrics(SM_CYSMICON),
163 LR_DEFAULTCOLOR);
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);
170 // free icon handle
171 if(niData.hIcon && DestroyIcon(niData.hIcon))
172 niData.hIcon = NULL;
175 // Create a thread which waits for incoming pipe connections
176 CAutoGeneralHandle hPipeThread = CreateThread(
177 NULL, // no security attribute
178 0, // default stack size
179 PipeThread,
180 (LPVOID) &bRun, // thread parameter
181 0, // not suspended
182 &dwThreadId); // returns thread ID
184 if (!hPipeThread)
186 //OutputDebugStringA("TSVNCache: Could not create pipe thread\n");
187 //DebugOutputLastError();
188 return 0;
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
196 CommandWaitThread,
197 (LPVOID) &bRun, // thread parameter
198 0, // not suspended
199 &dwThreadId); // returns thread ID
201 if (!hCommandWaitThread)
203 //OutputDebugStringA("TSVNCache: Could not create command wait thread\n");
204 //DebugOutputLastError();
205 return 0;
209 // loop to handle window messages.
210 BOOL bLoopRet;
211 while (bRun)
213 bLoopRet = GetMessage(&msg, NULL, 0, 0);
214 if ((bLoopRet != -1)&&(bLoopRet != 0))
216 DispatchMessage(&msg);
220 bRun = false;
222 Shell_NotifyIcon(NIM_DELETE,&niData);
223 CGitStatusCache::Destroy();
225 return 0;
228 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
230 switch (message)
232 case TRAY_CALLBACK:
234 switch(lParam)
236 case WM_LBUTTONDBLCLK:
237 if (IsWindowVisible(hWnd))
238 ShowWindow(hWnd, SW_HIDE);
239 else
240 ShowWindow(hWnd, SW_RESTORE);
241 break;
242 case WM_MOUSEMOVE:
244 CString sInfoTip;
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);
257 break;
258 case WM_RBUTTONUP:
259 case WM_CONTEXTMENU:
261 POINT pt;
262 DWORD ptW = GetMessagePos();
263 pt.x = GET_X_LPARAM(ptW);
264 pt.y = GET_Y_LPARAM(ptW);
265 HMENU hMenu = CreatePopupMenu();
266 if(hMenu)
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);
271 DestroyMenu(hMenu);
274 break;
277 break;
278 case WM_PAINT:
280 PAINTSTRUCT ps;
281 HDC hdc = BeginPaint(hWnd, &ps);
282 RECT rect;
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);
289 int line = 0;
290 SIZE fontsize = {0};
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]));
296 line++;
298 for (int i=0; i<nCurrentCrawledpathIndex; ++i)
300 TextOut(hdc, 0, line*fontsize.cy, szCurrentCrawledPath[i], (int)_tcslen(szCurrentCrawledPath[i]));
301 line++;
304 SelectObject(hdc,oldbrush);
305 EndPaint(hWnd, &ps);
306 DeleteObject(background);
307 return 0L;
309 break;
310 case WM_COMMAND:
312 WORD wmId = LOWORD(wParam);
314 switch (wmId)
316 case TRAYPOP_EXIT:
317 DestroyWindow(hWnd);
318 break;
320 return 1;
322 case WM_QUERYENDSESSION:
324 ATLTRACE("WM_QUERYENDSESSION\n");
325 CAutoWriteWeakLock writeLock(CGitStatusCache::Instance().GetGuard(), 200);
326 CGitStatusCache::Instance().Stop();
327 return TRUE;
329 break;
330 case WM_CLOSE:
331 case WM_ENDSESSION:
332 case WM_DESTROY:
333 case WM_QUIT:
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)
340 PostQuitMessage(0);
341 bRun = false;
342 return 1;
344 break;
345 case WM_DEVICECHANGE:
347 DEV_BROADCAST_HDR * phdr = (DEV_BROADCAST_HDR*)lParam;
348 switch (wParam)
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);
370 break;
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);
379 else
381 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
382 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
384 break;
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);
393 else
395 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
396 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
398 break;
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);
407 else
409 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
410 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
412 break;
415 break;
416 default:
417 break;
419 return DefWindowProc(hWnd, message, wParam, lParam);
422 //////////////////////////////////////////////////////////////////////////
424 VOID GetAnswerToRequest(const TGITCacheRequest* pRequest, TGITCacheResponse* pReply, DWORD* pResponseLength)
426 CTGitPath path;
427 *pResponseLength = 0;
428 if(pRequest->flags & TGITCACHE_FLAGS_FOLDERISKNOWN)
430 path.SetFromWin(pRequest->path, !!(pRequest->flags & TGITCACHE_FLAGS_ISFOLDER));
432 else
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);
442 else
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.
457 DWORD dwThreadId;
458 BOOL fConnected;
459 CAutoFile hPipe;
461 while (*bRun)
463 hPipe = CreateNamedPipe(
464 GetCachePipeName(),
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
473 NULL); // NULL DACL
475 if (!hPipe)
477 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
478 //DebugOutputLastError();
479 if (*bRun)
480 Sleep(200);
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);
488 if (fConnected)
490 // Create a thread for this client.
491 CAutoGeneralHandle hInstanceThread = CreateThread(
492 NULL, // no security attribute
493 0, // default stack size
494 InstanceThread,
495 (HANDLE) hPipe, // thread parameter
496 0, // not suspended
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);
508 return 1;
510 // detach the handle, since we passed it to the thread
511 hPipe.Detach();
513 else
515 // The client could not connect, so close the pipe.
516 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
517 //DebugOutputLastError();
518 hPipe.CloseHandle();
519 if (*bRun)
520 Sleep(200);
521 continue; // don't end the thread!
524 ATLTRACE("Pipe thread exited\n");
525 return 0;
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.
536 DWORD dwThreadId;
537 BOOL fConnected;
538 CAutoFile hPipe;
540 while (*bRun)
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
552 NULL); // NULL DACL
554 if (!hPipe)
556 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
557 //DebugOutputLastError();
558 if (*bRun)
559 Sleep(200);
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);
567 if (fConnected)
569 // Create a thread for this client.
570 CAutoGeneralHandle hCommandThread = CreateThread(
571 NULL, // no security attribute
572 0, // default stack size
573 CommandThread,
574 (HANDLE) hPipe, // thread parameter
575 0, // not suspended
576 &dwThreadId); // returns thread ID
578 if (!hCommandThread)
580 //OutputDebugStringA("TSVNCache: Could not create Command thread\n");
581 //DebugOutputLastError();
582 DisconnectNamedPipe(hPipe);
583 hPipe.CloseHandle();
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);
588 return 1;
590 // detach the handle, since we passed it to the thread
591 hPipe.Detach();
593 else
595 // The client could not connect, so close the pipe.
596 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
597 //DebugOutputLastError();
598 hPipe.CloseHandle();
599 if (*bRun)
600 Sleep(200);
601 continue; // don't end the thread!
604 ATLTRACE("CommandWait thread exited\n");
605 return 0;
608 DWORD WINAPI InstanceThread(LPVOID lpvParam)
610 ATLTRACE("InstanceThread started\n");
611 TGITCacheResponse response;
612 DWORD cbBytesRead, cbWritten;
613 BOOL fSuccess;
614 CAutoFile hPipe;
616 // The thread's parameter is a handle to a pipe instance.
618 hPipe = lpvParam;
619 InterlockedIncrement(&nThreadCount);
620 while (bRun)
622 // Read client requests from the pipe.
623 TGITCacheRequest request;
624 fSuccess = ReadFile(
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);
638 return 1;
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);
659 return 1;
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);
673 return 0;
676 DWORD WINAPI CommandThread(LPVOID lpvParam)
678 ATLTRACE("CommandThread started\n");
679 DWORD cbBytesRead;
680 BOOL fSuccess;
681 CAutoFile hPipe;
683 // The thread's parameter is a handle to a pipe instance.
685 hPipe = lpvParam;
687 while (bRun)
689 // Read client requests from the pipe.
690 TGITCacheCommand command;
691 fSuccess = ReadFile(
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");
702 return 1;
705 switch (command.command)
707 case TGITCACHECOMMAND_END:
708 FlushFileBuffers(hPipe);
709 DisconnectNamedPipe(hPipe);
710 ATLTRACE("Command thread exited\n");
711 return 0;
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());
723 break;
724 case TGITCACHECOMMAND_REFRESHALL:
726 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
727 CGitStatusCache::Instance().Refresh();
729 break;
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);
739 break;
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);
747 break;
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);
755 break;
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");
766 return 0;