Mark g_IndexFileMap extern
[TortoiseGit.git] / src / TGitCache / TGITCache.cpp
blob7cf07b5cc97ff7e2537189c7a857c375100ded7a
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"
36 #include "CreateProcessHelper.h"
37 #include "gitindex.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 #if ENABLE_CRASHHANLDER
50 CCrashReportTGit crasher(L"TGitCache " _T(APP_X64_STRING), TGIT_VERMAJOR, TGIT_VERMINOR, TGIT_VERMICRO, TGIT_VERBUILD, TGIT_VERDATE);
51 #endif
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);
58 bool bRun = true;
59 bool bRestart = false;
60 NOTIFYICONDATA niData;
61 HWND hWnd;
62 HWND hTrayWnd;
63 TCHAR szCurrentCrawledPath[MAX_CRAWLEDPATHS][MAX_CRAWLEDPATHSLEN];
64 int nCurrentCrawledpathIndex = 0;
65 CComAutoCriticalSection critSec;
66 CGitIndexFileMap g_IndexFileMap;
68 volatile LONG nThreadCount = 0;
70 #define PACKVERSION(major,minor) MAKELONG(minor,major)
72 void DebugOutputLastError()
74 LPVOID lpMsgBuf;
75 if (!FormatMessage(
76 FORMAT_MESSAGE_ALLOCATE_BUFFER |
77 FORMAT_MESSAGE_FROM_SYSTEM |
78 FORMAT_MESSAGE_IGNORE_INSERTS,
79 NULL,
80 GetLastError(),
81 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
82 (LPTSTR) &lpMsgBuf,
84 NULL ))
86 return;
89 // Display the string.
90 OutputDebugStringA("TGitCache GetLastError(): ");
91 OutputDebugString((LPCTSTR)lpMsgBuf);
92 OutputDebugStringA("\n");
94 // Free the buffer.
95 LocalFree( lpMsgBuf );
98 void HandleCommandLine(LPSTR lpCmdLine)
100 char *ptr = strstr(lpCmdLine, "/kill:");
101 if (ptr)
103 DWORD pid = (DWORD)atoi(ptr + strlen("/kill:"));
104 HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE, FALSE, pid);
105 if (hProcess)
107 if (::WaitForSingleObject(hProcess, 5000) != WAIT_OBJECT_0)
109 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Killing previous TGitCache PID %d\n", pid);
110 if (!::TerminateProcess(hProcess, (UINT)-1))
111 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Kill previous TGitCache PID %d failed\n", pid);
112 ::WaitForSingleObject(hProcess, 5000);
114 ::CloseHandle(hProcess);
115 for (int i = 0; i < 5; i++)
117 HANDLE hMutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, GetCacheMutexName());
118 if (!hMutex)
119 break;
120 ::CloseHandle(hMutex);
121 ::Sleep(1000);
127 void HandleRestart()
129 if (bRestart)
131 TCHAR exeName[MAX_PATH] = { 0 };
132 ::GetModuleFileName(nullptr, exeName, _countof(exeName));
133 TCHAR cmdLine[20] = { 0 };
134 _stprintf_s(cmdLine, _T(" /kill:%d"), GetCurrentProcessId());
135 if (!CCreateProcessHelper::CreateProcessDetached(exeName, cmdLine))
136 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Failed to start cache\n");
140 int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR lpCmdLine, int /*cmdShow*/)
142 SetDllDirectory(L"");
143 git_threads_init();
144 HandleCommandLine(lpCmdLine);
145 CAutoGeneralHandle hReloadProtection = ::CreateMutex(NULL, FALSE, GetCacheMutexName());
147 if ((!hReloadProtection) || (GetLastError() == ERROR_ALREADY_EXISTS))
149 // An instance of TGitCache is already running
150 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": TGitCache ignoring restart\n");
151 return 0;
154 CGitStatusCache::Create();
155 CGitStatusCache::Instance().Init();
157 SecureZeroMemory(szCurrentCrawledPath, sizeof(szCurrentCrawledPath));
159 DWORD dwThreadId;
160 MSG msg;
161 TCHAR szWindowClass[] = {TGIT_CACHE_WINDOW_NAME};
163 // create a hidden window to receive window messages.
164 WNDCLASSEX wcex;
165 wcex.cbSize = sizeof(WNDCLASSEX);
166 wcex.style = CS_HREDRAW | CS_VREDRAW;
167 wcex.lpfnWndProc = (WNDPROC)WndProc;
168 wcex.cbClsExtra = 0;
169 wcex.cbWndExtra = 0;
170 wcex.hInstance = hInstance;
171 wcex.hIcon = 0;
172 wcex.hCursor = 0;
173 wcex.hbrBackground = 0;
174 wcex.lpszMenuName = NULL;
175 wcex.lpszClassName = szWindowClass;
176 wcex.hIconSm = 0;
177 RegisterClassEx(&wcex);
178 hWnd = CreateWindow(TGIT_CACHE_WINDOW_NAME, TGIT_CACHE_WINDOW_NAME, WS_CAPTION, 0, 0, 800, 300, NULL, 0, hInstance, 0);
179 hTrayWnd = hWnd;
180 if (hWnd == NULL)
182 return 0;
184 if (CRegStdDWORD(_T("Software\\TortoiseGit\\CacheTrayIcon"), FALSE)==TRUE)
186 SecureZeroMemory(&niData,sizeof(NOTIFYICONDATA));
188 DWORD dwMajor = 0;
189 DWORD dwMinor = 0;
190 GetShellVersion(&dwMajor, &dwMinor);
191 DWORD dwVersion = PACKVERSION(dwMajor, dwMinor);
192 if (dwVersion >= PACKVERSION(6,0))
193 niData.cbSize = sizeof(NOTIFYICONDATA);
194 else if (dwVersion >= PACKVERSION(5,0))
195 niData.cbSize = NOTIFYICONDATA_V2_SIZE;
196 else
197 niData.cbSize = NOTIFYICONDATA_V1_SIZE;
199 niData.uID = TRAY_ID; // own tray icon ID
200 niData.hWnd = hWnd;
201 niData.uFlags = NIF_ICON|NIF_MESSAGE;
203 // load the icon
204 niData.hIcon =
205 (HICON)LoadImage(hInstance,
206 MAKEINTRESOURCE(IDI_TGITCACHE),
207 IMAGE_ICON,
208 GetSystemMetrics(SM_CXSMICON),
209 GetSystemMetrics(SM_CYSMICON),
210 LR_DEFAULTCOLOR);
212 // set the message to send
213 // note: the message value should be in the
214 // range of WM_APP through 0xBFFF
215 niData.uCallbackMessage = TRAY_CALLBACK;
216 Shell_NotifyIcon(NIM_ADD,&niData);
217 // free icon handle
218 if(niData.hIcon && DestroyIcon(niData.hIcon))
219 niData.hIcon = NULL;
222 // Create a thread which waits for incoming pipe connections
223 CAutoGeneralHandle hPipeThread = CreateThread(
224 NULL, // no security attribute
225 0, // default stack size
226 PipeThread,
227 (LPVOID) &bRun, // thread parameter
228 0, // not suspended
229 &dwThreadId); // returns thread ID
231 if (!hPipeThread)
233 return 0;
235 else hPipeThread.CloseHandle();
237 // Create a thread which waits for incoming pipe connections
238 CAutoGeneralHandle hCommandWaitThread = CreateThread(
239 NULL, // no security attribute
240 0, // default stack size
241 CommandWaitThread,
242 (LPVOID) &bRun, // thread parameter
243 0, // not suspended
244 &dwThreadId); // returns thread ID
246 if (!hCommandWaitThread)
248 return 0;
252 // loop to handle window messages.
253 while (bRun)
255 BOOL bLoopRet = GetMessage(&msg, NULL, 0, 0);
256 if ((bLoopRet != -1)&&(bLoopRet != 0))
258 DispatchMessage(&msg);
262 bRun = false;
264 Shell_NotifyIcon(NIM_DELETE,&niData);
265 CGitStatusCache::Destroy();
266 HandleRestart();
267 git_threads_shutdown();
268 return 0;
271 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
273 switch (message)
275 case TRAY_CALLBACK:
277 switch(lParam)
279 case WM_LBUTTONDBLCLK:
280 if (IsWindowVisible(hWnd))
281 ShowWindow(hWnd, SW_HIDE);
282 else
283 ShowWindow(hWnd, SW_RESTORE);
284 break;
285 case WM_MOUSEMOVE:
287 CString sInfoTip;
288 NOTIFYICONDATA SystemTray;
289 sInfoTip.Format(_T("TortoiseGit Overlay Icon Server\nCached Directories: %Id\nWatched paths: %d"),
290 CGitStatusCache::Instance().GetCacheSize(),
291 CGitStatusCache::Instance().GetNumberOfWatchedPaths());
293 SystemTray.cbSize = sizeof(NOTIFYICONDATA);
294 SystemTray.hWnd = hTrayWnd;
295 SystemTray.uID = TRAY_ID;
296 SystemTray.uFlags = NIF_TIP;
297 _tcscpy_s(SystemTray.szTip, sInfoTip);
298 Shell_NotifyIcon(NIM_MODIFY, &SystemTray);
300 break;
301 case WM_RBUTTONUP:
302 case WM_CONTEXTMENU:
304 POINT pt;
305 GetCursorPos(&pt);
306 HMENU hMenu = CreatePopupMenu();
307 if(hMenu)
309 bool enabled = (DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"), GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) != ShellCache::none;
310 InsertMenu(hMenu, (UINT)-1, MF_BYPOSITION, TRAYPOP_ENABLE, enabled ? _T("Disable Status Cache") : _T("Enable Status Cache"));
311 InsertMenu(hMenu, (UINT)-1, MF_BYPOSITION, TRAYPOP_EXIT, _T("Exit"));
312 SetForegroundWindow(hWnd);
313 TrackPopupMenu(hMenu, TPM_BOTTOMALIGN, pt.x, pt.y, 0, hWnd, NULL);
314 DestroyMenu(hMenu);
317 break;
320 break;
321 case WM_PAINT:
323 PAINTSTRUCT ps;
324 HDC hdc = BeginPaint(hWnd, &ps);
325 RECT rect;
326 GetClientRect(hWnd, &rect);
327 // clear the background
328 HBRUSH background = CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
329 HGDIOBJ oldbrush = SelectObject(hdc, background);
330 FillRect(hdc, &rect, background);
332 int line = 0;
333 SIZE fontsize = {0};
334 AutoLocker print(critSec);
335 GetTextExtentPoint32( hdc, szCurrentCrawledPath[0], (int)_tcslen(szCurrentCrawledPath[0]), &fontsize );
336 for (int i=nCurrentCrawledpathIndex; i<MAX_CRAWLEDPATHS; ++i)
338 TextOut(hdc, 0, line*fontsize.cy, szCurrentCrawledPath[i], (int)_tcslen(szCurrentCrawledPath[i]));
339 ++line;
341 for (int i=0; i<nCurrentCrawledpathIndex; ++i)
343 TextOut(hdc, 0, line*fontsize.cy, szCurrentCrawledPath[i], (int)_tcslen(szCurrentCrawledPath[i]));
344 ++line;
347 SelectObject(hdc,oldbrush);
348 EndPaint(hWnd, &ps);
349 DeleteObject(background);
350 return 0L;
352 break;
353 case WM_COMMAND:
355 WORD wmId = LOWORD(wParam);
357 switch (wmId)
359 case TRAYPOP_ENABLE:
361 CRegStdDWORD reg = CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"), GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe);
362 bool enabled = (DWORD)reg != ShellCache::none;
363 reg = enabled ? ShellCache::none : ShellCache::exe;
364 if (enabled)
366 bRestart = true;
367 DestroyWindow(hWnd);
369 break;
371 case TRAYPOP_EXIT:
372 DestroyWindow(hWnd);
373 break;
375 return 1;
377 case WM_QUERYENDSESSION:
379 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": WM_QUERYENDSESSION\n");
380 CAutoWriteWeakLock writeLock(CGitStatusCache::Instance().GetGuard(), 200);
381 CGitStatusCache::Instance().Stop();
382 return TRUE;
384 break;
385 case WM_CLOSE:
386 case WM_ENDSESSION:
387 case WM_DESTROY:
388 case WM_QUIT:
390 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": WM_CLOSE/DESTROY/ENDSESSION/QUIT\n");
391 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
392 CGitStatusCache::Instance().Stop();
393 CGitStatusCache::Instance().SaveCache();
394 if (message != WM_QUIT)
395 PostQuitMessage(0);
396 bRun = false;
397 return 1;
399 break;
400 case WM_DEVICECHANGE:
402 DEV_BROADCAST_HDR * phdr = (DEV_BROADCAST_HDR*)lParam;
403 switch (wParam)
405 case DBT_CUSTOMEVENT:
407 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": WM_DEVICECHANGE with DBT_CUSTOMEVENT\n");
408 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
410 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
411 if (IsEqualGUID(phandle->dbch_eventguid, GUID_IO_VOLUME_DISMOUNT))
413 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Device to be dismounted\n");
414 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
415 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_handle);
417 if (IsEqualGUID(phandle->dbch_eventguid, GUID_IO_VOLUME_LOCK))
419 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Device lock event\n");
420 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
421 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_handle);
425 break;
426 case DBT_DEVICEREMOVEPENDING:
427 case DBT_DEVICEQUERYREMOVE:
428 case DBT_DEVICEREMOVECOMPLETE:
429 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": WM_DEVICECHANGE with DBT_DEVICEREMOVEPENDING/DBT_DEVICEQUERYREMOVE/DBT_DEVICEREMOVECOMPLETE\n");
430 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
432 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
433 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
434 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_handle);
436 else if (phdr->dbch_devicetype == DBT_DEVTYP_VOLUME)
438 DEV_BROADCAST_VOLUME * pVolume = (DEV_BROADCAST_VOLUME*)lParam;
439 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
440 for (BYTE i = 0; i < 26; ++i)
442 if (pVolume->dbcv_unitmask & (1 << i))
444 TCHAR driveletter = 'A' + i;
445 CString drive = CString(driveletter);
446 drive += L":\\";
447 CGitStatusCache::Instance().CloseWatcherHandles(CTGitPath(drive));
451 else
453 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
454 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
456 break;
459 break;
460 default:
461 break;
463 return DefWindowProc(hWnd, message, wParam, lParam);
466 //////////////////////////////////////////////////////////////////////////
468 VOID GetAnswerToRequest(const TGITCacheRequest* pRequest, TGITCacheResponse* pReply, DWORD* pResponseLength)
470 CTGitPath path;
471 *pResponseLength = 0;
472 if(pRequest->flags & TGITCACHE_FLAGS_FOLDERISKNOWN)
474 path.SetFromWin(pRequest->path, !!(pRequest->flags & TGITCACHE_FLAGS_ISFOLDER));
476 else
478 path.SetFromWin(pRequest->path);
481 CAutoReadWeakLock readLock(CGitStatusCache::Instance().GetGuard(), 2000);
482 if (readLock.IsAcquired())
484 CGitStatusCache::Instance().GetStatusForPath(path, pRequest->flags, false).BuildCacheResponse(*pReply, *pResponseLength);
486 else
488 CStatusCacheEntry entry;
489 entry.BuildCacheResponse(*pReply, *pResponseLength);
493 DWORD WINAPI PipeThread(LPVOID lpvParam)
495 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": PipeThread started\n");
496 bool * bRun = (bool *)lpvParam;
497 // The main loop creates an instance of the named pipe and
498 // then waits for a client to connect to it. When the client
499 // connects, a thread is created to handle communications
500 // with that client, and the loop is repeated.
501 DWORD dwThreadId;
502 BOOL fConnected;
503 CAutoFile hPipe;
505 while (*bRun)
507 hPipe = CreateNamedPipe(
508 GetCachePipeName(),
509 PIPE_ACCESS_DUPLEX, // read/write access
510 PIPE_TYPE_MESSAGE | // message type pipe
511 PIPE_READMODE_MESSAGE | // message-read mode
512 PIPE_WAIT, // blocking mode
513 PIPE_UNLIMITED_INSTANCES, // max. instances
514 BUFSIZE, // output buffer size
515 BUFSIZE, // input buffer size
516 NMPWAIT_USE_DEFAULT_WAIT, // client time-out
517 NULL); // NULL DACL
519 if (!hPipe)
521 if (*bRun)
522 Sleep(200);
523 continue; // never leave the thread!
526 // Wait for the client to connect; if it succeeds,
527 // the function returns a nonzero value. If the function returns
528 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
529 fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
530 if (fConnected)
532 // Create a thread for this client.
533 CAutoGeneralHandle hInstanceThread = CreateThread(
534 NULL, // no security attribute
535 0, // default stack size
536 InstanceThread,
537 (HANDLE) hPipe, // thread parameter
538 0, // not suspended
539 &dwThreadId); // returns thread ID
541 if (!hInstanceThread)
543 DisconnectNamedPipe(hPipe);
544 // since we're now closing this thread, we also have to close the whole application!
545 // otherwise the thread is dead, but the app is still running, refusing new instances
546 // but no pipe will be available anymore.
547 PostMessage(hWnd, WM_CLOSE, 0, 0);
548 return 1;
550 // detach the handle, since we passed it to the thread
551 hPipe.Detach();
553 else
555 // The client could not connect, so close the pipe.
556 hPipe.CloseHandle();
557 if (*bRun)
558 Sleep(200);
559 continue; // don't end the thread!
562 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Pipe thread exited\n");
563 return 0;
566 DWORD WINAPI CommandWaitThread(LPVOID lpvParam)
568 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": CommandWaitThread started\n");
569 bool * bRun = (bool *)lpvParam;
570 // The main loop creates an instance of the named pipe and
571 // then waits for a client to connect to it. When the client
572 // connects, a thread is created to handle communications
573 // with that client, and the loop is repeated.
574 DWORD dwThreadId;
575 BOOL fConnected;
576 CAutoFile hPipe;
578 while (*bRun)
580 hPipe = CreateNamedPipe(
581 GetCacheCommandPipeName(),
582 PIPE_ACCESS_DUPLEX, // read/write access
583 PIPE_TYPE_MESSAGE | // message type pipe
584 PIPE_READMODE_MESSAGE | // message-read mode
585 PIPE_WAIT, // blocking mode
586 PIPE_UNLIMITED_INSTANCES, // max. instances
587 BUFSIZE, // output buffer size
588 BUFSIZE, // input buffer size
589 NMPWAIT_USE_DEFAULT_WAIT, // client time-out
590 NULL); // NULL DACL
592 if (!hPipe)
594 if (*bRun)
595 Sleep(200);
596 continue; // never leave the thread!
599 // Wait for the client to connect; if it succeeds,
600 // the function returns a nonzero value. If the function returns
601 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
602 fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
603 if (fConnected)
605 // Create a thread for this client.
606 CAutoGeneralHandle hCommandThread = CreateThread(
607 NULL, // no security attribute
608 0, // default stack size
609 CommandThread,
610 (HANDLE) hPipe, // thread parameter
611 0, // not suspended
612 &dwThreadId); // returns thread ID
614 if (!hCommandThread)
616 DisconnectNamedPipe(hPipe);
617 hPipe.CloseHandle();
618 // since we're now closing this thread, we also have to close the whole application!
619 // otherwise the thread is dead, but the app is still running, refusing new instances
620 // but no pipe will be available anymore.
621 PostMessage(hWnd, WM_CLOSE, 0, 0);
622 return 1;
624 // detach the handle, since we passed it to the thread
625 hPipe.Detach();
627 else
629 // The client could not connect, so close the pipe.
630 hPipe.CloseHandle();
631 if (*bRun)
632 Sleep(200);
633 continue; // don't end the thread!
636 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": CommandWait thread exited\n");
637 return 0;
640 DWORD WINAPI InstanceThread(LPVOID lpvParam)
642 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": InstanceThread started\n");
643 TGITCacheResponse response;
644 DWORD cbBytesRead, cbWritten;
645 CAutoFile hPipe;
647 // The thread's parameter is a handle to a pipe instance.
649 hPipe = lpvParam;
650 InterlockedIncrement(&nThreadCount);
651 while (bRun)
653 // Read client requests from the pipe.
654 TGITCacheRequest request;
655 BOOL fSuccess = ReadFile(
656 hPipe, // handle to pipe
657 &request, // buffer to receive data
658 sizeof(request), // size of buffer
659 &cbBytesRead, // number of bytes read
660 NULL); // not overlapped I/O
662 if (! fSuccess || cbBytesRead == 0)
664 DisconnectNamedPipe(hPipe);
665 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Instance thread exited\n");
666 InterlockedDecrement(&nThreadCount);
667 if (nThreadCount == 0)
668 PostMessage(hWnd, WM_CLOSE, 0, 0);
669 return 1;
672 DWORD responseLength;
673 GetAnswerToRequest(&request, &response, &responseLength);
675 // Write the reply to the pipe.
676 fSuccess = WriteFile(
677 hPipe, // handle to pipe
678 &response, // buffer to write from
679 responseLength, // number of bytes to write
680 &cbWritten, // number of bytes written
681 NULL); // not overlapped I/O
683 if (! fSuccess || responseLength != cbWritten)
685 DisconnectNamedPipe(hPipe);
686 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Instance thread exited\n");
687 InterlockedDecrement(&nThreadCount);
688 if (nThreadCount == 0)
689 PostMessage(hWnd, WM_CLOSE, 0, 0);
690 return 1;
694 // Flush the pipe to allow the client to read the pipe's contents
695 // before disconnecting. Then disconnect the pipe, and close the
696 // handle to this pipe instance.
698 FlushFileBuffers(hPipe);
699 DisconnectNamedPipe(hPipe);
700 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Instance thread exited\n");
701 InterlockedDecrement(&nThreadCount);
702 if (nThreadCount == 0)
703 PostMessage(hWnd, WM_CLOSE, 0, 0);
704 return 0;
707 DWORD WINAPI CommandThread(LPVOID lpvParam)
709 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": CommandThread started\n");
710 DWORD cbBytesRead;
711 CAutoFile hPipe;
713 // The thread's parameter is a handle to a pipe instance.
715 hPipe = lpvParam;
717 while (bRun)
719 // Read client requests from the pipe.
720 TGITCacheCommand command;
721 BOOL fSuccess = ReadFile(
722 hPipe, // handle to pipe
723 &command, // buffer to receive data
724 sizeof(command), // size of buffer
725 &cbBytesRead, // number of bytes read
726 NULL); // not overlapped I/O
728 if (! fSuccess || cbBytesRead == 0)
730 DisconnectNamedPipe(hPipe);
731 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Command thread exited\n");
732 return 1;
735 switch (command.command)
737 case TGITCACHECOMMAND_END:
738 FlushFileBuffers(hPipe);
739 DisconnectNamedPipe(hPipe);
740 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Command thread exited\n");
741 return 0;
742 case TGITCACHECOMMAND_CRAWL:
744 CTGitPath changedpath;
745 changedpath.SetFromWin(command.path, true);
746 // remove the path from our cache - that will 'invalidate' it.
748 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
749 CGitStatusCache::Instance().RemoveCacheForPath(changedpath);
751 CGitStatusCache::Instance().AddFolderForCrawling(changedpath.GetDirectory());
753 break;
754 case TGITCACHECOMMAND_REFRESHALL:
756 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
757 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": refresh all\n"));
758 CGitStatusCache::Instance().Refresh();
760 break;
761 case TGITCACHECOMMAND_RELEASE:
763 CTGitPath changedpath;
764 changedpath.SetFromWin(command.path, true);
765 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": release handle for path %s\n"), changedpath.GetWinPath());
766 CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard());
767 CGitStatusCache::Instance().CloseWatcherHandles(changedpath);
768 CGitStatusCache::Instance().RemoveCacheForPath(changedpath);
770 break;
771 case TGITCACHECOMMAND_BLOCK:
773 CTGitPath changedpath;
774 changedpath.SetFromWin(command.path);
775 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": block path %s\n"), changedpath.GetWinPath());
776 CGitStatusCache::Instance().BlockPath(changedpath);
778 break;
779 case TGITCACHECOMMAND_UNBLOCK:
781 CTGitPath changedpath;
782 changedpath.SetFromWin(command.path);
783 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": unblock path %s\n"), changedpath.GetWinPath());
784 CGitStatusCache::Instance().UnBlockPath(changedpath);
786 break;
790 // Flush the pipe to allow the client to read the pipe's contents
791 // before disconnecting. Then disconnect the pipe, and close the
792 // handle to this pipe instance.
794 FlushFileBuffers(hPipe);
795 DisconnectNamedPipe(hPipe);
796 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Command thread exited\n");
797 return 0;