bool CRemoteCacheLink::GetStatusFromRemoteCache(const CTGitPath& Path, TGITCacheResponse* pReturnedStatus, bool bRecursive) { if(!EnsurePipeOpen()) { // We've failed to open the pipe - try and start the cache // but only if the last try to start the cache was a certain time // ago. If we just try over and over again without a small pause // in between, the explorer is rendered unusable! // Failing to start the cache can have different reasons: missing exe, // missing registry key, corrupt exe, ... if (((long)GetTickCount() - m_lastTimeout) < 0) return false; // if we're in protected mode, don't try to start the cache: since we're // here, we know we can't access it anyway and starting a new process will // trigger a warning dialog in IE7+ on Vista - we don't want that. if (GetProcessIntegrityLevel() < SECURITY_MANDATORY_MEDIUM_RID) return false; if (!RunTGitCacheProcess()) return false; // Wait for the cache to open long endTime = (long)GetTickCount()+1000; while(!EnsurePipeOpen()) { if(((long)GetTickCount() - endTime) > 0) { m_lastTimeout = (long)GetTickCount()+10000; return false; } } m_lastTimeout = (long)GetTickCount()+10000; } AutoLocker lock(m_critSec); DWORD nBytesRead; TGITCacheRequest request; request.flags = TGITCACHE_FLAGS_NONOTIFICATIONS; if(bRecursive) { request.flags |= TGITCACHE_FLAGS_RECUSIVE_STATUS; } wcsncpy_s(request.path, Path.GetWinPath(), _countof(request.path) - 1); SecureZeroMemory(&m_Overlapped, sizeof(OVERLAPPED)); m_Overlapped.hEvent = m_hEvent; // Do the transaction in overlapped mode. // That way, if anything happens which might block this call // we still can get out of it. We NEVER MUST BLOCK THE SHELL! // A blocked shell is a very bad user impression, because users // who don't know why it's blocked might find the only solution // to such a problem is a reboot and therefore they might loose // valuable data. // One particular situation where the shell could hang is when // the cache crashes and our crash report dialog comes up. // Sure, it would be better to have no situations where the shell // even can get blocked, but the timeout of 10 seconds is long enough // so that users still recognize that something might be wrong and // report back to us so we can investigate further. BOOL fSuccess = TransactNamedPipe(m_hPipe, &request, sizeof(request), pReturnedStatus, sizeof(*pReturnedStatus), &nBytesRead, &m_Overlapped); if (!fSuccess) { if (GetLastError()!=ERROR_IO_PENDING) { //OutputDebugStringA("TortoiseShell: TransactNamedPipe failed\n"); ClosePipe(); return false; } // TransactNamedPipe is working in an overlapped operation. // Wait for it to finish DWORD dwWait = WaitForSingleObject(m_hEvent, 10000); if (dwWait == WAIT_OBJECT_0) { fSuccess = GetOverlappedResult(m_hPipe, &m_Overlapped, &nBytesRead, FALSE); } else { // the cache didn't respond! fSuccess = FALSE; } } if (fSuccess) { return true; } ClosePipe(); return false; }
bool CCacheDlg::GetStatusFromRemoteCache(const CTGitPath& Path, bool bRecursive) { if(!EnsurePipeOpen()) { STARTUPINFO startup = { 0 }; PROCESS_INFORMATION process = { 0 }; startup.cb = sizeof(startup); CString sCachePath = L"TGitCache.exe"; if (CreateProcess(sCachePath.GetBuffer(sCachePath.GetLength() + 1), L"", nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startup, &process) == 0) { // It's not appropriate to do a message box here, because there may be hundreds of calls sCachePath.ReleaseBuffer(); ATLTRACE("Failed to start cache\n"); return false; } sCachePath.ReleaseBuffer(); // Wait for the cache to open ULONGLONG endTime = GetTickCount64()+1000; while(!EnsurePipeOpen()) { if((GetTickCount64() - endTime) > 0) { return false; } } } DWORD nBytesRead; TGITCacheRequest request; request.flags = TGITCACHE_FLAGS_NONOTIFICATIONS; if(bRecursive) { request.flags |= TGITCACHE_FLAGS_RECUSIVE_STATUS; } wcsncpy_s(request.path, Path.GetWinPath(), MAX_PATH); SecureZeroMemory(&m_Overlapped, sizeof(OVERLAPPED)); m_Overlapped.hEvent = m_hEvent; // Do the transaction in overlapped mode. // That way, if anything happens which might block this call // we still can get out of it. We NEVER MUST BLOCK THE SHELL! // A blocked shell is a very bad user impression, because users // who don't know why it's blocked might find the only solution // to such a problem is a reboot and therefore they might loose // valuable data. // Sure, it would be better to have no situations where the shell // even can get blocked, but the timeout of 5 seconds is long enough // so that users still recognize that something might be wrong and // report back to us so we can investigate further. TGITCacheResponse ReturnedStatus; BOOL fSuccess = TransactNamedPipe(m_hPipe, &request, sizeof(request), &ReturnedStatus, sizeof(ReturnedStatus), &nBytesRead, &m_Overlapped); if (!fSuccess) { if (GetLastError()!=ERROR_IO_PENDING) { ClosePipe(); return false; } // TransactNamedPipe is working in an overlapped operation. // Wait for it to finish DWORD dwWait = WaitForSingleObject(m_hEvent, INFINITE); if (dwWait == WAIT_OBJECT_0) { fSuccess = GetOverlappedResult(m_hPipe, &m_Overlapped, &nBytesRead, FALSE); return TRUE; } else fSuccess = FALSE; } ClosePipe(); return false; }
bool CRemoteCacheLink::GetStatusFromRemoteCache(const CTGitPath& Path, TGITCacheResponse* pReturnedStatus, bool bRecursive) { if(!EnsurePipeOpen()) { // We've failed to open the pipe - try and start the cache // but only if the last try to start the cache was a certain time // ago. If we just try over and over again without a small pause // in between, the explorer is rendered unusable! // Failing to start the cache can have different reasons: missing exe, // missing registry key, corrupt exe, ... if (((long)GetTickCount() - m_lastTimeout) < 0) return false; STARTUPINFO startup; PROCESS_INFORMATION process; memset(&startup, 0, sizeof(startup)); startup.cb = sizeof(startup); memset(&process, 0, sizeof(process)); CString sCachePath = CPathUtils::GetAppDirectory(g_hmodThisDll) + _T("TGitCache.exe"); #ifndef _WIN64 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); LPFN_ISWOW64PROCESS fnIsWow64Process; fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")),"IsWow64Process"); if (NULL != fnIsWow64Process) { BOOL bIsWow64 = false; if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64)) { bIsWow64 = false; } if (bIsWow64) { CRegString tgitinstalled64 = CRegString(_T("Software\\TortoiseGit\\CachePath"), _T(""), false, HKEY_LOCAL_MACHINE, KEY_WOW64_64KEY); if (!CString(tgitinstalled64).IsEmpty()) sCachePath = tgitinstalled64; } } if (!CCreateProcessHelper::CreateProcessDetached(sCachePath, NULL)) { ATLTRACE("Failed to start x64 cache\n"); CString sCachePath = CPathUtils::GetAppDirectory(g_hmodThisDll) + _T("TGitCache.exe"); if (!CCreateProcessHelper::CreateProcessDetached(sCachePath, NULL)) { // It's not appropriate to do a message box here, because there may be hundreds of calls ATLTRACE("Failed to start cache\n"); return false; } } #else if (!CCreateProcessHelper::CreateProcessDetached(sCachePath, NULL)) { // It's not appropriate to do a message box here, because there may be hundreds of calls ATLTRACE("Failed to start cache\n"); return false; } #endif CloseHandle(process.hThread); CloseHandle(process.hProcess); sCachePath.ReleaseBuffer(); // Wait for the cache to open long endTime = (long)GetTickCount()+1000; while(!EnsurePipeOpen()) { if(((long)GetTickCount() - endTime) > 0) { m_lastTimeout = (long)GetTickCount()+10000; return false; } } } AutoLocker lock(m_critSec); DWORD nBytesRead; TGITCacheRequest request; request.flags = TGITCACHE_FLAGS_NONOTIFICATIONS; if(bRecursive) { request.flags |= TGITCACHE_FLAGS_RECUSIVE_STATUS; } wcsncpy_s(request.path, MAX_PATH+1, Path.GetWinPath(), MAX_PATH); SecureZeroMemory(&m_Overlapped, sizeof(OVERLAPPED)); m_Overlapped.hEvent = m_hEvent; // Do the transaction in overlapped mode. // That way, if anything happens which might block this call // we still can get out of it. We NEVER MUST BLOCK THE SHELL! // A blocked shell is a very bad user impression, because users // who don't know why it's blocked might find the only solution // to such a problem is a reboot and therefore they might lose // valuable data. // One particular situation where the shell could hang is when // the cache crashes and our crash report dialog comes up. // Sure, it would be better to have no situations where the shell // even can get blocked, but the timeout of 10 seconds is long enough // so that users still recognize that something might be wrong and // report back to us so we can investigate further. BOOL fSuccess = TransactNamedPipe(m_hPipe, &request, sizeof(request), pReturnedStatus, sizeof(*pReturnedStatus), &nBytesRead, &m_Overlapped); if (!fSuccess) { if (GetLastError()!=ERROR_IO_PENDING) { //OutputDebugStringA("TortoiseShell: TransactNamedPipe failed\n"); ClosePipe(); return false; } // TransactNamedPipe is working in an overlapped operation. // Wait for it to finish DWORD dwWait = WaitForSingleObject(m_hEvent, 10000); if (dwWait == WAIT_OBJECT_0) { fSuccess = GetOverlappedResult(m_hPipe, &m_Overlapped, &nBytesRead, FALSE); } else { // the cache didn't respond! fSuccess = FALSE; } } if (fSuccess) { return true; } ClosePipe(); return false; }