Implement commands to (un)block paths from getting crawled
[TortoiseGit.git] / src / TGitCache / TGITCache.cpp
blob78e0a1d29fb1ea20502d9dcee692fe6cce8731b7
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-2011 - 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 "..\crashrpt\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"
37 #include <ShellAPI.h>
39 #ifndef GET_X_LPARAM
40 #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
41 #endif
42 #ifndef GET_Y_LPARAM
43 #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
44 #endif
47 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
49 CCrashReport crasher("tortoisegit-bug@googlegroups.com", "Crash Report for TGitCache " APP_X64_STRING " : " STRPRODUCTVER, TRUE);// crash
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)
67 DWORD GetDllVersion(LPCTSTR lpszDllName)
69 DWORD dwVersion = 0;
71 /* For security purposes, LoadLibrary should be provided with a
72 fully-qualified path to the DLL. The lpszDllName variable should be
73 tested to ensure that it is a fully qualified path before it is used. */
74 CAutoLibrary hinstDll = LoadLibrary(lpszDllName);
76 if(hinstDll)
78 DLLGETVERSIONPROC pDllGetVersion;
79 pDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinstDll,
80 "DllGetVersion");
82 /* Because some DLLs might not implement this function, you
83 must test for it explicitly. Depending on the particular
84 DLL, the lack of a DllGetVersion function can be a useful
85 indicator of the version. */
87 if(pDllGetVersion)
89 DLLVERSIONINFO dvi;
90 HRESULT hr;
92 SecureZeroMemory(&dvi, sizeof(dvi));
93 dvi.cbSize = sizeof(dvi);
95 hr = (*pDllGetVersion)(&dvi);
97 if(SUCCEEDED(hr))
99 dwVersion = PACKVERSION(dvi.dwMajorVersion, dvi.dwMinorVersion);
103 return dwVersion;
106 void DebugOutputLastError()
108 LPVOID lpMsgBuf;
109 if (!FormatMessage(
110 FORMAT_MESSAGE_ALLOCATE_BUFFER |
111 FORMAT_MESSAGE_FROM_SYSTEM |
112 FORMAT_MESSAGE_IGNORE_INSERTS,
113 NULL,
114 GetLastError(),
115 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
116 (LPTSTR) &lpMsgBuf,
118 NULL ))
120 return;
123 // Display the string.
124 OutputDebugStringA("TGitCache GetLastError(): ");
125 OutputDebugString((LPCTSTR)lpMsgBuf);
126 OutputDebugStringA("\n");
128 // Free the buffer.
129 LocalFree( lpMsgBuf );
132 int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*cmdShow*/)
134 SetDllDirectory(L"");
135 CAutoGeneralHandle hReloadProtection = ::CreateMutex(NULL, FALSE, GetCacheMutexName());
137 if ((!hReloadProtection) || (GetLastError() == ERROR_ALREADY_EXISTS))
139 // An instance of TGitCache is already running
140 ATLTRACE("TGitCache ignoring restart\n");
141 return 0;
144 // apr_initialize();
145 // svn_dso_initialize2();
146 g_GitAdminDir.Init();
147 CGitStatusCache::Create();
148 CGitStatusCache::Instance().Init();
150 SecureZeroMemory(szCurrentCrawledPath, sizeof(szCurrentCrawledPath));
152 DWORD dwThreadId;
153 MSG msg;
154 TCHAR szWindowClass[] = {TGIT_CACHE_WINDOW_NAME};
156 // create a hidden window to receive window messages.
157 WNDCLASSEX wcex;
158 wcex.cbSize = sizeof(WNDCLASSEX);
159 wcex.style = CS_HREDRAW | CS_VREDRAW;
160 wcex.lpfnWndProc = (WNDPROC)WndProc;
161 wcex.cbClsExtra = 0;
162 wcex.cbWndExtra = 0;
163 wcex.hInstance = hInstance;
164 wcex.hIcon = 0;
165 wcex.hCursor = 0;
166 wcex.hbrBackground = 0;
167 wcex.lpszMenuName = NULL;
168 wcex.lpszClassName = szWindowClass;
169 wcex.hIconSm = 0;
170 RegisterClassEx(&wcex);
171 hWnd = CreateWindow(TGIT_CACHE_WINDOW_NAME, TGIT_CACHE_WINDOW_NAME, WS_CAPTION, 0, 0, 800, 300, NULL, 0, hInstance, 0);
172 hTrayWnd = hWnd;
173 if (hWnd == NULL)
175 return 0;
177 if (CRegStdDWORD(_T("Software\\TortoiseGit\\CacheTrayIcon"), FALSE)==TRUE)
179 SecureZeroMemory(&niData,sizeof(NOTIFYICONDATA));
181 DWORD dwVersion = GetDllVersion(_T("Shell32.dll"));
183 if (dwVersion >= PACKVERSION(6,0))
184 niData.cbSize = sizeof(NOTIFYICONDATA);
185 else if (dwVersion >= PACKVERSION(5,0))
186 niData.cbSize = NOTIFYICONDATA_V2_SIZE;
187 else
188 niData.cbSize = NOTIFYICONDATA_V1_SIZE;
190 niData.uID = TRAY_ID; // own tray icon ID
191 niData.hWnd = hWnd;
192 niData.uFlags = NIF_ICON|NIF_MESSAGE;
194 // load the icon
195 niData.hIcon =
196 (HICON)LoadImage(hInstance,
197 MAKEINTRESOURCE(IDI_TGITCACHE),
198 IMAGE_ICON,
199 GetSystemMetrics(SM_CXSMICON),
200 GetSystemMetrics(SM_CYSMICON),
201 LR_DEFAULTCOLOR);
203 // set the message to send
204 // note: the message value should be in the
205 // range of WM_APP through 0xBFFF
206 niData.uCallbackMessage = TRAY_CALLBACK;
207 Shell_NotifyIcon(NIM_ADD,&niData);
208 // free icon handle
209 if(niData.hIcon && DestroyIcon(niData.hIcon))
210 niData.hIcon = NULL;
213 // Create a thread which waits for incoming pipe connections
214 CAutoGeneralHandle hPipeThread = CreateThread(
215 NULL, // no security attribute
216 0, // default stack size
217 PipeThread,
218 (LPVOID) &bRun, // thread parameter
219 0, // not suspended
220 &dwThreadId); // returns thread ID
222 if (!hPipeThread)
224 //OutputDebugStringA("TSVNCache: Could not create pipe thread\n");
225 //DebugOutputLastError();
226 return 0;
228 else CloseHandle(hPipeThread);
230 // Create a thread which waits for incoming pipe connections
231 CAutoGeneralHandle hCommandWaitThread = CreateThread(
232 NULL, // no security attribute
233 0, // default stack size
234 CommandWaitThread,
235 (LPVOID) &bRun, // thread parameter
236 0, // not suspended
237 &dwThreadId); // returns thread ID
239 if (!hCommandWaitThread)
241 //OutputDebugStringA("TSVNCache: Could not create command wait thread\n");
242 //DebugOutputLastError();
243 return 0;
247 // loop to handle window messages.
248 BOOL bLoopRet;
249 while (bRun)
251 bLoopRet = GetMessage(&msg, NULL, 0, 0);
252 if ((bLoopRet != -1)&&(bLoopRet != 0))
254 DispatchMessage(&msg);
258 bRun = false;
260 Shell_NotifyIcon(NIM_DELETE,&niData);
261 CGitStatusCache::Destroy();
262 g_GitAdminDir.Close();
263 // apr_terminate();
265 return 0;
268 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
270 switch (message)
272 case TRAY_CALLBACK:
274 switch(lParam)
276 case WM_LBUTTONDBLCLK:
277 if (IsWindowVisible(hWnd))
278 ShowWindow(hWnd, SW_HIDE);
279 else
280 ShowWindow(hWnd, SW_RESTORE);
281 break;
282 case WM_MOUSEMOVE:
284 CString sInfoTip;
285 NOTIFYICONDATA SystemTray;
286 sInfoTip.Format(_T("Cached Directories : %ld\nWatched paths : %ld"),
287 CGitStatusCache::Instance().GetCacheSize(),
288 CGitStatusCache::Instance().GetNumberOfWatchedPaths());
290 SystemTray.cbSize = sizeof(NOTIFYICONDATA);
291 SystemTray.hWnd = hTrayWnd;
292 SystemTray.uID = TRAY_ID;
293 SystemTray.uFlags = NIF_TIP;
294 _tcscpy_s(SystemTray.szTip, sInfoTip);
295 Shell_NotifyIcon(NIM_MODIFY, &SystemTray);
297 break;
298 case WM_RBUTTONUP:
299 case WM_CONTEXTMENU:
301 POINT pt;
302 DWORD ptW = GetMessagePos();
303 pt.x = GET_X_LPARAM(ptW);
304 pt.y = GET_Y_LPARAM(ptW);
305 HMENU hMenu = CreatePopupMenu();
306 if(hMenu)
308 InsertMenu(hMenu, (UINT)-1, MF_BYPOSITION, TRAYPOP_EXIT, _T("Exit"));
309 SetForegroundWindow(hWnd);
310 TrackPopupMenu(hMenu, TPM_BOTTOMALIGN, pt.x, pt.y, 0, hWnd, NULL);
311 DestroyMenu(hMenu);
314 break;
317 break;
318 case WM_PAINT:
320 PAINTSTRUCT ps;
321 HDC hdc = BeginPaint(hWnd, &ps);
322 RECT rect;
323 GetClientRect(hWnd, &rect);
324 // clear the background
325 HBRUSH background = CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
326 HGDIOBJ oldbrush = SelectObject(hdc, background);
327 FillRect(hdc, &rect, background);
329 int line = 0;
330 SIZE fontsize = {0};
331 AutoLocker print(critSec);
332 GetTextExtentPoint32( hdc, szCurrentCrawledPath[0], (int)_tcslen(szCurrentCrawledPath[0]), &fontsize );
333 for (int i=nCurrentCrawledpathIndex; i<MAX_CRAWLEDPATHS; ++i)
335 TextOut(hdc, 0, line*fontsize.cy, szCurrentCrawledPath[i], (int)_tcslen(szCurrentCrawledPath[i]));
336 line++;
338 for (int i=0; i<nCurrentCrawledpathIndex; ++i)
340 TextOut(hdc, 0, line*fontsize.cy, szCurrentCrawledPath[i], (int)_tcslen(szCurrentCrawledPath[i]));
341 line++;
345 SelectObject(hdc,oldbrush);
346 EndPaint(hWnd, &ps);
347 DeleteObject(background);
348 return 0L;
350 break;
351 case WM_COMMAND:
353 WORD wmId = LOWORD(wParam);
355 switch (wmId)
357 case TRAYPOP_EXIT:
358 DestroyWindow(hWnd);
359 break;
361 return 1;
363 case WM_QUERYENDSESSION:
365 ATLTRACE("WM_QUERYENDSESSION\n");
366 if (CGitStatusCache::Instance().WaitToWrite(200))
368 CGitStatusCache::Instance().Stop();
369 CGitStatusCache::Instance().Done();
371 return TRUE;
373 break;
374 case WM_CLOSE:
375 case WM_ENDSESSION:
376 case WM_DESTROY:
377 case WM_QUIT:
379 ATLTRACE("WM_CLOSE/DESTROY/ENDSESSION/QUIT\n");
380 CGitStatusCache::Instance().WaitToWrite();
381 CGitStatusCache::Instance().Stop();
382 CGitStatusCache::Instance().SaveCache();
383 if (message != WM_QUIT)
384 PostQuitMessage(0);
385 bRun = false;
386 return 1;
388 break;
389 case WM_DEVICECHANGE:
391 DEV_BROADCAST_HDR * phdr = (DEV_BROADCAST_HDR*)lParam;
392 switch (wParam)
394 case DBT_CUSTOMEVENT:
396 ATLTRACE("WM_DEVICECHANGE with DBT_CUSTOMEVENT\n");
397 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
399 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
400 if (IsEqualGUID(phandle->dbch_eventguid, GUID_IO_VOLUME_DISMOUNT))
402 ATLTRACE("Device to be dismounted\n");
403 CGitStatusCache::Instance().WaitToWrite();
404 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
405 CGitStatusCache::Instance().Done();
407 if (IsEqualGUID(phandle->dbch_eventguid, GUID_IO_VOLUME_LOCK))
409 ATLTRACE("Device lock event\n");
410 CGitStatusCache::Instance().WaitToWrite();
411 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
412 CGitStatusCache::Instance().Done();
416 break;
417 case DBT_DEVICEREMOVEPENDING:
418 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVEPENDING\n");
419 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
421 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
422 CGitStatusCache::Instance().WaitToWrite();
423 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
424 CGitStatusCache::Instance().Done();
426 else
428 CGitStatusCache::Instance().WaitToWrite();
429 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
430 CGitStatusCache::Instance().Done();
432 break;
433 case DBT_DEVICEQUERYREMOVE:
434 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEQUERYREMOVE\n");
435 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
437 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
438 CGitStatusCache::Instance().WaitToWrite();
439 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
440 CGitStatusCache::Instance().Done();
442 else
444 CGitStatusCache::Instance().WaitToWrite();
445 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
446 CGitStatusCache::Instance().Done();
448 break;
449 case DBT_DEVICEREMOVECOMPLETE:
450 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVECOMPLETE\n");
451 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
453 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
454 CGitStatusCache::Instance().WaitToWrite();
455 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
456 CGitStatusCache::Instance().Done();
458 else
460 CGitStatusCache::Instance().WaitToWrite();
461 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
462 CGitStatusCache::Instance().Done();
464 break;
467 break;
468 default:
469 break;
471 return DefWindowProc(hWnd, message, wParam, lParam);
474 //////////////////////////////////////////////////////////////////////////
476 VOID GetAnswerToRequest(const TGITCacheRequest* pRequest, TGITCacheResponse* pReply, DWORD* pResponseLength)
478 CTGitPath path;
479 *pResponseLength = 0;
480 if(pRequest->flags & TGITCACHE_FLAGS_FOLDERISKNOWN)
482 path.SetFromWin(pRequest->path, !!(pRequest->flags & TGITCACHE_FLAGS_ISFOLDER));
484 else
486 path.SetFromWin(pRequest->path);
489 if (CGitStatusCache::Instance().WaitToRead(2000))
491 CGitStatusCache::Instance().GetStatusForPath(path, pRequest->flags, false).BuildCacheResponse(*pReply, *pResponseLength);
492 CGitStatusCache::Instance().Done();
494 else
496 CStatusCacheEntry entry;
497 entry.BuildCacheResponse(*pReply, *pResponseLength);
501 DWORD WINAPI PipeThread(LPVOID lpvParam)
503 ATLTRACE("PipeThread started\n");
504 bool * bRun = (bool *)lpvParam;
505 // The main loop creates an instance of the named pipe and
506 // then waits for a client to connect to it. When the client
507 // connects, a thread is created to handle communications
508 // with that client, and the loop is repeated.
509 DWORD dwThreadId;
510 BOOL fConnected;
511 CAutoFile hPipe;
513 while (*bRun)
515 hPipe = CreateNamedPipe(
516 GetCachePipeName(),
517 PIPE_ACCESS_DUPLEX, // read/write access
518 PIPE_TYPE_MESSAGE | // message type pipe
519 PIPE_READMODE_MESSAGE | // message-read mode
520 PIPE_WAIT, // blocking mode
521 PIPE_UNLIMITED_INSTANCES, // max. instances
522 BUFSIZE, // output buffer size
523 BUFSIZE, // input buffer size
524 NMPWAIT_USE_DEFAULT_WAIT, // client time-out
525 NULL); // NULL DACL
527 if (!hPipe)
529 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
530 //DebugOutputLastError();
531 if (*bRun)
532 Sleep(200);
533 continue; // never leave the thread!
536 // Wait for the client to connect; if it succeeds,
537 // the function returns a nonzero value. If the function returns
538 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
539 fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
540 if (fConnected)
542 // Create a thread for this client.
543 CAutoGeneralHandle hInstanceThread = CreateThread(
544 NULL, // no security attribute
545 0, // default stack size
546 InstanceThread,
547 (HANDLE) hPipe, // thread parameter
548 0, // not suspended
549 &dwThreadId); // returns thread ID
551 if (!hInstanceThread)
553 //OutputDebugStringA("TSVNCache: Could not create Instance thread\n");
554 //DebugOutputLastError();
555 DisconnectNamedPipe(hPipe);
556 // since we're now closing this thread, we also have to close the whole application!
557 // otherwise the thread is dead, but the app is still running, refusing new instances
558 // but no pipe will be available anymore.
559 PostMessage(hWnd, WM_CLOSE, 0, 0);
560 return 1;
562 // detach the handle, since we passed it to the thread
563 hPipe.Detach();
565 else
567 // The client could not connect, so close the pipe.
568 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
569 //DebugOutputLastError();
570 hPipe.CloseHandle();
571 if (*bRun)
572 Sleep(200);
573 continue; // don't end the thread!
576 ATLTRACE("Pipe thread exited\n");
577 return 0;
580 DWORD WINAPI CommandWaitThread(LPVOID lpvParam)
582 ATLTRACE("CommandWaitThread started\n");
583 bool * bRun = (bool *)lpvParam;
584 // The main loop creates an instance of the named pipe and
585 // then waits for a client to connect to it. When the client
586 // connects, a thread is created to handle communications
587 // with that client, and the loop is repeated.
588 DWORD dwThreadId;
589 BOOL fConnected;
590 CAutoFile hPipe;
592 while (*bRun)
594 hPipe = CreateNamedPipe(
595 GetCacheCommandPipeName(),
596 PIPE_ACCESS_DUPLEX, // read/write access
597 PIPE_TYPE_MESSAGE | // message type pipe
598 PIPE_READMODE_MESSAGE | // message-read mode
599 PIPE_WAIT, // blocking mode
600 PIPE_UNLIMITED_INSTANCES, // max. instances
601 BUFSIZE, // output buffer size
602 BUFSIZE, // input buffer size
603 NMPWAIT_USE_DEFAULT_WAIT, // client time-out
604 NULL); // NULL DACL
606 if (!hPipe)
608 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
609 //DebugOutputLastError();
610 if (*bRun)
611 Sleep(200);
612 continue; // never leave the thread!
615 // Wait for the client to connect; if it succeeds,
616 // the function returns a nonzero value. If the function returns
617 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
618 fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
619 if (fConnected)
621 // Create a thread for this client.
622 CAutoGeneralHandle hCommandThread = CreateThread(
623 NULL, // no security attribute
624 0, // default stack size
625 CommandThread,
626 (HANDLE) hPipe, // thread parameter
627 0, // not suspended
628 &dwThreadId); // returns thread ID
630 if (!hCommandThread)
632 //OutputDebugStringA("TSVNCache: Could not create Command thread\n");
633 //DebugOutputLastError();
634 DisconnectNamedPipe(hPipe);
635 CloseHandle(hPipe);
636 // since we're now closing this thread, we also have to close the whole application!
637 // otherwise the thread is dead, but the app is still running, refusing new instances
638 // but no pipe will be available anymore.
639 PostMessage(hWnd, WM_CLOSE, 0, 0);
640 return 1;
642 // detach the handle, since we passed it to the thread
643 hPipe.Detach();
645 else
647 // The client could not connect, so close the pipe.
648 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
649 //DebugOutputLastError();
650 hPipe.CloseHandle();
651 if (*bRun)
652 Sleep(200);
653 continue; // don't end the thread!
656 ATLTRACE("CommandWait thread exited\n");
657 return 0;
660 DWORD WINAPI InstanceThread(LPVOID lpvParam)
662 ATLTRACE("InstanceThread started\n");
663 TGITCacheResponse response;
664 DWORD cbBytesRead, cbWritten;
665 BOOL fSuccess;
666 CAutoFile hPipe;
668 // The thread's parameter is a handle to a pipe instance.
670 hPipe = lpvParam;
671 InterlockedIncrement(&nThreadCount);
672 while (bRun)
674 // Read client requests from the pipe.
675 TGITCacheRequest request;
676 fSuccess = ReadFile(
677 hPipe, // handle to pipe
678 &request, // buffer to receive data
679 sizeof(request), // size of buffer
680 &cbBytesRead, // number of bytes read
681 NULL); // not overlapped I/O
683 if (! fSuccess || cbBytesRead == 0)
685 DisconnectNamedPipe(hPipe);
686 ATLTRACE("Instance thread exited\n");
687 InterlockedDecrement(&nThreadCount);
688 if (nThreadCount == 0)
689 PostMessage(hWnd, WM_CLOSE, 0, 0);
690 return 1;
693 DWORD responseLength;
694 GetAnswerToRequest(&request, &response, &responseLength);
696 // Write the reply to the pipe.
697 fSuccess = WriteFile(
698 hPipe, // handle to pipe
699 &response, // buffer to write from
700 responseLength, // number of bytes to write
701 &cbWritten, // number of bytes written
702 NULL); // not overlapped I/O
704 if (! fSuccess || responseLength != cbWritten)
706 DisconnectNamedPipe(hPipe);
707 ATLTRACE("Instance thread exited\n");
708 InterlockedDecrement(&nThreadCount);
709 if (nThreadCount == 0)
710 PostMessage(hWnd, WM_CLOSE, 0, 0);
711 return 1;
715 // Flush the pipe to allow the client to read the pipe's contents
716 // before disconnecting. Then disconnect the pipe, and close the
717 // handle to this pipe instance.
719 FlushFileBuffers(hPipe);
720 DisconnectNamedPipe(hPipe);
721 ATLTRACE("Instance thread exited\n");
722 InterlockedDecrement(&nThreadCount);
723 if (nThreadCount == 0)
724 PostMessage(hWnd, WM_CLOSE, 0, 0);
725 return 0;
728 DWORD WINAPI CommandThread(LPVOID lpvParam)
730 ATLTRACE("CommandThread started\n");
731 DWORD cbBytesRead;
732 BOOL fSuccess;
733 CAutoFile hPipe;
735 // The thread's parameter is a handle to a pipe instance.
737 hPipe = lpvParam;
739 while (bRun)
741 // Read client requests from the pipe.
742 TGITCacheCommand command;
743 fSuccess = ReadFile(
744 hPipe, // handle to pipe
745 &command, // buffer to receive data
746 sizeof(command), // size of buffer
747 &cbBytesRead, // number of bytes read
748 NULL); // not overlapped I/O
750 if (! fSuccess || cbBytesRead == 0)
752 DisconnectNamedPipe(hPipe);
753 ATLTRACE("Command thread exited\n");
754 return 1;
757 switch (command.command)
759 case TGITCACHECOMMAND_END:
760 FlushFileBuffers(hPipe);
761 DisconnectNamedPipe(hPipe);
762 ATLTRACE("Command thread exited\n");
763 return 0;
764 case TGITCACHECOMMAND_CRAWL:
766 CTGitPath changedpath;
767 changedpath.SetFromWin(CString(command.path), true);
768 // remove the path from our cache - that will 'invalidate' it.
769 CGitStatusCache::Instance().WaitToWrite();
770 CGitStatusCache::Instance().RemoveCacheForPath(changedpath);
771 CGitStatusCache::Instance().Done();
772 CGitStatusCache::Instance().AddFolderForCrawling(changedpath.GetDirectory());
774 break;
775 case TGITCACHECOMMAND_REFRESHALL:
776 CGitStatusCache::Instance().WaitToWrite();
777 CGitStatusCache::Instance().Refresh();
778 CGitStatusCache::Instance().Done();
779 break;
780 case TGITCACHECOMMAND_RELEASE:
782 CTGitPath changedpath;
783 changedpath.SetFromWin(CString(command.path), true);
784 ATLTRACE(_T("release handle for path %s\n"), changedpath.GetWinPath());
785 CGitStatusCache::Instance().WaitToWrite();
786 CGitStatusCache::Instance().CloseWatcherHandles(changedpath);
787 CGitStatusCache::Instance().RemoveCacheForPath(changedpath);
788 CGitStatusCache::Instance().Done();
790 break;
791 case TGITCACHECOMMAND_BLOCK:
793 CTGitPath changedpath;
794 changedpath.SetFromWin(CString(command.path));
795 ATLTRACE(_T("block path %s\n"), changedpath.GetWinPath());
796 CGitStatusCache::Instance().BlockPath(changedpath);
798 break;
799 case TGITCACHECOMMAND_UNBLOCK:
801 CTGitPath changedpath;
802 changedpath.SetFromWin(CString(command.path));
803 ATLTRACE(_T("block path %s\n"), changedpath.GetWinPath());
804 CGitStatusCache::Instance().UnBlockPath(changedpath);
806 break;
810 // Flush the pipe to allow the client to read the pipe's contents
811 // before disconnecting. Then disconnect the pipe, and close the
812 // handle to this pipe instance.
814 FlushFileBuffers(hPipe);
815 DisconnectNamedPipe(hPipe);
816 ATLTRACE("Command thread exited\n");
817 return 0;