STDMETHODIMP CHashCheck::Initialize( LPCITEMIDLIST pidlFolder, LPDATAOBJECT pdtobj, HKEY hkeyProgID ) { // We'll be needing a buffer, and let's double it just to be safe TCHAR szPath[MAX_PATH << 1]; // Make sure that we are working with a fresh list SLRelease(m_hList); m_hList = SLCreate(); // This indent exists to facilitate diffing against the CmdOpen source { FORMATETC format = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM medium; if (!pdtobj || pdtobj->GetData(&format, &medium) != S_OK) return(E_INVALIDARG); if (HDROP hDrop = (HDROP)GlobalLock(medium.hGlobal)) { UINT uDrops = DragQueryFile(hDrop, -1, NULL, 0); for (UINT uDrop = 0; uDrop < uDrops; ++uDrop) { if (DragQueryFile(hDrop, uDrop, szPath, countof(szPath))) { SLAddStringI(m_hList, szPath); } } GlobalUnlock(medium.hGlobal); } ReleaseStgMedium(&medium); } // If there was any failure, the list would be empty... return((SLCheck(m_hList)) ? S_OK : E_INVALIDARG); }
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); } } }