1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // External Cache Copyright (C) 2005 - 2006 - Will Dean, Stefan Kueng
\r
5 // This program is free software; you can redistribute it and/or
\r
6 // modify it under the terms of the GNU General Public License
\r
7 // as published by the Free Software Foundation; either version 2
\r
8 // of the License, or (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, write to the Free Software Foundation,
\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
21 #include "shellapi.h"
\r
22 #include "TSVNCache.h"
\r
23 #include "GitStatusCache.h"
\r
24 #include "CacheInterface.h"
\r
25 #include "Resource.h"
\r
26 #include "registry.h"
\r
27 #include "..\crashrpt\CrashReport.h"
\r
28 #include "GitAdminDir.h"
\r
30 #include <initguid.h>
\r
31 #include "ioevent.h"
\r
32 #include "..\version.h"
\r
33 //#include "svn_dso.h"
\r
35 #include <ShellAPI.h>
\r
37 #ifndef GET_X_LPARAM
\r
38 #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
\r
40 #ifndef GET_Y_LPARAM
\r
41 #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
\r
45 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
\r
47 CCrashReport crasher("tortoisegit-bug@googlegroups.com", "Crash Report for TGitCache " APP_X64_STRING " : " STRPRODUCTVER, TRUE);// crash
\r
49 DWORD WINAPI InstanceThread(LPVOID);
\r
50 DWORD WINAPI PipeThread(LPVOID);
\r
51 DWORD WINAPI CommandWaitThread(LPVOID);
\r
52 DWORD WINAPI CommandThread(LPVOID);
\r
53 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
\r
55 NOTIFYICONDATA niData;
\r
58 TCHAR szCurrentCrawledPath[MAX_CRAWLEDPATHS][MAX_CRAWLEDPATHSLEN];
\r
59 int nCurrentCrawledpathIndex = 0;
\r
60 CComAutoCriticalSection critSec;
\r
62 volatile LONG nThreadCount = 0;
\r
64 #define PACKVERSION(major,minor) MAKELONG(minor,major)
\r
65 DWORD GetDllVersion(LPCTSTR lpszDllName)
\r
68 DWORD dwVersion = 0;
\r
70 /* For security purposes, LoadLibrary should be provided with a
\r
71 fully-qualified path to the DLL. The lpszDllName variable should be
\r
72 tested to ensure that it is a fully qualified path before it is used. */
\r
73 hinstDll = LoadLibrary(lpszDllName);
\r
77 DLLGETVERSIONPROC pDllGetVersion;
\r
78 pDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinstDll,
\r
81 /* Because some DLLs might not implement this function, you
\r
82 must test for it explicitly. Depending on the particular
\r
83 DLL, the lack of a DllGetVersion function can be a useful
\r
84 indicator of the version. */
\r
91 SecureZeroMemory(&dvi, sizeof(dvi));
\r
92 dvi.cbSize = sizeof(dvi);
\r
94 hr = (*pDllGetVersion)(&dvi);
\r
98 dwVersion = PACKVERSION(dvi.dwMajorVersion, dvi.dwMinorVersion);
\r
102 FreeLibrary(hinstDll);
\r
107 void DebugOutputLastError()
\r
110 if (!FormatMessage(
\r
111 FORMAT_MESSAGE_ALLOCATE_BUFFER |
\r
112 FORMAT_MESSAGE_FROM_SYSTEM |
\r
113 FORMAT_MESSAGE_IGNORE_INSERTS,
\r
116 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
\r
117 (LPTSTR) &lpMsgBuf,
\r
124 // Display the string.
\r
125 OutputDebugStringA("TGitCache GetLastError(): ");
\r
126 OutputDebugString((LPCTSTR)lpMsgBuf);
\r
127 OutputDebugStringA("\n");
\r
129 // Free the buffer.
\r
130 LocalFree( lpMsgBuf );
\r
133 int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*cmdShow*/)
\r
135 HANDLE hReloadProtection = ::CreateMutex(NULL, FALSE, GetCacheMutexName());
\r
137 if (hReloadProtection == 0 || GetLastError() == ERROR_ALREADY_EXISTS)
\r
139 // An instance of TGitCache is already running
\r
140 ATLTRACE("TGitCache ignoring restart\n");
\r
144 // apr_initialize();
\r
145 // svn_dso_initialize2();
\r
146 g_GitAdminDir.Init();
\r
147 CGitStatusCache::Create();
\r
148 CGitStatusCache::Instance().Init();
\r
150 SecureZeroMemory(szCurrentCrawledPath, sizeof(szCurrentCrawledPath));
\r
153 HANDLE hPipeThread;
\r
154 HANDLE hCommandWaitThread;
\r
156 TCHAR szWindowClass[] = {TSVN_CACHE_WINDOW_NAME};
\r
158 // create a hidden window to receive window messages.
\r
160 wcex.cbSize = sizeof(WNDCLASSEX);
\r
161 wcex.style = CS_HREDRAW | CS_VREDRAW;
\r
162 wcex.lpfnWndProc = (WNDPROC)WndProc;
\r
163 wcex.cbClsExtra = 0;
\r
164 wcex.cbWndExtra = 0;
\r
165 wcex.hInstance = hInstance;
\r
168 wcex.hbrBackground = 0;
\r
169 wcex.lpszMenuName = NULL;
\r
170 wcex.lpszClassName = szWindowClass;
\r
172 RegisterClassEx(&wcex);
\r
173 hWnd = CreateWindow(TSVN_CACHE_WINDOW_NAME, TSVN_CACHE_WINDOW_NAME, WS_CAPTION, 0, 0, 800, 300, NULL, 0, hInstance, 0);
\r
179 if (CRegStdWORD(_T("Software\\TortoiseGit\\CacheTrayIcon"), FALSE)==TRUE)
\r
181 SecureZeroMemory(&niData,sizeof(NOTIFYICONDATA));
\r
183 DWORD dwVersion = GetDllVersion(_T("Shell32.dll"));
\r
185 if (dwVersion >= PACKVERSION(6,0))
\r
186 niData.cbSize = sizeof(NOTIFYICONDATA);
\r
187 else if (dwVersion >= PACKVERSION(5,0))
\r
188 niData.cbSize = NOTIFYICONDATA_V2_SIZE;
\r
190 niData.cbSize = NOTIFYICONDATA_V1_SIZE;
\r
192 niData.uID = TRAY_ID; // own tray icon ID
\r
193 niData.hWnd = hWnd;
\r
194 niData.uFlags = NIF_ICON|NIF_MESSAGE;
\r
198 (HICON)LoadImage(hInstance,
\r
199 MAKEINTRESOURCE(IDI_TSVNCACHE),
\r
201 GetSystemMetrics(SM_CXSMICON),
\r
202 GetSystemMetrics(SM_CYSMICON),
\r
205 // set the message to send
\r
206 // note: the message value should be in the
\r
207 // range of WM_APP through 0xBFFF
\r
208 niData.uCallbackMessage = TRAY_CALLBACK;
\r
209 Shell_NotifyIcon(NIM_ADD,&niData);
\r
210 // free icon handle
\r
211 if(niData.hIcon && DestroyIcon(niData.hIcon))
\r
212 niData.hIcon = NULL;
\r
215 // Create a thread which waits for incoming pipe connections
\r
216 hPipeThread = CreateThread(
\r
217 NULL, // no security attribute
\r
218 0, // default stack size
\r
220 (LPVOID) &bRun, // thread parameter
\r
221 0, // not suspended
\r
222 &dwThreadId); // returns thread ID
\r
224 if (hPipeThread == NULL)
\r
226 //OutputDebugStringA("TSVNCache: Could not create pipe thread\n");
\r
227 //DebugOutputLastError();
\r
230 else CloseHandle(hPipeThread);
\r
232 // Create a thread which waits for incoming pipe connections
\r
233 hCommandWaitThread = CreateThread(
\r
234 NULL, // no security attribute
\r
235 0, // default stack size
\r
236 CommandWaitThread,
\r
237 (LPVOID) &bRun, // thread parameter
\r
238 0, // not suspended
\r
239 &dwThreadId); // returns thread ID
\r
241 if (hCommandWaitThread == NULL)
\r
243 //OutputDebugStringA("TSVNCache: Could not create command wait thread\n");
\r
244 //DebugOutputLastError();
\r
247 else CloseHandle(hCommandWaitThread);
\r
250 // loop to handle window messages.
\r
254 bLoopRet = GetMessage(&msg, NULL, 0, 0);
\r
255 if ((bLoopRet != -1)&&(bLoopRet != 0))
\r
257 DispatchMessage(&msg);
\r
263 Shell_NotifyIcon(NIM_DELETE,&niData);
\r
264 CGitStatusCache::Destroy();
\r
265 g_GitAdminDir.Close();
\r
266 // apr_terminate();
\r
271 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
275 case TRAY_CALLBACK:
\r
279 case WM_LBUTTONDBLCLK:
\r
280 if (IsWindowVisible(hWnd))
\r
281 ShowWindow(hWnd, SW_HIDE);
\r
283 ShowWindow(hWnd, SW_RESTORE);
\r
288 NOTIFYICONDATA SystemTray;
\r
289 sInfoTip.Format(_T("Cached Directories : %ld\nWatched paths : %ld"),
\r
290 CGitStatusCache::Instance().GetCacheSize(),
\r
291 CGitStatusCache::Instance().GetNumberOfWatchedPaths());
\r
293 SystemTray.cbSize = sizeof(NOTIFYICONDATA);
\r
294 SystemTray.hWnd = hTrayWnd;
\r
295 SystemTray.uID = TRAY_ID;
\r
296 SystemTray.uFlags = NIF_TIP;
\r
297 _tcscpy_s(SystemTray.szTip, sInfoTip);
\r
298 Shell_NotifyIcon(NIM_MODIFY, &SystemTray);
\r
302 case WM_CONTEXTMENU:
\r
305 DWORD ptW = GetMessagePos();
\r
306 pt.x = GET_X_LPARAM(ptW);
\r
307 pt.y = GET_Y_LPARAM(ptW);
\r
308 HMENU hMenu = CreatePopupMenu();
\r
311 InsertMenu(hMenu, (UINT)-1, MF_BYPOSITION, TRAYPOP_EXIT, _T("Exit"));
\r
312 SetForegroundWindow(hWnd);
\r
313 TrackPopupMenu(hMenu, TPM_BOTTOMALIGN, pt.x, pt.y, 0, hWnd, NULL);
\r
314 DestroyMenu(hMenu);
\r
324 HDC hdc = BeginPaint(hWnd, &ps);
\r
326 GetClientRect(hWnd, &rect);
\r
327 // clear the background
\r
328 HBRUSH background = CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
\r
329 HGDIOBJ oldbrush = SelectObject(hdc, background);
\r
330 FillRect(hdc, &rect, background);
\r
333 SIZE fontsize = {0};
\r
334 AutoLocker print(critSec);
\r
335 GetTextExtentPoint32( hdc, szCurrentCrawledPath[0], (int)_tcslen(szCurrentCrawledPath[0]), &fontsize );
\r
336 for (int i=nCurrentCrawledpathIndex; i<MAX_CRAWLEDPATHS; ++i)
\r
338 TextOut(hdc, 0, line*fontsize.cy, szCurrentCrawledPath[i], (int)_tcslen(szCurrentCrawledPath[i]));
\r
341 for (int i=0; i<nCurrentCrawledpathIndex; ++i)
\r
343 TextOut(hdc, 0, line*fontsize.cy, szCurrentCrawledPath[i], (int)_tcslen(szCurrentCrawledPath[i]));
\r
348 SelectObject(hdc,oldbrush);
\r
349 EndPaint(hWnd, &ps);
\r
350 DeleteObject(background);
\r
356 WORD wmId = LOWORD(wParam);
\r
361 DestroyWindow(hWnd);
\r
366 case WM_QUERYENDSESSION:
\r
368 ATLTRACE("WM_QUERYENDSESSION\n");
\r
369 if (CGitStatusCache::Instance().WaitToWrite(200))
\r
371 CGitStatusCache::Instance().Stop();
\r
372 CGitStatusCache::Instance().Done();
\r
378 case WM_ENDSESSION:
\r
382 ATLTRACE("WM_CLOSE/DESTROY/ENDSESSION/QUIT\n");
\r
383 CGitStatusCache::Instance().WaitToWrite();
\r
384 CGitStatusCache::Instance().Stop();
\r
385 CGitStatusCache::Instance().SaveCache();
\r
386 if (message != WM_QUIT)
\r
387 PostQuitMessage(0);
\r
392 case WM_DEVICECHANGE:
\r
394 DEV_BROADCAST_HDR * phdr = (DEV_BROADCAST_HDR*)lParam;
\r
397 case DBT_CUSTOMEVENT:
\r
399 ATLTRACE("WM_DEVICECHANGE with DBT_CUSTOMEVENT\n");
\r
400 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
\r
402 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
\r
403 if (IsEqualGUID(phandle->dbch_eventguid, GUID_IO_VOLUME_DISMOUNT))
\r
405 ATLTRACE("Device to be dismounted\n");
\r
406 CGitStatusCache::Instance().WaitToWrite();
\r
407 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
\r
408 CGitStatusCache::Instance().Done();
\r
410 if (IsEqualGUID(phandle->dbch_eventguid, GUID_IO_VOLUME_LOCK))
\r
412 ATLTRACE("Device lock event\n");
\r
413 CGitStatusCache::Instance().WaitToWrite();
\r
414 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
\r
415 CGitStatusCache::Instance().Done();
\r
420 case DBT_DEVICEREMOVEPENDING:
\r
421 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVEPENDING\n");
\r
422 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
\r
424 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
\r
425 CGitStatusCache::Instance().WaitToWrite();
\r
426 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
\r
427 CGitStatusCache::Instance().Done();
\r
431 CGitStatusCache::Instance().WaitToWrite();
\r
432 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
\r
433 CGitStatusCache::Instance().Done();
\r
436 case DBT_DEVICEQUERYREMOVE:
\r
437 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEQUERYREMOVE\n");
\r
438 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
\r
440 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
\r
441 CGitStatusCache::Instance().WaitToWrite();
\r
442 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
\r
443 CGitStatusCache::Instance().Done();
\r
447 CGitStatusCache::Instance().WaitToWrite();
\r
448 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
\r
449 CGitStatusCache::Instance().Done();
\r
452 case DBT_DEVICEREMOVECOMPLETE:
\r
453 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVECOMPLETE\n");
\r
454 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
\r
456 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
\r
457 CGitStatusCache::Instance().WaitToWrite();
\r
458 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
\r
459 CGitStatusCache::Instance().Done();
\r
463 CGitStatusCache::Instance().WaitToWrite();
\r
464 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
\r
465 CGitStatusCache::Instance().Done();
\r
474 return DefWindowProc(hWnd, message, wParam, lParam);
\r
477 //////////////////////////////////////////////////////////////////////////
\r
479 VOID GetAnswerToRequest(const TSVNCacheRequest* pRequest, TSVNCacheResponse* pReply, DWORD* pResponseLength)
\r
482 *pResponseLength = 0;
\r
483 if(pRequest->flags & TSVNCACHE_FLAGS_FOLDERISKNOWN)
\r
485 path.SetFromWin(pRequest->path, !!(pRequest->flags & TSVNCACHE_FLAGS_ISFOLDER));
\r
489 path.SetFromWin(pRequest->path);
\r
492 if (CGitStatusCache::Instance().WaitToRead(2000))
\r
494 CGitStatusCache::Instance().GetStatusForPath(path, pRequest->flags, false).BuildCacheResponse(*pReply, *pResponseLength);
\r
495 CGitStatusCache::Instance().Done();
\r
499 CStatusCacheEntry entry;
\r
500 entry.BuildCacheResponse(*pReply, *pResponseLength);
\r
504 DWORD WINAPI PipeThread(LPVOID lpvParam)
\r
506 ATLTRACE("PipeThread started\n");
\r
507 bool * bRun = (bool *)lpvParam;
\r
508 // The main loop creates an instance of the named pipe and
\r
509 // then waits for a client to connect to it. When the client
\r
510 // connects, a thread is created to handle communications
\r
511 // with that client, and the loop is repeated.
\r
514 HANDLE hPipe = INVALID_HANDLE_VALUE;
\r
515 HANDLE hInstanceThread = INVALID_HANDLE_VALUE;
\r
519 hPipe = CreateNamedPipe(
\r
520 GetCachePipeName(),
\r
521 PIPE_ACCESS_DUPLEX, // read/write access
\r
522 PIPE_TYPE_MESSAGE | // message type pipe
\r
523 PIPE_READMODE_MESSAGE | // message-read mode
\r
524 PIPE_WAIT, // blocking mode
\r
525 PIPE_UNLIMITED_INSTANCES, // max. instances
\r
526 BUFSIZE, // output buffer size
\r
527 BUFSIZE, // input buffer size
\r
528 NMPWAIT_USE_DEFAULT_WAIT, // client time-out
\r
529 NULL); // NULL DACL
\r
531 if (hPipe == INVALID_HANDLE_VALUE)
\r
533 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
\r
534 //DebugOutputLastError();
\r
537 continue; // never leave the thread!
\r
540 // Wait for the client to connect; if it succeeds,
\r
541 // the function returns a nonzero value. If the function returns
\r
542 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
\r
543 fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
\r
546 // Create a thread for this client.
\r
547 hInstanceThread = CreateThread(
\r
548 NULL, // no security attribute
\r
549 0, // default stack size
\r
551 (LPVOID) hPipe, // thread parameter
\r
552 0, // not suspended
\r
553 &dwThreadId); // returns thread ID
\r
555 if (hInstanceThread == NULL)
\r
557 //OutputDebugStringA("TSVNCache: Could not create Instance thread\n");
\r
558 //DebugOutputLastError();
\r
559 DisconnectNamedPipe(hPipe);
\r
560 CloseHandle(hPipe);
\r
561 // since we're now closing this thread, we also have to close the whole application!
\r
562 // otherwise the thread is dead, but the app is still running, refusing new instances
\r
563 // but no pipe will be available anymore.
\r
564 PostMessage(hWnd, WM_CLOSE, 0, 0);
\r
567 else CloseHandle(hInstanceThread);
\r
571 // The client could not connect, so close the pipe.
\r
572 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
\r
573 //DebugOutputLastError();
\r
574 CloseHandle(hPipe);
\r
577 continue; // don't end the thread!
\r
580 ATLTRACE("Pipe thread exited\n");
\r
584 DWORD WINAPI CommandWaitThread(LPVOID lpvParam)
\r
586 ATLTRACE("CommandWaitThread started\n");
\r
587 bool * bRun = (bool *)lpvParam;
\r
588 // The main loop creates an instance of the named pipe and
\r
589 // then waits for a client to connect to it. When the client
\r
590 // connects, a thread is created to handle communications
\r
591 // with that client, and the loop is repeated.
\r
594 HANDLE hPipe = INVALID_HANDLE_VALUE;
\r
595 HANDLE hCommandThread = INVALID_HANDLE_VALUE;
\r
599 hPipe = CreateNamedPipe(
\r
600 GetCacheCommandPipeName(),
\r
601 PIPE_ACCESS_DUPLEX, // read/write access
\r
602 PIPE_TYPE_MESSAGE | // message type pipe
\r
603 PIPE_READMODE_MESSAGE | // message-read mode
\r
604 PIPE_WAIT, // blocking mode
\r
605 PIPE_UNLIMITED_INSTANCES, // max. instances
\r
606 BUFSIZE, // output buffer size
\r
607 BUFSIZE, // input buffer size
\r
608 NMPWAIT_USE_DEFAULT_WAIT, // client time-out
\r
609 NULL); // NULL DACL
\r
611 if (hPipe == INVALID_HANDLE_VALUE)
\r
613 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
\r
614 //DebugOutputLastError();
\r
617 continue; // never leave the thread!
\r
620 // Wait for the client to connect; if it succeeds,
\r
621 // the function returns a nonzero value. If the function returns
\r
622 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
\r
623 fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
\r
626 // Create a thread for this client.
\r
627 hCommandThread = CreateThread(
\r
628 NULL, // no security attribute
\r
629 0, // default stack size
\r
631 (LPVOID) hPipe, // thread parameter
\r
632 0, // not suspended
\r
633 &dwThreadId); // returns thread ID
\r
635 if (hCommandThread == NULL)
\r
637 //OutputDebugStringA("TSVNCache: Could not create Command thread\n");
\r
638 //DebugOutputLastError();
\r
639 DisconnectNamedPipe(hPipe);
\r
640 CloseHandle(hPipe);
\r
641 // since we're now closing this thread, we also have to close the whole application!
\r
642 // otherwise the thread is dead, but the app is still running, refusing new instances
\r
643 // but no pipe will be available anymore.
\r
644 PostMessage(hWnd, WM_CLOSE, 0, 0);
\r
647 else CloseHandle(hCommandThread);
\r
651 // The client could not connect, so close the pipe.
\r
652 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
\r
653 //DebugOutputLastError();
\r
654 CloseHandle(hPipe);
\r
657 continue; // don't end the thread!
\r
660 ATLTRACE("CommandWait thread exited\n");
\r
664 DWORD WINAPI InstanceThread(LPVOID lpvParam)
\r
666 ATLTRACE("InstanceThread started\n");
\r
667 TSVNCacheResponse response;
\r
668 DWORD cbBytesRead, cbWritten;
\r
672 // The thread's parameter is a handle to a pipe instance.
\r
674 hPipe = (HANDLE) lpvParam;
\r
675 InterlockedIncrement(&nThreadCount);
\r
678 // Read client requests from the pipe.
\r
679 TSVNCacheRequest request;
\r
680 fSuccess = ReadFile(
\r
681 hPipe, // handle to pipe
\r
682 &request, // buffer to receive data
\r
683 sizeof(request), // size of buffer
\r
684 &cbBytesRead, // number of bytes read
\r
685 NULL); // not overlapped I/O
\r
687 if (! fSuccess || cbBytesRead == 0)
\r
689 DisconnectNamedPipe(hPipe);
\r
690 CloseHandle(hPipe);
\r
691 ATLTRACE("Instance thread exited\n");
\r
692 InterlockedDecrement(&nThreadCount);
\r
693 if (nThreadCount == 0)
\r
694 PostMessage(hWnd, WM_CLOSE, 0, 0);
\r
698 DWORD responseLength;
\r
699 GetAnswerToRequest(&request, &response, &responseLength);
\r
701 // Write the reply to the pipe.
\r
702 fSuccess = WriteFile(
\r
703 hPipe, // handle to pipe
\r
704 &response, // buffer to write from
\r
705 responseLength, // number of bytes to write
\r
706 &cbWritten, // number of bytes written
\r
707 NULL); // not overlapped I/O
\r
709 if (! fSuccess || responseLength != cbWritten)
\r
711 DisconnectNamedPipe(hPipe);
\r
712 CloseHandle(hPipe);
\r
713 ATLTRACE("Instance thread exited\n");
\r
714 InterlockedDecrement(&nThreadCount);
\r
715 if (nThreadCount == 0)
\r
716 PostMessage(hWnd, WM_CLOSE, 0, 0);
\r
721 // Flush the pipe to allow the client to read the pipe's contents
\r
722 // before disconnecting. Then disconnect the pipe, and close the
\r
723 // handle to this pipe instance.
\r
725 FlushFileBuffers(hPipe);
\r
726 DisconnectNamedPipe(hPipe);
\r
727 CloseHandle(hPipe);
\r
728 ATLTRACE("Instance thread exited\n");
\r
729 InterlockedDecrement(&nThreadCount);
\r
730 if (nThreadCount == 0)
\r
731 PostMessage(hWnd, WM_CLOSE, 0, 0);
\r
735 DWORD WINAPI CommandThread(LPVOID lpvParam)
\r
737 ATLTRACE("CommandThread started\n");
\r
738 DWORD cbBytesRead;
\r
742 // The thread's parameter is a handle to a pipe instance.
\r
744 hPipe = (HANDLE) lpvParam;
\r
748 // Read client requests from the pipe.
\r
749 TSVNCacheCommand command;
\r
750 fSuccess = ReadFile(
\r
751 hPipe, // handle to pipe
\r
752 &command, // buffer to receive data
\r
753 sizeof(command), // size of buffer
\r
754 &cbBytesRead, // number of bytes read
\r
755 NULL); // not overlapped I/O
\r
757 if (! fSuccess || cbBytesRead == 0)
\r
759 DisconnectNamedPipe(hPipe);
\r
760 CloseHandle(hPipe);
\r
761 ATLTRACE("Command thread exited\n");
\r
765 switch (command.command)
\r
767 case TSVNCACHECOMMAND_END:
\r
768 FlushFileBuffers(hPipe);
\r
769 DisconnectNamedPipe(hPipe);
\r
770 CloseHandle(hPipe);
\r
771 ATLTRACE("Command thread exited\n");
\r
773 case TSVNCACHECOMMAND_CRAWL:
\r
775 CTGitPath changedpath;
\r
776 changedpath.SetFromWin(CString(command.path), true);
\r
777 // remove the path from our cache - that will 'invalidate' it.
\r
778 CGitStatusCache::Instance().WaitToWrite();
\r
779 CGitStatusCache::Instance().RemoveCacheForPath(changedpath);
\r
780 CGitStatusCache::Instance().Done();
\r
781 CGitStatusCache::Instance().AddFolderForCrawling(changedpath.GetDirectory());
\r
784 case TSVNCACHECOMMAND_REFRESHALL:
\r
785 CGitStatusCache::Instance().WaitToWrite();
\r
786 CGitStatusCache::Instance().Refresh();
\r
787 CGitStatusCache::Instance().Done();
\r
789 case TSVNCACHECOMMAND_RELEASE:
\r
791 CTGitPath changedpath;
\r
792 changedpath.SetFromWin(CString(command.path), true);
\r
793 ATLTRACE(_T("release handle for path %s\n"), changedpath.GetWinPath());
\r
794 CGitStatusCache::Instance().WaitToWrite();
\r
795 CGitStatusCache::Instance().CloseWatcherHandles(changedpath);
\r
796 CGitStatusCache::Instance().RemoveCacheForPath(changedpath);
\r
797 CGitStatusCache::Instance().Done();
\r
804 // Flush the pipe to allow the client to read the pipe's contents
\r
805 // before disconnecting. Then disconnect the pipe, and close the
\r
806 // handle to this pipe instance.
\r
808 FlushFileBuffers(hPipe);
\r
809 DisconnectNamedPipe(hPipe);
\r
810 CloseHandle(hPipe);
\r
811 ATLTRACE("Command thread exited\n");
\r