Fixed issue #1226: Show log crash when path contain some unicode symbols
[TortoiseGit.git] / src / TGitCache / TGITCache.cpp
blobef9103d2b5a0cd39c02f96b581153eea3bee8fd7
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-2012 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "stdafx.h"
22 #include "shellapi.h"
23 #include "TGITCache.h"
24 #include "GitStatusCache.h"
25 #include "CacheInterface.h"
26 #include "Resource.h"
27 #include "registry.h"
28 #include "CrashReport.h"
29 #include "GitAdminDir.h"
30 #include "Dbt.h"
31 #include <initguid.h>
32 #include "ioevent.h"
33 #include "..\version.h"
34 //#include "svn_dso.h"
35 #include "SmartHandle.h"
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 CCrashReportTGit crasher(L"TGitCache " _T(APP_X64_STRING));
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)
68 void DebugOutputLastError()
70 LPVOID lpMsgBuf;
71 if (!FormatMessage(
72 FORMAT_MESSAGE_ALLOCATE_BUFFER |
73 FORMAT_MESSAGE_FROM_SYSTEM |
74 FORMAT_MESSAGE_IGNORE_INSERTS,
75 NULL,
76 GetLastError(),
77 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
78 (LPTSTR) &lpMsgBuf,
80 NULL ))
82 return;
85 // Display the string.
86 OutputDebugStringA("TGitCache GetLastError(): ");
87 OutputDebugString((LPCTSTR)lpMsgBuf);
88 OutputDebugStringA("\n");
90 // Free the buffer.
91 LocalFree( lpMsgBuf );
94 int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*cmdShow*/)
96 SetDllDirectory(L"");
97 CAutoGeneralHandle hReloadProtection = ::CreateMutex(NULL, FALSE, GetCacheMutexName());
99 if ((!hReloadProtection) || (GetLastError() == ERROR_ALREADY_EXISTS))
101 // An instance of TGitCache is already running
102 ATLTRACE("TGitCache ignoring restart\n");
103 return 0;
106 // apr_initialize();
107 // svn_dso_initialize2();
108 g_GitAdminDir.Init();
109 CGitStatusCache::Create();
110 CGitStatusCache::Instance().Init();
112 SecureZeroMemory(szCurrentCrawledPath, sizeof(szCurrentCrawledPath));
114 DWORD dwThreadId;
115 MSG msg;
116 TCHAR szWindowClass[] = {TGIT_CACHE_WINDOW_NAME};
118 // create a hidden window to receive window messages.
119 WNDCLASSEX wcex;
120 wcex.cbSize = sizeof(WNDCLASSEX);
121 wcex.style = CS_HREDRAW | CS_VREDRAW;
122 wcex.lpfnWndProc = (WNDPROC)WndProc;
123 wcex.cbClsExtra = 0;
124 wcex.cbWndExtra = 0;
125 wcex.hInstance = hInstance;
126 wcex.hIcon = 0;
127 wcex.hCursor = 0;
128 wcex.hbrBackground = 0;
129 wcex.lpszMenuName = NULL;
130 wcex.lpszClassName = szWindowClass;
131 wcex.hIconSm = 0;
132 RegisterClassEx(&wcex);
133 hWnd = CreateWindow(TGIT_CACHE_WINDOW_NAME, TGIT_CACHE_WINDOW_NAME, WS_CAPTION, 0, 0, 800, 300, NULL, 0, hInstance, 0);
134 hTrayWnd = hWnd;
135 if (hWnd == NULL)
137 return 0;
139 if (CRegStdDWORD(_T("Software\\TortoiseGit\\CacheTrayIcon"), FALSE)==TRUE)
141 SecureZeroMemory(&niData,sizeof(NOTIFYICONDATA));
143 DWORD dwMajor = 0;
144 DWORD dwMinor = 0;
145 AtlGetShellVersion(&dwMajor, &dwMinor);
146 DWORD dwVersion = PACKVERSION(dwMajor, dwMinor);
147 if (dwVersion >= PACKVERSION(6,0))
148 niData.cbSize = sizeof(NOTIFYICONDATA);
149 else if (dwVersion >= PACKVERSION(5,0))
150 niData.cbSize = NOTIFYICONDATA_V2_SIZE;
151 else
152 niData.cbSize = NOTIFYICONDATA_V1_SIZE;
154 niData.uID = TRAY_ID; // own tray icon ID
155 niData.hWnd = hWnd;
156 niData.uFlags = NIF_ICON|NIF_MESSAGE;
158 // load the icon
159 niData.hIcon =
160 (HICON)LoadImage(hInstance,
161 MAKEINTRESOURCE(IDI_TGITCACHE),
162 IMAGE_ICON,
163 GetSystemMetrics(SM_CXSMICON),
164 GetSystemMetrics(SM_CYSMICON),
165 LR_DEFAULTCOLOR);
167 // set the message to send
168 // note: the message value should be in the
169 // range of WM_APP through 0xBFFF
170 niData.uCallbackMessage = TRAY_CALLBACK;
171 Shell_NotifyIcon(NIM_ADD,&niData);
172 // free icon handle
173 if(niData.hIcon && DestroyIcon(niData.hIcon))
174 niData.hIcon = NULL;
177 // Create a thread which waits for incoming pipe connections
178 CAutoGeneralHandle hPipeThread = CreateThread(
179 NULL, // no security attribute
180 0, // default stack size
181 PipeThread,
182 (LPVOID) &bRun, // thread parameter
183 0, // not suspended
184 &dwThreadId); // returns thread ID
186 if (!hPipeThread)
188 //OutputDebugStringA("TSVNCache: Could not create pipe thread\n");
189 //DebugOutputLastError();
190 return 0;
192 else hPipeThread.CloseHandle();
194 // Create a thread which waits for incoming pipe connections
195 CAutoGeneralHandle hCommandWaitThread = CreateThread(
196 NULL, // no security attribute
197 0, // default stack size
198 CommandWaitThread,
199 (LPVOID) &bRun, // thread parameter
200 0, // not suspended
201 &dwThreadId); // returns thread ID
203 if (!hCommandWaitThread)
205 //OutputDebugStringA("TSVNCache: Could not create command wait thread\n");
206 //DebugOutputLastError();
207 return 0;
211 // loop to handle window messages.
212 BOOL bLoopRet;
213 while (bRun)
215 bLoopRet = GetMessage(&msg, NULL, 0, 0);
216 if ((bLoopRet != -1)&&(bLoopRet != 0))
218 DispatchMessage(&msg);
222 bRun = false;
224 Shell_NotifyIcon(NIM_DELETE,&niData);
225 CGitStatusCache::Destroy();
226 g_GitAdminDir.Close();
227 // apr_terminate();
229 return 0;
232 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
234 switch (message)
236 case TRAY_CALLBACK:
238 switch(lParam)
240 case WM_LBUTTONDBLCLK:
241 if (IsWindowVisible(hWnd))
242 ShowWindow(hWnd, SW_HIDE);
243 else
244 ShowWindow(hWnd, SW_RESTORE);
245 break;
246 case WM_MOUSEMOVE:
248 CString sInfoTip;
249 NOTIFYICONDATA SystemTray;
250 sInfoTip.Format(_T("Cached Directories : %ld\nWatched paths : %ld"),
251 CGitStatusCache::Instance().GetCacheSize(),
252 CGitStatusCache::Instance().GetNumberOfWatchedPaths());
254 SystemTray.cbSize = sizeof(NOTIFYICONDATA);
255 SystemTray.hWnd = hTrayWnd;
256 SystemTray.uID = TRAY_ID;
257 SystemTray.uFlags = NIF_TIP;
258 _tcscpy_s(SystemTray.szTip, sInfoTip);
259 Shell_NotifyIcon(NIM_MODIFY, &SystemTray);
261 break;
262 case WM_RBUTTONUP:
263 case WM_CONTEXTMENU:
265 POINT pt;
266 DWORD ptW = GetMessagePos();
267 pt.x = GET_X_LPARAM(ptW);
268 pt.y = GET_Y_LPARAM(ptW);
269 HMENU hMenu = CreatePopupMenu();
270 if(hMenu)
272 InsertMenu(hMenu, (UINT)-1, MF_BYPOSITION, TRAYPOP_EXIT, _T("Exit"));
273 SetForegroundWindow(hWnd);
274 TrackPopupMenu(hMenu, TPM_BOTTOMALIGN, pt.x, pt.y, 0, hWnd, NULL);
275 DestroyMenu(hMenu);
278 break;
281 break;
282 case WM_PAINT:
284 PAINTSTRUCT ps;
285 HDC hdc = BeginPaint(hWnd, &ps);
286 RECT rect;
287 GetClientRect(hWnd, &rect);
288 // clear the background
289 HBRUSH background = CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
290 HGDIOBJ oldbrush = SelectObject(hdc, background);
291 FillRect(hdc, &rect, background);
293 int line = 0;
294 SIZE fontsize = {0};
295 AutoLocker print(critSec);
296 GetTextExtentPoint32( hdc, szCurrentCrawledPath[0], (int)_tcslen(szCurrentCrawledPath[0]), &fontsize );
297 for (int i=nCurrentCrawledpathIndex; i<MAX_CRAWLEDPATHS; ++i)
299 TextOut(hdc, 0, line*fontsize.cy, szCurrentCrawledPath[i], (int)_tcslen(szCurrentCrawledPath[i]));
300 line++;
302 for (int i=0; i<nCurrentCrawledpathIndex; ++i)
304 TextOut(hdc, 0, line*fontsize.cy, szCurrentCrawledPath[i], (int)_tcslen(szCurrentCrawledPath[i]));
305 line++;
309 SelectObject(hdc,oldbrush);
310 EndPaint(hWnd, &ps);
311 DeleteObject(background);
312 return 0L;
314 break;
315 case WM_COMMAND:
317 WORD wmId = LOWORD(wParam);
319 switch (wmId)
321 case TRAYPOP_EXIT:
322 DestroyWindow(hWnd);
323 break;
325 return 1;
327 case WM_QUERYENDSESSION:
329 ATLTRACE("WM_QUERYENDSESSION\n");
330 if (CGitStatusCache::Instance().WaitToWrite(200))
332 CGitStatusCache::Instance().Stop();
333 CGitStatusCache::Instance().Done();
335 return TRUE;
337 break;
338 case WM_CLOSE:
339 case WM_ENDSESSION:
340 case WM_DESTROY:
341 case WM_QUIT:
343 ATLTRACE("WM_CLOSE/DESTROY/ENDSESSION/QUIT\n");
344 CGitStatusCache::Instance().WaitToWrite();
345 CGitStatusCache::Instance().Stop();
346 CGitStatusCache::Instance().SaveCache();
347 if (message != WM_QUIT)
348 PostQuitMessage(0);
349 bRun = false;
350 return 1;
352 break;
353 case WM_DEVICECHANGE:
355 DEV_BROADCAST_HDR * phdr = (DEV_BROADCAST_HDR*)lParam;
356 switch (wParam)
358 case DBT_CUSTOMEVENT:
360 ATLTRACE("WM_DEVICECHANGE with DBT_CUSTOMEVENT\n");
361 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
363 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
364 if (IsEqualGUID(phandle->dbch_eventguid, GUID_IO_VOLUME_DISMOUNT))
366 ATLTRACE("Device to be dismounted\n");
367 CGitStatusCache::Instance().WaitToWrite();
368 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
369 CGitStatusCache::Instance().Done();
371 if (IsEqualGUID(phandle->dbch_eventguid, GUID_IO_VOLUME_LOCK))
373 ATLTRACE("Device lock event\n");
374 CGitStatusCache::Instance().WaitToWrite();
375 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
376 CGitStatusCache::Instance().Done();
380 break;
381 case DBT_DEVICEREMOVEPENDING:
382 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVEPENDING\n");
383 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
385 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
386 CGitStatusCache::Instance().WaitToWrite();
387 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
388 CGitStatusCache::Instance().Done();
390 else
392 CGitStatusCache::Instance().WaitToWrite();
393 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
394 CGitStatusCache::Instance().Done();
396 break;
397 case DBT_DEVICEQUERYREMOVE:
398 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEQUERYREMOVE\n");
399 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
401 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
402 CGitStatusCache::Instance().WaitToWrite();
403 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
404 CGitStatusCache::Instance().Done();
406 else
408 CGitStatusCache::Instance().WaitToWrite();
409 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
410 CGitStatusCache::Instance().Done();
412 break;
413 case DBT_DEVICEREMOVECOMPLETE:
414 ATLTRACE("WM_DEVICECHANGE with DBT_DEVICEREMOVECOMPLETE\n");
415 if (phdr->dbch_devicetype == DBT_DEVTYP_HANDLE)
417 DEV_BROADCAST_HANDLE * phandle = (DEV_BROADCAST_HANDLE*)lParam;
418 CGitStatusCache::Instance().WaitToWrite();
419 CGitStatusCache::Instance().CloseWatcherHandles(phandle->dbch_hdevnotify);
420 CGitStatusCache::Instance().Done();
422 else
424 CGitStatusCache::Instance().WaitToWrite();
425 CGitStatusCache::Instance().CloseWatcherHandles(INVALID_HANDLE_VALUE);
426 CGitStatusCache::Instance().Done();
428 break;
431 break;
432 default:
433 break;
435 return DefWindowProc(hWnd, message, wParam, lParam);
438 //////////////////////////////////////////////////////////////////////////
440 VOID GetAnswerToRequest(const TGITCacheRequest* pRequest, TGITCacheResponse* pReply, DWORD* pResponseLength)
442 CTGitPath path;
443 *pResponseLength = 0;
444 if(pRequest->flags & TGITCACHE_FLAGS_FOLDERISKNOWN)
446 path.SetFromWin(pRequest->path, !!(pRequest->flags & TGITCACHE_FLAGS_ISFOLDER));
448 else
450 path.SetFromWin(pRequest->path);
453 if (CGitStatusCache::Instance().WaitToRead(2000))
455 CGitStatusCache::Instance().GetStatusForPath(path, pRequest->flags, false).BuildCacheResponse(*pReply, *pResponseLength);
456 CGitStatusCache::Instance().Done();
458 else
460 CStatusCacheEntry entry;
461 entry.BuildCacheResponse(*pReply, *pResponseLength);
465 DWORD WINAPI PipeThread(LPVOID lpvParam)
467 ATLTRACE("PipeThread started\n");
468 bool * bRun = (bool *)lpvParam;
469 // The main loop creates an instance of the named pipe and
470 // then waits for a client to connect to it. When the client
471 // connects, a thread is created to handle communications
472 // with that client, and the loop is repeated.
473 DWORD dwThreadId;
474 BOOL fConnected;
475 CAutoFile hPipe;
477 while (*bRun)
479 hPipe = CreateNamedPipe(
480 GetCachePipeName(),
481 PIPE_ACCESS_DUPLEX, // read/write access
482 PIPE_TYPE_MESSAGE | // message type pipe
483 PIPE_READMODE_MESSAGE | // message-read mode
484 PIPE_WAIT, // blocking mode
485 PIPE_UNLIMITED_INSTANCES, // max. instances
486 BUFSIZE, // output buffer size
487 BUFSIZE, // input buffer size
488 NMPWAIT_USE_DEFAULT_WAIT, // client time-out
489 NULL); // NULL DACL
491 if (!hPipe)
493 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
494 //DebugOutputLastError();
495 if (*bRun)
496 Sleep(200);
497 continue; // never leave the thread!
500 // Wait for the client to connect; if it succeeds,
501 // the function returns a nonzero value. If the function returns
502 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
503 fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
504 if (fConnected)
506 // Create a thread for this client.
507 CAutoGeneralHandle hInstanceThread = CreateThread(
508 NULL, // no security attribute
509 0, // default stack size
510 InstanceThread,
511 (HANDLE) hPipe, // thread parameter
512 0, // not suspended
513 &dwThreadId); // returns thread ID
515 if (!hInstanceThread)
517 //OutputDebugStringA("TSVNCache: Could not create Instance thread\n");
518 //DebugOutputLastError();
519 DisconnectNamedPipe(hPipe);
520 // since we're now closing this thread, we also have to close the whole application!
521 // otherwise the thread is dead, but the app is still running, refusing new instances
522 // but no pipe will be available anymore.
523 PostMessage(hWnd, WM_CLOSE, 0, 0);
524 return 1;
526 // detach the handle, since we passed it to the thread
527 hPipe.Detach();
529 else
531 // The client could not connect, so close the pipe.
532 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
533 //DebugOutputLastError();
534 hPipe.CloseHandle();
535 if (*bRun)
536 Sleep(200);
537 continue; // don't end the thread!
540 ATLTRACE("Pipe thread exited\n");
541 return 0;
544 DWORD WINAPI CommandWaitThread(LPVOID lpvParam)
546 ATLTRACE("CommandWaitThread started\n");
547 bool * bRun = (bool *)lpvParam;
548 // The main loop creates an instance of the named pipe and
549 // then waits for a client to connect to it. When the client
550 // connects, a thread is created to handle communications
551 // with that client, and the loop is repeated.
552 DWORD dwThreadId;
553 BOOL fConnected;
554 CAutoFile hPipe;
556 while (*bRun)
558 hPipe = CreateNamedPipe(
559 GetCacheCommandPipeName(),
560 PIPE_ACCESS_DUPLEX, // read/write access
561 PIPE_TYPE_MESSAGE | // message type pipe
562 PIPE_READMODE_MESSAGE | // message-read mode
563 PIPE_WAIT, // blocking mode
564 PIPE_UNLIMITED_INSTANCES, // max. instances
565 BUFSIZE, // output buffer size
566 BUFSIZE, // input buffer size
567 NMPWAIT_USE_DEFAULT_WAIT, // client time-out
568 NULL); // NULL DACL
570 if (!hPipe)
572 //OutputDebugStringA("TSVNCache: CreatePipe failed\n");
573 //DebugOutputLastError();
574 if (*bRun)
575 Sleep(200);
576 continue; // never leave the thread!
579 // Wait for the client to connect; if it succeeds,
580 // the function returns a nonzero value. If the function returns
581 // zero, GetLastError returns ERROR_PIPE_CONNECTED.
582 fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
583 if (fConnected)
585 // Create a thread for this client.
586 CAutoGeneralHandle hCommandThread = CreateThread(
587 NULL, // no security attribute
588 0, // default stack size
589 CommandThread,
590 (HANDLE) hPipe, // thread parameter
591 0, // not suspended
592 &dwThreadId); // returns thread ID
594 if (!hCommandThread)
596 //OutputDebugStringA("TSVNCache: Could not create Command thread\n");
597 //DebugOutputLastError();
598 DisconnectNamedPipe(hPipe);
599 hPipe.CloseHandle();
600 // since we're now closing this thread, we also have to close the whole application!
601 // otherwise the thread is dead, but the app is still running, refusing new instances
602 // but no pipe will be available anymore.
603 PostMessage(hWnd, WM_CLOSE, 0, 0);
604 return 1;
606 // detach the handle, since we passed it to the thread
607 hPipe.Detach();
609 else
611 // The client could not connect, so close the pipe.
612 //OutputDebugStringA("TSVNCache: ConnectNamedPipe failed\n");
613 //DebugOutputLastError();
614 hPipe.CloseHandle();
615 if (*bRun)
616 Sleep(200);
617 continue; // don't end the thread!
620 ATLTRACE("CommandWait thread exited\n");
621 return 0;
624 DWORD WINAPI InstanceThread(LPVOID lpvParam)
626 ATLTRACE("InstanceThread started\n");
627 TGITCacheResponse response;
628 DWORD cbBytesRead, cbWritten;
629 BOOL fSuccess;
630 CAutoFile hPipe;
632 // The thread's parameter is a handle to a pipe instance.
634 hPipe = lpvParam;
635 InterlockedIncrement(&nThreadCount);
636 while (bRun)
638 // Read client requests from the pipe.
639 TGITCacheRequest request;
640 fSuccess = ReadFile(
641 hPipe, // handle to pipe
642 &request, // buffer to receive data
643 sizeof(request), // size of buffer
644 &cbBytesRead, // number of bytes read
645 NULL); // not overlapped I/O
647 if (! fSuccess || cbBytesRead == 0)
649 DisconnectNamedPipe(hPipe);
650 ATLTRACE("Instance thread exited\n");
651 InterlockedDecrement(&nThreadCount);
652 if (nThreadCount == 0)
653 PostMessage(hWnd, WM_CLOSE, 0, 0);
654 return 1;
657 DWORD responseLength;
658 GetAnswerToRequest(&request, &response, &responseLength);
660 // Write the reply to the pipe.
661 fSuccess = WriteFile(
662 hPipe, // handle to pipe
663 &response, // buffer to write from
664 responseLength, // number of bytes to write
665 &cbWritten, // number of bytes written
666 NULL); // not overlapped I/O
668 if (! fSuccess || responseLength != cbWritten)
670 DisconnectNamedPipe(hPipe);
671 ATLTRACE("Instance thread exited\n");
672 InterlockedDecrement(&nThreadCount);
673 if (nThreadCount == 0)
674 PostMessage(hWnd, WM_CLOSE, 0, 0);
675 return 1;
679 // Flush the pipe to allow the client to read the pipe's contents
680 // before disconnecting. Then disconnect the pipe, and close the
681 // handle to this pipe instance.
683 FlushFileBuffers(hPipe);
684 DisconnectNamedPipe(hPipe);
685 ATLTRACE("Instance thread exited\n");
686 InterlockedDecrement(&nThreadCount);
687 if (nThreadCount == 0)
688 PostMessage(hWnd, WM_CLOSE, 0, 0);
689 return 0;
692 DWORD WINAPI CommandThread(LPVOID lpvParam)
694 ATLTRACE("CommandThread started\n");
695 DWORD cbBytesRead;
696 BOOL fSuccess;
697 CAutoFile hPipe;
699 // The thread's parameter is a handle to a pipe instance.
701 hPipe = lpvParam;
703 while (bRun)
705 // Read client requests from the pipe.
706 TGITCacheCommand command;
707 fSuccess = ReadFile(
708 hPipe, // handle to pipe
709 &command, // buffer to receive data
710 sizeof(command), // size of buffer
711 &cbBytesRead, // number of bytes read
712 NULL); // not overlapped I/O
714 if (! fSuccess || cbBytesRead == 0)
716 DisconnectNamedPipe(hPipe);
717 ATLTRACE("Command thread exited\n");
718 return 1;
721 switch (command.command)
723 case TGITCACHECOMMAND_END:
724 FlushFileBuffers(hPipe);
725 DisconnectNamedPipe(hPipe);
726 ATLTRACE("Command thread exited\n");
727 return 0;
728 case TGITCACHECOMMAND_CRAWL:
730 CTGitPath changedpath;
731 changedpath.SetFromWin(CString(command.path), true);
732 // remove the path from our cache - that will 'invalidate' it.
733 CGitStatusCache::Instance().WaitToWrite();
734 CGitStatusCache::Instance().RemoveCacheForPath(changedpath);
735 CGitStatusCache::Instance().Done();
736 CGitStatusCache::Instance().AddFolderForCrawling(changedpath.GetDirectory());
738 break;
739 case TGITCACHECOMMAND_REFRESHALL:
740 CGitStatusCache::Instance().WaitToWrite();
741 CGitStatusCache::Instance().Refresh();
742 CGitStatusCache::Instance().Done();
743 break;
744 case TGITCACHECOMMAND_RELEASE:
746 CTGitPath changedpath;
747 changedpath.SetFromWin(CString(command.path), true);
748 ATLTRACE(_T("release handle for path %s\n"), changedpath.GetWinPath());
749 CGitStatusCache::Instance().WaitToWrite();
750 CGitStatusCache::Instance().CloseWatcherHandles(changedpath);
751 CGitStatusCache::Instance().RemoveCacheForPath(changedpath);
752 CGitStatusCache::Instance().Done();
754 break;
755 case TGITCACHECOMMAND_BLOCK:
757 CTGitPath changedpath;
758 changedpath.SetFromWin(CString(command.path));
759 ATLTRACE(_T("block path %s\n"), changedpath.GetWinPath());
760 CGitStatusCache::Instance().BlockPath(changedpath);
762 break;
763 case TGITCACHECOMMAND_UNBLOCK:
765 CTGitPath changedpath;
766 changedpath.SetFromWin(CString(command.path));
767 ATLTRACE(_T("block path %s\n"), changedpath.GetWinPath());
768 CGitStatusCache::Instance().UnBlockPath(changedpath);
770 break;
774 // Flush the pipe to allow the client to read the pipe's contents
775 // before disconnecting. Then disconnect the pipe, and close the
776 // handle to this pipe instance.
778 FlushFileBuffers(hPipe);
779 DisconnectNamedPipe(hPipe);
780 ATLTRACE("Command thread exited\n");
781 return 0;