VOID __fastcall HashSaveWorkerMain( PHASHSAVECONTEXT phsctx ) { // Note that ALL message communication to and from the main window MUST // be asynchronous, or else there may be a deadlock. PHASHSAVEITEM pItem; // Prep: expand directories, max path, etc. (prefix was set by earlier call) PostMessage(phsctx->hWnd, HM_WORKERTHREAD_TOGGLEPREP, (WPARAM)phsctx, TRUE); HashCalcPrepare(phsctx); PostMessage(phsctx->hWnd, HM_WORKERTHREAD_TOGGLEPREP, (WPARAM)phsctx, FALSE); // Indicate which hash type we are after, see WHEX... values in WinHash.h phsctx->whctx.flags = 1 << (phsctx->ofn.nFilterIndex - 1); while (pItem = SLGetDataAndStep(phsctx->hList)) { // Get the hash WorkerThreadHashFile( (PCOMMONCONTEXT)phsctx, pItem->szPath, &pItem->bValid, &phsctx->whctx, &pItem->results, NULL ); if (phsctx->status == CANCEL_REQUESTED) return; // Write the data HashCalcWriteResult(phsctx, pItem); // Update the UI ++phsctx->cSentMsgs; PostMessage(phsctx->hWnd, HM_WORKERTHREAD_UPDATE, (WPARAM)phsctx, (LPARAM)pItem); } }
VOID __fastcall HashVerifyWorkerMain( PHASHVERIFYCONTEXT phvctx ) { // Note that ALL message communication to and from the main window MUST // be asynchronous, or else there may be a deadlock PHASHVERIFYITEM pItem; // We need to keep track of the thread's execution time so that we can do a // sound notification of completion when appropriate DWORD dwTickStart = GetTickCount(); // Initialize the path prefix length; used for building the full path PTSTR pszPathTail = StrRChr(phvctx->pszPath, NULL, TEXT('\\')); SIZE_T cchPathPrefix = (pszPathTail) ? pszPathTail + 1 - phvctx->pszPath : 0; while (pItem = SLGetDataAndStep(phvctx->hList)) { BOOL bSuccess; // Part 1: Build the path { SIZE_T cchPrefix = cchPathPrefix; // Do not use the prefix if pszDisplayName is an absolute path if ( pItem->pszDisplayName[0] == TEXT('\\') || pItem->pszDisplayName[1] == TEXT(':') ) { cchPrefix = 0; } SSChainNCpy2( phvctx->ex.pszPath, phvctx->pszPath, cchPrefix, pItem->pszDisplayName, pItem->cchDisplayName ); } // Part 2: Calculate the checksum WorkerThreadHashFile( (PCOMMONCONTEXT)phvctx, phvctx->ex.pszPath, &bSuccess, &phvctx->whctx, NULL, &pItem->filesize ); if (phvctx->status == CANCEL_REQUESTED) return; // Part 3: Do something with the results if (bSuccess) { if (phvctx->whctx.flags == WHEX_ALL128) { // If the MD4/MD5 STILL has not been settled by this point, then // settle it by a simple heuristic: if the checksum matches MD4, // go with that, otherwise default to MD5. if (StrCmpI(pItem->pszExpected, phvctx->whctx.results.szHexMD4) == 0) phvctx->whctx.flags = WHEX_CHECKMD4; else phvctx->whctx.flags = WHEX_CHECKMD5; } switch (phvctx->whctx.flags) { case WHEX_CHECKCRC32: SSStaticCpy(pItem->szActual, phvctx->whctx.results.szHexCRC32); break; case WHEX_CHECKMD4: SSStaticCpy(pItem->szActual, phvctx->whctx.results.szHexMD4); break; case WHEX_CHECKMD5: SSStaticCpy(pItem->szActual, phvctx->whctx.results.szHexMD5); break; case WHEX_CHECKSHA1: SSStaticCpy(pItem->szActual, phvctx->whctx.results.szHexSHA1); break; } if (StrCmpI(pItem->pszExpected, pItem->szActual) == 0) pItem->uStatusID = HV_STATUS_MATCH; else pItem->uStatusID = HV_STATUS_MISMATCH; } else { pItem->uStatusID = HV_STATUS_UNREADABLE; } // Part 4: Update the UI ++phvctx->cSentMsgs; PostMessage(phvctx->hWnd, HM_WORKERTHREAD_UPDATE, (WPARAM)phvctx, (LPARAM)pItem); } // Play a sound to signal the normal, successful termination of operations, // but exempt operations that were nearly instantaneous if (phvctx->cTotal && GetTickCount() - dwTickStart >= 2000) MessageBeep(MB_ICONASTERISK); }
VOID __fastcall HashSaveWorkerMain( PHASHSAVECONTEXT phsctx ) { // Note that ALL message communication to and from the main window MUST // be asynchronous, or else there may be a deadlock. // Prep: expand directories, max path, etc. (prefix was set by earlier call) PostMessage(phsctx->hWnd, HM_WORKERTHREAD_TOGGLEPREP, (WPARAM)phsctx, TRUE); if (! HashCalcPrepare(phsctx)) return; HashCalcSetSaveFormat(phsctx); PostMessage(phsctx->hWnd, HM_WORKERTHREAD_TOGGLEPREP, (WPARAM)phsctx, FALSE); // Extract the slist into a vector for parallel_for_each std::vector<PHASHSAVEITEM> vecpItems; vecpItems.resize(phsctx->cTotal + 1); SLBuildIndex(phsctx->hList, (PVOID*)vecpItems.data()); assert(vecpItems.back() == nullptr); vecpItems.pop_back(); assert(vecpItems.back() != nullptr); #ifdef USE_PPL const bool bMultithreaded = vecpItems.size() > 1 && IsSSD(vecpItems[0]->szPath); concurrency::concurrent_vector<void*> vecBuffers; // a vector of all allocated read buffers (one per thread) DWORD dwBufferTlsIndex = TlsAlloc(); // TLS index of the current thread's read buffer if (dwBufferTlsIndex == TLS_OUT_OF_INDEXES) return; #else constexpr bool bMultithreaded = false; #endif PBYTE pbTheBuffer; // file read buffer, used iff not multithreaded if (! bMultithreaded) { pbTheBuffer = (PBYTE)VirtualAlloc(NULL, READ_BUFFER_SIZE, MEM_COMMIT, PAGE_READWRITE); if (pbTheBuffer == NULL) return; } // Initialize the progress bar update synchronization vars CRITICAL_SECTION updateCritSec; volatile ULONGLONG cbCurrentMaxSize = 0; if (bMultithreaded) InitializeCriticalSection(&updateCritSec); #ifdef _TIMED DWORD dwStarted; dwStarted = GetTickCount(); #endif class CanceledException {}; // concurrency::parallel_for_each(vecpItems.cbegin(), vecpItems.cend(), ... auto per_file_worker = [&](PHASHSAVEITEM pItem) { WHCTXEX whctx; // Indicate which hash type we are after, see WHEX... values in WinHash.h whctx.dwFlags = 1 << (phsctx->ofn.nFilterIndex - 1); PBYTE pbBuffer; #ifdef USE_PPL if (bMultithreaded) { // Allocate or retrieve the already-allocated read buffer for the current thread pbBuffer = (PBYTE)TlsGetValue(dwBufferTlsIndex); if (pbBuffer == NULL) { pbBuffer = (PBYTE)VirtualAlloc(NULL, READ_BUFFER_SIZE, MEM_COMMIT, PAGE_READWRITE); if (pbBuffer == NULL) throw CanceledException(); // Cache the read buffer for the current thread vecBuffers.push_back(pbBuffer); TlsSetValue(dwBufferTlsIndex, pbBuffer); } } #endif #pragma warning(push) #pragma warning(disable: 4700 4703) // potentially uninitialized local pointer variable 'pbBuffer' used // Get the hash WorkerThreadHashFile( (PCOMMONCONTEXT)phsctx, pItem->szPath, &whctx, &pItem->results, bMultithreaded ? pbBuffer : pbTheBuffer, NULL, 0, bMultithreaded ? &updateCritSec : NULL, &cbCurrentMaxSize #ifdef _TIMED , &pItem->dwElapsed #endif ); if (phsctx->status == PAUSED) WaitForSingleObject(phsctx->hUnpauseEvent, INFINITE); if (phsctx->status == CANCEL_REQUESTED) throw CanceledException(); // Write the data HashCalcWriteResult(phsctx, pItem); // Update the UI InterlockedIncrement(&phsctx->cSentMsgs); PostMessage(phsctx->hWnd, HM_WORKERTHREAD_UPDATE, (WPARAM)phsctx, (LPARAM)pItem); }; #pragma warning(pop) try { #ifdef USE_PPL if (bMultithreaded) concurrency::parallel_for_each(vecpItems.cbegin(), vecpItems.cend(), per_file_worker); else #endif std::for_each(vecpItems.cbegin(), vecpItems.cend(), per_file_worker); } catch (CanceledException) {} // ignore cancellation requests #ifdef _TIMED if (phsctx->cTotal > 1 && phsctx->status != CANCEL_REQUESTED) { union { CHAR szA[MAX_STRINGMSG]; WCHAR szW[MAX_STRINGMSG]; } buffer; size_t cbBufferLeft; if (phsctx->opt.dwSaveEncoding == 1) // UTF-16 { StringCbPrintfExW(buffer.szW, sizeof(buffer), NULL, &cbBufferLeft, 0, L"; Total elapsed: %d ms\r\n", GetTickCount() - dwStarted); } else // UTF-8 or ANSI { StringCbPrintfExA(buffer.szA, sizeof(buffer), NULL, &cbBufferLeft, 0, "; Total elapsed: %d ms\r\n", GetTickCount() - dwStarted); } DWORD dwUnused; WriteFile(phsctx->hFileOut, buffer.szA, (DWORD) (sizeof(buffer) - cbBufferLeft), &dwUnused, NULL); } #endif #ifdef USE_PPL if (bMultithreaded) { for (void* pBuffer : vecBuffers) VirtualFree(pBuffer, 0, MEM_RELEASE); DeleteCriticalSection(&updateCritSec); } else #endif VirtualFree(pbTheBuffer, 0, MEM_RELEASE); }