disable drag'n'drop handler in explorer for folders, until we have a fix for issue...
[TortoiseGit.git] / src / TGitCache / TSVNCache.cpp
bloba0b0f75f0e10e0253a620198fb0bb56f160130c6
1 // TortoiseGit - a Windows shell extension for easy version control
3 // External Cache Copyright (C) 2005 - 2006 - 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 "TSVNCache.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"
36 #include <ShellAPI.h>
38 #ifndef GET_X_LPARAM
39 #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
40 #endif
41 #ifndef GET_Y_LPARAM
42 #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
43 #endif
46 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
48 CCrashReport crasher("tortoisegit-bug@googlegroups.com", "Crash Report for TGitCache " APP_X64_STRING " : " STRPRODUCTVER, TRUE);// crash
50 DWORD WINAPI InstanceThread(LPVOID);
51 DWORD WINAPI PipeThread(LPVOID);
52 DWORD WINAPI CommandWaitThread(LPVOID);
53 DWORD WINAPI CommandThread(LPVOID);
54 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
55 bool bRun = true;
56 NOTIFYICONDATA niData;
57 HWND hWnd;
58 HWND hTrayWnd;
59 TCHAR szCurrentCrawledPath[MAX_CRAWLEDPATHS][MAX_CRAWLEDPATHSLEN];
60 int nCurrentCrawledpathIndex = 0;
61 CComAutoCriticalSection critSec;
63 volatile LONG nThreadCount = 0;
65 #define PACKVERSION(major,minor) MAKELONG(minor,major)
66 DWORD GetDllVersion(LPCTSTR lpszDllName)
68 HINSTANCE hinstDll;
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 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 FreeLibrary(hinstDll);
105 return dwVersion;
108 void DebugOutputLastError()
110 LPVOID lpMsgBuf;
111 if (!FormatMessage(
112 FORMAT_MESSAGE_ALLOCATE_BUFFER |
113 FORMAT_MESSAGE_FROM_SYSTEM |
114 FORMAT_MESSAGE_IGNORE_INSERTS,
115 NULL,
116 GetLastError(),
117 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
118 (LPTSTR) &lpMsgBuf,
120 NULL ))
122 return;
125 // Display the string.
126 OutputDebugStringA("TGitCache GetLastError(): ");
127 OutputDebugString((LPCTSTR)lpMsgBuf);
128 OutputDebugStringA("\n");
130 // Free the buffer.
131 LocalFree( lpMsgBuf );
134 int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*cmdShow*/)
136 SetDllDirectory(L"");
137 HANDLE hReloadProtection = ::CreateMutex(NULL, FALSE, GetCacheMutexName());
139 if (hReloadProtection == 0 || GetLastError() == ERROR_ALREADY_EXISTS)
141 // An instance of TGitCache is already running
142 ATLTRACE("TGitCache ignoring restart\n");
143 return 0;
146 // apr_initialize();
147 // svn_dso_initialize2();
148 g_GitAdminDir.Init();
149 CGitStatusCache::Create();
150 CGitStatusCache::Instance().Init();
152 SecureZeroMemory(szCurrentCrawledPath, sizeof(szCurrentCrawledPath));
154 DWORD dwThreadId;
155 HANDLE hPipeThread;
156 HANDLE hCommandWaitThread;
157 MSG msg;
158 TCHAR szWindowClass[] = {TSVN_CACHE_WINDOW_NAME};
160 // create a hidden window to receive window messages.
161 WNDCLASSEX wcex;
162 wcex.cbSize = sizeof(WNDCLASSEX);
163 wcex.style = CS_HREDRAW | CS_VREDRAW;
164 wcex.lpfnWndProc = (WNDPROC)WndProc;
165 wcex.cbClsExtra = 0;
166 wcex.cbWndExtra = 0;
167 wcex.hInstance = hInstance;
168 wcex.hIcon = 0;
169 wcex.hCursor = 0;
170 wcex.hbrBackground = 0;
171 wcex.lpszMenuName = NULL;
172 wcex.lpszClassName = szWindowClass;
173 wcex.hIconSm = 0;
174 RegisterClassEx(&wcex);
175 hWnd = CreateWindow(TSVN_CACHE_WINDOW_NAME, TSVN_CACHE_WINDOW_NAME, WS_CAPTION, 0, 0, 800, 300, NULL, 0, hInstance, 0);
176 hTrayWnd = hWnd;
177 if (hWnd == NULL)
179 return 0;
181 if (CRegStdDWORD(_T("Software\\TortoiseGit\\CacheTrayIcon"), FALSE)==TRUE)
183 SecureZeroMemory(&niData,sizeof(NOTIFYICONDATA));
185 DWORD dwVersion = GetDllVersion(_T("Shell32.dll"));
187 if (dwVersion >= PACKVERSION(6,0))
188 niData.cbSize = sizeof(NOTIFYICONDATA);
189 else if (dwVersion >= PACKVERSION(5,0))
190 niData.cbSize = NOTIFYICONDATA_V2_SIZE;
191 else
192 niData.cbSize = NOTIFYICONDATA_V1_SIZE;
194 niData.uID = TRAY_ID; // own tray icon ID
195 niData.hWnd = hWnd;
196 niData.uFlags = NIF_ICON|NIF_MESSAGE;
198 // load the icon
199 niData.hIcon =
200 (HICON)LoadImage(hInstance,
201 MAKEINTRESOURCE(IDI_TSVNCACHE),
202 IMAGE_ICON,
203 GetSystemMetrics(SM_CXSMICON),
204 GetSystemMetrics(SM_CYSMICON),
205 LR_DEFAULTCOLOR);
207 // set the message to send
208 // note: the message value should be in the
209 // range of WM_APP through 0xBFFF
210 niData.uCallbackMessage = TRAY_CALLBACK;
211 Shell_NotifyIcon(NIM_ADD,&niData);
212 // free icon handle
213 if(niData.hIcon && DestroyIcon(niData.hIcon))
214 niData.hIcon = NULL;
217 // Create a thread which waits for incoming pipe connections
218 hPipeThread = CreateThread(
219 NULL, // no security attribute
220 0, // default stack size
221 PipeThread,
222 (LPVOID) &bRun, // thread parameter
223 0, // not suspended
224 &dwThreadId); // returns thread ID
226 if (hPipeThread == NULL)
228 //OutputDebugStringA("TSVNCache: Could not create pipe thread\n");
229 //DebugOutputLastError();
230 return 0;
232 else CloseHandle(hPipeThread);
234 // Create a thread which waits for incoming pipe connections
235 hCommandWaitThread = CreateThread(
236 NULL, // no security attribute
237 0, // default stack size
238 CommandWaitThread,
239 (LPVOID) &bRun, // thread parameter
240 0, // not suspended
241 &dwThreadId); // returns thread ID
243 if (hCommandWaitThread == NULL)
245 //OutputDebugStringA("TSVNCache: Could not create command wait thread\n");
246 //DebugOutputLastError();
247 return 0;
249 else CloseHandle(hCommandWaitThread);
252 // loop to handle window messages.
253 BOOL bLoopRet;
254 while (bRun)
256 bLoopRet = GetMessage(&msg, NULL, 0, 0);
257 if ((bLoopRet != -1)&&(bLoopRet != 0))
259 DispatchMessage(&msg);
263 bRun = false;
265 Shell_NotifyIcon(NIM_DELETE,&niData);
266 CGitStatusCache::Destroy();
267 g_GitAdminDir.Close();
268 // apr_terminate();
270 return 0;
273 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
275 switch (message)
277 case TRAY_CALLBACK:
279 switch(lParam)
281 case WM_LBUTTONDBLCLK:
282 if (IsWindowVisible(hWnd))
283 ShowWindow(hWnd, SW_HIDE);
284 else
285 ShowWindow(hWnd, SW_RESTORE);
286 break;
287 case WM_MOUSEMOVE:
289 CString sInfoTip;
290 NOTIFYICONDATA SystemTray;
291 sInfoTip.Format(_T("Cached Directories : %ld\nWatched paths : %ld"),
292 CGitStatusCache::Instance().GetCacheSize(),
293 CGitStatusCache::Instance().GetNumberOfWatchedPaths());
295 SystemTray.cbSize = sizeof(NOTIFYICONDATA);
296 SystemTray.hWnd = hTrayWnd;
297 SystemTray.uID = TRAY_ID;
298 SystemTray.uFlags = NIF_TIP;
299 _tcscpy_s(SystemTray.szTip, sInfoTip);
300 Shell_NotifyIcon(NIM_MODIFY, &SystemTray);
302 break;
303 case WM_RBUTTONUP:
304 case WM_CONTEXTMENU:
306 POINT pt;
307 DWORD ptW = GetMessagePos();
308 pt.x = GET_X_LPARAM(ptW);
309 pt.y = GET_Y_LPARAM(ptW);
310 HMENU hMenu = CreatePopupMenu();
311 if(hMenu)
313 InsertMenu(hMenu, (UINT)-1, MF_BYPOSITION, TRAYPOP_EXIT, _T("Exit"));
314 SetForegroundWindow(hWnd);
315 TrackPopupMenu(hMenu, TPM_BOTTOMALIGN, pt.x, pt.y, 0, hWnd, NULL);
316 DestroyMenu(hMenu);
319 break;
322 break;
323 case WM_PAINT:
325 PAINTSTRUCT ps;
326 HDC hdc = BeginPaint(hWnd, &ps);
327 RECT rect;
328 GetClientRect(hWnd, &rect);
329 // clear the background
330 HBRUSH background = CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
331 HGDIOBJ oldbrush = SelectObject(hdc, background);
332 FillRect(hdc, &rect, background);
334 int line = 0;
335 SIZE fontsize = {0};
336 AutoLocker print(critSec);
337 GetTextExtentPoint32( hdc, szCurrentCrawledPath[0], (int)_tcslen(szCurrentCrawledPath[0]), &fontsize );
338 for (int i=nCurrentCrawledpathIndex; i<MAX_CRAWLEDPATHS; ++i)
340 TextOut(hdc, 0, line*fontsize.cy, szCurrentCrawledPath[i], (int)_tcslen(szCurrentCrawledPath[i]));
341 line++;
343 for (int i=0; i<nCurrentCrawledpathIndex; ++i)
345 TextOut(hdc, 0, line*fontsize.cy, szCurrentCrawledPath[i], (int)_tcslen(szCurrentCrawledPath[i]));
346 line++;
350 SelectObject(hdc,oldbrush);
351 EndPaint(hWnd, &ps);
352 DeleteObject(background);
353 return 0L;
355 break;
356 case WM_COMMAND:
358 WORD wmId = LOWORD(wParam);
360 switch (wmId)
362 case TRAYPOP_EXIT:
363 DestroyWindow(hWnd);
364 break;
366 return 1;
368 case WM_QUERYENDSESSION:
370 ATLTRACE("WM_QUERYENDSESSION\n");
371 if (CGitStatusCache::Instance().WaitToWrite(200))
373 CGitStatusCache::Instance().Stop();
374 CGitStatusCache::Instance().Done();
376 return TRUE;
378 break;
379 case WM_CLOSE:
380 case WM_ENDSESSION:
381 case WM_DESTROY:
382 case WM_QUIT:
384 ATLTRACE("WM_CLOSE/DESTROY/ENDSESSION/QUIT\n");
385 CGitStatusCache::Instance().WaitToWrite();
386 CGitStatusCache::Instance().Stop();
387 CGitStatusCache::Instance().SaveCache();
388 if (message != WM_QUIT)
389 PostQuitMessage(0);
390 bRun = false;
391 return 1;
393 break;
394 case WM_DEVICECHANGE:
396 DEV_BROADCAST_HDR * phdr = (DEV_BROADCAST_HDR*)lParam;
397 switch (wParam)
399 case DBT_CUSTOMEVENT:
401 ATLTRACE("WM_DEVICECHANGE with DBT_CUSTOMEVENT\n");
402 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
404 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
405 if (IsEqualGUID(phandle->dbch_eventguid, GUID_IO_VOLUME_DISMOUNT))
407 ATLTRACE("Device to be dismounted\n");
408 CGitStatusCache::Instance().WaitToWrite();
409 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
410 CGitStatusCache::Instance().Done();
412 if (IsEqualGUID(phandle->dbch_eventguid, GUID_IO_VOLUME_LOCK))
414 ATLTRACE("Device lock event\n");
415 CGitStatusCache::Instance().WaitToWrite();
416 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
417 CGitStatusCache::Instance().Done();
421 break;
422 case DBT_DEVICEREMOVEPENDING:
423 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVEPENDING\n");
424 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
426 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
427 CGitStatusCache::Instance().WaitToWrite();
428 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
429 CGitStatusCache::Instance().Done();
431 else
433 CGitStatusCache::Instance().WaitToWrite();
434 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
435 CGitStatusCache::Instance().Done();
437 break;
438 case DBT_DEVICEQUERYREMOVE:
439 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEQUERYREMOVE\n");
440 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
442 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
443 CGitStatusCache::Instance().WaitToWrite();
444 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
445 CGitStatusCache::Instance().Done();
447 else
449 CGitStatusCache::Instance().WaitToWrite();
450 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
451 CGitStatusCache::Instance().Done();
453 break;
454 case DBT_DEVICEREMOVECOMPLETE:
455 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVECOMPLETE\n");
456 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
458 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
459 CGitStatusCache::Instance().WaitToWrite();
460 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
461 CGitStatusCache::Instance().Done();
463 else
465 CGitStatusCache::Instance().WaitToWrite();
466 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
467 CGitStatusCache::Instance().Done();
469 break;
472 break;
473 default:
474 break;
476 return DefWindowProc(hWnd, message, wParam, lParam);
479 //////////////////////////////////////////////////////////////////////////
481 VOID GetAnswerToRequest(const TSVNCacheRequest* pRequest, TSVNCacheResponse* pReply, DWORD* pResponseLength)
483 CTGitPath path;
484 *pResponseLength = 0;
485 if(pRequest->flags & TSVNCACHE_FLAGS_FOLDERISKNOWN)
487 path.SetFromWin(pRequest->path, !!(pRequest->flags & TSVNCACHE_FLAGS_ISFOLDER));
489 else
491 path.SetFromWin(pRequest->path);
494 if (CGitStatusCache::Instance().WaitToRead(2000))
496 CGitStatusCache::Instance().GetStatusForPath(path, pRequest->flags, false).BuildCacheResponse(*pReply, *pResponseLength);
497 CGitStatusCache::Instance().Done();
499 else
501 CStatusCacheEntry entry;
502 entry.BuildCacheResponse(*pReply, *pResponseLength);
506 DWORD WINAPI PipeThread(LPVOID lpvParam)
508 ATLTRACE("PipeThread started\n");
509 bool * bRun = (bool *)lpvParam;
510 // The main loop creates an instance of the named pipe and
511 // then waits for a client to connect to it. When the client
512 // connects, a thread is created to handle communications
513 // with that client, and the loop is repeated.
514 DWORD dwThreadId;
515 BOOL fConnected;
516 HANDLE hPipe = INVALID_HANDLE_VALUE;
517 HANDLE hInstanceThread = INVALID_HANDLE_VALUE;
519 while (*bRun)
521 hPipe = CreateNamedPipe(
522 GetCachePipeName(),
523 PIPE_ACCESS_DUPLEX, // read/write access
524 PIPE_TYPE_MESSAGE | // message type pipe
525 PIPE_READMODE_MESSAGE | // message-read mode
526 PIPE_WAIT, // blocking mode
527 PIPE_UNLIMITED_INSTANCES, // max. instances
528 BUFSIZE, // output buffer size
529 BUFSIZE, // input buffer size
530 NMPWAIT_USE_DEFAULT_WAIT, // client time-out
531 NULL); // NULL DACL
533 if (hPipe == INVALID_HANDLE_VALUE)
535 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
536 //DebugOutputLastError();
537 if (*bRun)
538 Sleep(200);
539 continue; // never leave the thread!
542 // Wait for the client to connect; if it succeeds,
543 // the function returns a nonzero value. If the function returns
544 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
545 fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
546 if (fConnected)
548 // Create a thread for this client.
549 hInstanceThread = CreateThread(
550 NULL, // no security attribute
551 0, // default stack size
552 InstanceThread,
553 (LPVOID) hPipe, // thread parameter
554 0, // not suspended
555 &dwThreadId); // returns thread ID
557 if (hInstanceThread == NULL)
559 //OutputDebugStringA("TSVNCache: Could not create Instance thread\n");
560 //DebugOutputLastError();
561 DisconnectNamedPipe(hPipe);
562 CloseHandle(hPipe);
563 // since we're now closing this thread, we also have to close the whole application!
564 // otherwise the thread is dead, but the app is still running, refusing new instances
565 // but no pipe will be available anymore.
566 PostMessage(hWnd, WM_CLOSE, 0, 0);
567 return 1;
569 else CloseHandle(hInstanceThread);
571 else
573 // The client could not connect, so close the pipe.
574 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
575 //DebugOutputLastError();
576 CloseHandle(hPipe);
577 if (*bRun)
578 Sleep(200);
579 continue; // don't end the thread!
582 ATLTRACE("Pipe thread exited\n");
583 return 0;
586 DWORD WINAPI CommandWaitThread(LPVOID lpvParam)
588 ATLTRACE("CommandWaitThread started\n");
589 bool * bRun = (bool *)lpvParam;
590 // The main loop creates an instance of the named pipe and
591 // then waits for a client to connect to it. When the client
592 // connects, a thread is created to handle communications
593 // with that client, and the loop is repeated.
594 DWORD dwThreadId;
595 BOOL fConnected;
596 HANDLE hPipe = INVALID_HANDLE_VALUE;
597 HANDLE hCommandThread = INVALID_HANDLE_VALUE;
599 while (*bRun)
601 hPipe = CreateNamedPipe(
602 GetCacheCommandPipeName(),
603 PIPE_ACCESS_DUPLEX, // read/write access
604 PIPE_TYPE_MESSAGE | // message type pipe
605 PIPE_READMODE_MESSAGE | // message-read mode
606 PIPE_WAIT, // blocking mode
607 PIPE_UNLIMITED_INSTANCES, // max. instances
608 BUFSIZE, // output buffer size
609 BUFSIZE, // input buffer size
610 NMPWAIT_USE_DEFAULT_WAIT, // client time-out
611 NULL); // NULL DACL
613 if (hPipe == INVALID_HANDLE_VALUE)
615 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
616 //DebugOutputLastError();
617 if (*bRun)
618 Sleep(200);
619 continue; // never leave the thread!
622 // Wait for the client to connect; if it succeeds,
623 // the function returns a nonzero value. If the function returns
624 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
625 fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
626 if (fConnected)
628 // Create a thread for this client.
629 hCommandThread = CreateThread(
630 NULL, // no security attribute
631 0, // default stack size
632 CommandThread,
633 (LPVOID) hPipe, // thread parameter
634 0, // not suspended
635 &dwThreadId); // returns thread ID
637 if (hCommandThread == NULL)
639 //OutputDebugStringA("TSVNCache: Could not create Command thread\n");
640 //DebugOutputLastError();
641 DisconnectNamedPipe(hPipe);
642 CloseHandle(hPipe);
643 // since we're now closing this thread, we also have to close the whole application!
644 // otherwise the thread is dead, but the app is still running, refusing new instances
645 // but no pipe will be available anymore.
646 PostMessage(hWnd, WM_CLOSE, 0, 0);
647 return 1;
649 else CloseHandle(hCommandThread);
651 else
653 // The client could not connect, so close the pipe.
654 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
655 //DebugOutputLastError();
656 CloseHandle(hPipe);
657 if (*bRun)
658 Sleep(200);
659 continue; // don't end the thread!
662 ATLTRACE("CommandWait thread exited\n");
663 return 0;
666 DWORD WINAPI InstanceThread(LPVOID lpvParam)
668 ATLTRACE("InstanceThread started\n");
669 TSVNCacheResponse response;
670 DWORD cbBytesRead, cbWritten;
671 BOOL fSuccess;
672 HANDLE hPipe;
674 // The thread's parameter is a handle to a pipe instance.
676 hPipe = (HANDLE) lpvParam;
677 InterlockedIncrement(&nThreadCount);
678 while (bRun)
680 // Read client requests from the pipe.
681 TSVNCacheRequest request;
682 fSuccess = ReadFile(
683 hPipe, // handle to pipe
684 &request, // buffer to receive data
685 sizeof(request), // size of buffer
686 &cbBytesRead, // number of bytes read
687 NULL); // not overlapped I/O
689 if (! fSuccess || cbBytesRead == 0)
691 DisconnectNamedPipe(hPipe);
692 CloseHandle(hPipe);
693 ATLTRACE("Instance thread exited\n");
694 InterlockedDecrement(&nThreadCount);
695 if (nThreadCount == 0)
696 PostMessage(hWnd, WM_CLOSE, 0, 0);
697 return 1;
700 DWORD responseLength;
701 GetAnswerToRequest(&request, &response, &responseLength);
703 // Write the reply to the pipe.
704 fSuccess = WriteFile(
705 hPipe, // handle to pipe
706 &response, // buffer to write from
707 responseLength, // number of bytes to write
708 &cbWritten, // number of bytes written
709 NULL); // not overlapped I/O
711 if (! fSuccess || responseLength != cbWritten)
713 DisconnectNamedPipe(hPipe);
714 CloseHandle(hPipe);
715 ATLTRACE("Instance thread exited\n");
716 InterlockedDecrement(&nThreadCount);
717 if (nThreadCount == 0)
718 PostMessage(hWnd, WM_CLOSE, 0, 0);
719 return 1;
723 // Flush the pipe to allow the client to read the pipe's contents
724 // before disconnecting. Then disconnect the pipe, and close the
725 // handle to this pipe instance.
727 FlushFileBuffers(hPipe);
728 DisconnectNamedPipe(hPipe);
729 CloseHandle(hPipe);
730 ATLTRACE("Instance thread exited\n");
731 InterlockedDecrement(&nThreadCount);
732 if (nThreadCount == 0)
733 PostMessage(hWnd, WM_CLOSE, 0, 0);
734 return 0;
737 DWORD WINAPI CommandThread(LPVOID lpvParam)
739 ATLTRACE("CommandThread started\n");
740 DWORD cbBytesRead;
741 BOOL fSuccess;
742 HANDLE hPipe;
744 // The thread's parameter is a handle to a pipe instance.
746 hPipe = (HANDLE) lpvParam;
748 while (bRun)
750 // Read client requests from the pipe.
751 TSVNCacheCommand command;
752 fSuccess = ReadFile(
753 hPipe, // handle to pipe
754 &command, // buffer to receive data
755 sizeof(command), // size of buffer
756 &cbBytesRead, // number of bytes read
757 NULL); // not overlapped I/O
759 if (! fSuccess || cbBytesRead == 0)
761 DisconnectNamedPipe(hPipe);
762 CloseHandle(hPipe);
763 ATLTRACE("Command thread exited\n");
764 return 1;
767 switch (command.command)
769 case TSVNCACHECOMMAND_END:
770 FlushFileBuffers(hPipe);
771 DisconnectNamedPipe(hPipe);
772 CloseHandle(hPipe);
773 ATLTRACE("Command thread exited\n");
774 return 0;
775 case TSVNCACHECOMMAND_CRAWL:
777 CTGitPath changedpath;
778 changedpath.SetFromWin(CString(command.path), true);
779 // remove the path from our cache - that will 'invalidate' it.
780 CGitStatusCache::Instance().WaitToWrite();
781 CGitStatusCache::Instance().RemoveCacheForPath(changedpath);
782 CGitStatusCache::Instance().Done();
783 CGitStatusCache::Instance().AddFolderForCrawling(changedpath.GetDirectory());
785 break;
786 case TSVNCACHECOMMAND_REFRESHALL:
787 CGitStatusCache::Instance().WaitToWrite();
788 CGitStatusCache::Instance().Refresh();
789 CGitStatusCache::Instance().Done();
790 break;
791 case TSVNCACHECOMMAND_RELEASE:
793 CTGitPath changedpath;
794 changedpath.SetFromWin(CString(command.path), true);
795 ATLTRACE(_T("release handle for path %s\n"), changedpath.GetWinPath());
796 CGitStatusCache::Instance().WaitToWrite();
797 CGitStatusCache::Instance().CloseWatcherHandles(changedpath);
798 CGitStatusCache::Instance().RemoveCacheForPath(changedpath);
799 CGitStatusCache::Instance().Done();
801 break;
806 // Flush the pipe to allow the client to read the pipe's contents
807 // before disconnecting. Then disconnect the pipe, and close the
808 // handle to this pipe instance.
810 FlushFileBuffers(hPipe);
811 DisconnectNamedPipe(hPipe);
812 CloseHandle(hPipe);
813 ATLTRACE("Command thread exited\n");
814 return 0;