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 WINAPI HashCalcSetSavePrefix( PHASHCALCCONTEXT phcctx, PTSTR pszSave ) { // We have to be careful here about case sensitivity since we are now // working with a user-provided path instead of a system-provided path... // We want to build new paths without resorting to using "..", as that is // ugly, fragile (often more so than absolute paths), and not to mention, // complicated to calculate. This means that relative paths will be used // only for paths within the same line of ancestry. BOOL bMultiSel; PTSTR pszOrig; PTSTR pszTail; // First, grab one of the original paths to work with SLReset(phcctx->hListRaw); pszOrig = SLGetDataAndStep(phcctx->hListRaw); bMultiSel = SLCheck(phcctx->hListRaw); // Unfortunately, we also have to contend with the possibility that one of // these paths may be in short name format (e.g., if the user navigates to // %TEMP% on a NT 5.x system) { // The scratch buffer's sz members are large enough for us PTSTR pszOrigLong = (PTSTR)phcctx->scratch.szW; PTSTR pszSaveLong = (PTSTR)phcctx->scratch.szA; // Copy original path to scratch and terminate pszTail = SSChainNCpy(pszOrigLong, pszOrig, phcctx->cchPrefix); pszTail[0] = 0; // Copy output path to scratch and terminate pszTail = SSChainNCpy(pszSaveLong, pszSave, phcctx->ofn.nFileOffset); pszTail[0] = 0; // Normalize both paths to LFN GetLongPathName(pszOrigLong, pszOrigLong, MAX_PATH_BUFFER); GetLongPathName(pszSaveLong, pszSaveLong, MAX_PATH_BUFFER); // We will only handle the case where they are the same, to prevent our // re-prefixing from messing up the base behavior; it is not worth the // trouble to account for LFN for all cases--just let it fall through // to an absolute path. if (StrCmpNI(pszOrigLong, pszSaveLong, MAX_PATH_BUFFER) == 0) { phcctx->cchAdjusted = phcctx->cchPrefix; return; } } if (pszTail = StrRChr(pszSave, NULL, TEXT('\\'))) { phcctx->cchAdjusted = (UINT)(pszTail - pszSave) + 1; if (phcctx->cchAdjusted <= phcctx->cchPrefix) { if (StrCmpNI(pszOrig, pszSave, phcctx->cchAdjusted) == 0) { // If the ouput prefix is the same as or a parent of the input // prefix... if (!(IsDoubleSlashPath(pszSave) && phcctx->cchAdjusted < 3)) return; } } else if (!bMultiSel) { // We will make an exception for the case where the user selects // a single directory from the Shell and then saves the output in // that directory... BOOL bEqual; *pszTail = 0; bEqual = StrCmpNI(pszOrig, pszSave, phcctx->cchAdjusted) == 0; *pszTail = TEXT('\\'); if (bEqual) return; } } // If we have reached this point, we need to use an absolute path if ( pszSave[1] == TEXT(':') && phcctx->cchPrefix > 2 && StrCmpNI(pszOrig, pszSave, 2) == 0 ) { // Omit drive letter phcctx->cchAdjusted = 2; } else { // Full absolute path phcctx->cchAdjusted = 0; } }
VOID WINAPI HashCalcInitSave( PHASHCALCCONTEXT phcctx ) { HWND hWnd = phcctx->hWnd; // We can use the extended portion of the scratch buffer for the file name PTSTR pszFile = (PTSTR)phcctx->scratch.ext; // Default result value phcctx->hFileOut = INVALID_HANDLE_VALUE; // Load settings phcctx->opt.dwFlags = HCOF_FILTERINDEX | HCOF_SAVEENCODING; OptionsLoad(&phcctx->opt); // Initialize the struct for the first time, if needed if (phcctx->ofn.lStructSize == 0) { phcctx->ofn.lStructSize = sizeof(phcctx->ofn); phcctx->ofn.hwndOwner = hWnd; phcctx->ofn.lpstrFilter = SAVE_FILTERS; phcctx->ofn.nFilterIndex = phcctx->opt.dwFilterIndex; phcctx->ofn.lpstrFile = pszFile; phcctx->ofn.nMaxFile = MAX_PATH_BUFFER + 10; phcctx->ofn.Flags = OFN_DONTADDTORECENT | OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST; phcctx->ofn.lpstrDefExt = TEXT(""); // Set the initial file name { PTSTR pszOrigPath; SLReset(phcctx->hListRaw); pszOrigPath = SLGetDataAndStep(phcctx->hListRaw); if (SLCheck(phcctx->hListRaw)) { // Multiple items were selected in Explorer SSChainNCpy2( pszFile, pszOrigPath, phcctx->cchPrefix, SAVE_DEFAULT_NAME, countof(SAVE_DEFAULT_NAME) ); } else { // Only one item was selected in Explorer (may be a single // file or a directory containing multiple files) SSChainCpyCat(pszFile, pszOrigPath, SAVE_EXTS[phcctx->ofn.nFilterIndex]); } } } // We should also do a sanity check to make sure that the filter index // is set to a valid value since we depend on that to determine the format if ( GetSaveFileName(&phcctx->ofn) && phcctx->ofn.nFilterIndex && phcctx->ofn.nFilterIndex <= 4 ) { BOOL bSuccess = FALSE; // Save the filter in the user's preferences if (phcctx->opt.dwFilterIndex != phcctx->ofn.nFilterIndex) { phcctx->opt.dwFilterIndex = phcctx->ofn.nFilterIndex; phcctx->opt.dwFlags = HCOF_FILTERINDEX; OptionsSave(&phcctx->opt); } // Extension fixup: Correct the extension to match the selected // type, but only if the extension was one of the 4 in the list if (phcctx->ofn.nFileExtension) { PTSTR pszExt = pszFile + phcctx->ofn.nFileExtension - 1; if ( StrCmpI(pszExt, TEXT(".sfv")) == 0 || StrCmpI(pszExt, TEXT(".md4")) == 0 || StrCmpI(pszExt, TEXT(".md5")) == 0 || StrCmpI(pszExt, TEXT(".sha1")) == 0 ) { if (StrCmpI(pszExt, SAVE_EXTS[phcctx->ofn.nFilterIndex])) SSCpy(pszExt, SAVE_EXTS[phcctx->ofn.nFilterIndex]); } } // Adjust the file paths for the output path, if necessary HashCalcSetSavePrefix(phcctx, pszFile); // Open the file for output phcctx->hFileOut = CreateFile( pszFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL ); if (phcctx->hFileOut != INVALID_HANDLE_VALUE) { // The actual format will be set when HashCalcWriteResult is called phcctx->szFormat[0] = 0; if (phcctx->opt.dwSaveEncoding == 1) { // Write the BOM for UTF-16LE WCHAR BOM = 0xFEFF; DWORD cbWritten; WriteFile(phcctx->hFileOut, &BOM, sizeof(WCHAR), &cbWritten, NULL); } } else { TCHAR szMessage[MAX_STRINGMSG]; LoadString(g_hModThisDll, IDS_HC_SAVE_ERROR, szMessage, countof(szMessage)); MessageBox(hWnd, szMessage, NULL, MB_OK | MB_ICONERROR); } } }
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); }