Skip to content

Commit c8c09a8

Browse files
committed
add several safety/consistency and UI improvements
HashCheck will never save a partially complete checksum file: * canceling an in-progress save causes the checksum file to be deleted * the Save button on file properties is unavailable if Stop is pressed File read errors are now more visible: * in a checksum file, they are included as all 0's so that verify fails * on file properties they are included as all X's to stand out When selected hashes in Options change, the tab is updated immediately: * any hashes which have already been calculated are not recalculated Saving checksum files with an unselected hash from file properties works
1 parent 6e8307d commit c8c09a8

15 files changed

+424
-185
lines changed

HashCalc.c

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ __forceinline VOID WINAPI HashCalcSetSavePrefix( PHASHCALCCONTEXT phcctx, PTSTR
4444
Path processing
4545
\*============================================================================*/
4646

47-
VOID WINAPI HashCalcPrepare( PHASHCALCCONTEXT phcctx )
47+
BOOL WINAPI HashCalcPrepare( PHASHCALCCONTEXT phcctx )
4848
{
4949
PTSTR pszPrev = NULL;
5050
PTSTR pszCurrent, pszCurrentEnd;
@@ -128,7 +128,7 @@ VOID WINAPI HashCalcPrepare( PHASHCALCCONTEXT phcctx )
128128

129129
if (pItem)
130130
{
131-
pItem->bValid = FALSE;
131+
pItem->results.dwFlags = 0;
132132
pItem->cchPath = cchCurrent;
133133
memcpy(pItem->szPath, pszCurrent, cbCurrent);
134134

@@ -143,10 +143,11 @@ VOID WINAPI HashCalcPrepare( PHASHCALCCONTEXT phcctx )
143143
if (phcctx->status == PAUSED)
144144
WaitForSingleObject(phcctx->hUnpauseEvent, INFINITE);
145145
if (phcctx->status == CANCEL_REQUESTED)
146-
return;
146+
return(FALSE);
147147

148148
pszPrev = pszCurrent;
149149
}
150+
return(TRUE);
150151
}
151152

152153
VOID WINAPI HashCalcWalkDirectory( PHASHCALCCONTEXT phcctx, PTSTR pszPath, UINT cchPath )
@@ -192,7 +193,7 @@ VOID WINAPI HashCalcWalkDirectory( PHASHCALCCONTEXT phcctx, PTSTR pszPath, UINT
192193

193194
if (pItem)
194195
{
195-
pItem->bValid = FALSE;
196+
pItem->results.dwFlags = 0;
196197
pItem->cchPath = cchNew;
197198
memcpy(pItem->szPath, pszPath, cbPathBuffer);
198199

@@ -300,8 +301,6 @@ VOID WINAPI HashCalcInitSave( PHASHCALCCONTEXT phcctx )
300301
phcctx->ofn.nFilterIndex &&
301302
phcctx->ofn.nFilterIndex <= NUM_HASHES)
302303
{
303-
BOOL bSuccess = FALSE;
304-
305304
// Save the filter in the user's preferences
306305
if (phcctx->opt.dwFilterIndex != phcctx->ofn.nFilterIndex)
307306
{
@@ -330,7 +329,7 @@ VOID WINAPI HashCalcInitSave( PHASHCALCCONTEXT phcctx )
330329
// Open the file for output
331330
phcctx->hFileOut = CreateFile(
332331
pszFile,
333-
FILE_APPEND_DATA,
332+
FILE_APPEND_DATA | DELETE,
334333
FILE_SHARE_READ,
335334
NULL,
336335
CREATE_ALWAYS,
@@ -385,19 +384,32 @@ VOID WINAPI HashCalcSetSaveFormat( PHASHCALCCONTEXT phcctx )
385384

386385
BOOL WINAPI HashCalcWriteResult( PHASHCALCCONTEXT phcctx, PHASHCALCITEM pItem )
387386
{
388-
PCTSTR pszHash;
389-
WCHAR szWbuffer[MAX_PATH_BUFFER];
390-
CHAR szAbuffer[MAX_PATH_BUFFER];
387+
PCTSTR pszHash; // will be pointed to the hash name
388+
WCHAR szWbuffer[MAX_PATH_BUFFER]; // wide-char buffer
389+
CHAR szAbuffer[MAX_PATH_BUFFER]; // narrow-char buffer
391390
#ifdef UNICODE
392391
# define szTbuffer szWbuffer
393392
#else
394393
# define szTbuffer szAbuffer
395394
#endif
396-
PVOID pvLine; // Will be pointed to the buffer to write out
397-
size_t cchLine, cbLine; // Length of line, in TCHARs or BYTEs, EXCLUDING the terminator
398-
399-
if (!pItem->bValid)
400-
return(FALSE);
395+
PTSTR szTbufferAppend = szTbuffer; // current end of the buffer used to build output
396+
size_t cchLine = MAX_PATH_BUFFER; // starts off as count of remaining TCHARS in the buffer
397+
PVOID pvLine; // will be pointed to the buffer to write out
398+
size_t cbLine; // will be line length in bytes, EXCLUDING nul terminator
399+
BOOL bRetval = TRUE;
400+
401+
// If the checksum to save isn't present in the results
402+
if (! ((1 << (phcctx->ofn.nFilterIndex - 1)) & pItem->results.dwFlags))
403+
{
404+
// Start with a commented-out error message - "; UNREADABLE:"
405+
WCHAR szUnreadable[MAX_STRINGRES];
406+
LoadString(g_hModThisDll, IDS_HV_STATUS_UNREADABLE, szUnreadable, MAX_STRINGRES);
407+
StringCchPrintfEx(szTbufferAppend, cchLine, &szTbufferAppend, &cchLine, 0, TEXT("; %s:\r\n"), szUnreadable);
408+
409+
// We'll still output a hash, but it will be all 0's, that way Verify will indicate an mismatch
410+
HashCalcClearInvalid(&pItem->results, TEXT('0'));
411+
bRetval = FALSE;
412+
}
401413

402414
// Translate the filter index to a hash
403415
switch (phcctx->ofn.nFilterIndex)
@@ -409,19 +421,18 @@ BOOL WINAPI HashCalcWriteResult( PHASHCALCCONTEXT phcctx, PHASHCALCITEM pItem )
409421
}
410422

411423
// Format the line
412-
#define HashCalcFormat(a, b) StringCchPrintfEx(szTbuffer, MAX_PATH_BUFFER, NULL, &cchLine, 0, phcctx->szFormat, a, b)
424+
#define HashCalcFormat(a, b) StringCchPrintfEx(szTbufferAppend, cchLine, &szTbufferAppend, &cchLine, 0, phcctx->szFormat, a, b)
413425
(phcctx->ofn.nFilterIndex == 1) ?
414426
HashCalcFormat(pItem->szPath + phcctx->cchAdjusted, pszHash) : // SFV
415427
HashCalcFormat(pszHash, pItem->szPath + phcctx->cchAdjusted); // everything else
416428
#undef HashCalcFormat
417-
// cchLine is temporarily the count of characters left in the buffer instead of the line length
418429

419430
#ifdef _TIMED
420-
StringCchPrintfEx(szTbuffer + (MAX_PATH_BUFFER-cchLine), cchLine, NULL, &cchLine, 0,
431+
StringCchPrintfEx(szTbufferAppend, cchLine, NULL, &cchLine, 0,
421432
_T("; Elapsed: %d ms\r\n"), pItem->dwElapsed);
422433
#endif
423434

424-
cchLine = MAX_PATH_BUFFER - cchLine; // now it's back to being the line length
435+
cchLine = MAX_PATH_BUFFER - cchLine; // from now on cchLine is the line length in bytes, EXCLUDING nul terminator
425436
if (cchLine > 0)
426437
{
427438
// Convert to the correct encoding
@@ -479,7 +490,45 @@ BOOL WINAPI HashCalcWriteResult( PHASHCALCCONTEXT phcctx, PHASHCALCITEM pItem )
479490
}
480491
else return(FALSE);
481492

482-
return(TRUE);
493+
return(bRetval);
494+
}
495+
496+
VOID WINAPI HashCalcClearInvalid( PWHRESULTEX pwhres, WCHAR cInvalid )
497+
{
498+
#ifdef UNICODE
499+
# define _tmemset wmemset
500+
#else
501+
# define _tmemset memset
502+
#endif
503+
504+
#define HASH_CLEAR_INVALID_op(alg) \
505+
if (! (pwhres->dwFlags & WHEX_CHECK##alg)) \
506+
{ \
507+
_tmemset(pwhres->szHex##alg, cInvalid, countof(pwhres->szHex##alg) - 1); \
508+
pwhres->szHex##alg[countof(pwhres->szHex##alg) - 1] = L'\0'; \
509+
}
510+
FOR_EACH_HASH(HASH_CLEAR_INVALID_op)
511+
}
512+
513+
// This can only succeed on Windows Vista and later;
514+
// returns FALSE on failure
515+
BOOL WINAPI HashCalcDeleteFileByHandle(HANDLE hFile)
516+
{
517+
if (hFile == INVALID_HANDLE_VALUE)
518+
return(FALSE);
519+
520+
HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32.dll"));
521+
if (hKernel32 == NULL)
522+
return(FALSE);
523+
524+
typedef BOOL(WINAPI* PFN_SFIBH)(_In_ HANDLE, _In_ FILE_INFO_BY_HANDLE_CLASS, _In_ LPVOID, _In_ DWORD);
525+
PFN_SFIBH pfnSetFileInformationByHandle = (PFN_SFIBH)GetProcAddress(hKernel32, "SetFileInformationByHandle");
526+
if (pfnSetFileInformationByHandle == NULL)
527+
return(FALSE);
528+
529+
FILE_DISPOSITION_INFO fdi;
530+
fdi.DeleteFile = TRUE;
531+
return(pfnSetFileInformationByHandle(hFile, FileDispositionInfo, &fdi, sizeof(fdi)));
483532
}
484533

485534
VOID WINAPI HashCalcSetSavePrefix( PHASHCALCCONTEXT phcctx, PTSTR pszSave )

HashCalc.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ typedef struct {
9090

9191
// Per-file data
9292
typedef struct {
93-
BOOL bValid; // FALSE if the file could not be opened
9493
UINT cchPath; // length of path in characters, not including NULL
9594
WHRESULTEX results; // hash results
9695
#ifdef _TIMED
@@ -101,10 +100,12 @@ typedef struct {
101100
} HASHCALCITEM, *PHASHCALCITEM;
102101

103102
// Public functions
104-
VOID WINAPI HashCalcPrepare( PHASHCALCCONTEXT phcctx );
103+
BOOL WINAPI HashCalcPrepare( PHASHCALCCONTEXT phcctx );
105104
VOID WINAPI HashCalcInitSave( PHASHCALCCONTEXT phcctx );
106105
VOID WINAPI HashCalcSetSaveFormat( PHASHCALCCONTEXT phcctx );
107106
BOOL WINAPI HashCalcWriteResult( PHASHCALCCONTEXT phcctx, PHASHCALCITEM pItem );
107+
VOID WINAPI HashCalcClearInvalid( PWHRESULTEX pwhres, WCHAR cInvalid );
108+
BOOL WINAPI HashCalcDeleteFileByHandle( HANDLE hFile );
108109
VOID WINAPI HashCalcTogglePrep( PHASHCALCCONTEXT phcctx, BOOL bState );
109110

110111
#ifdef __cplusplus

HashCheck.rc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ IDD_OPTIONS DIALOGEX 10, 10, 200, 264
128128
AUTORADIOBUTTON "", IDC_OPT_ENCODING_UTF16, 13, 99, 174, 10
129129
AUTORADIOBUTTON "", IDC_OPT_ENCODING_ANSI, 13, 113, 174, 10
130130
GROUPBOX "", IDC_OPT_CHK, 7, 137, 186, 58, WS_GROUP
131-
AUTOCHECKBOX "CRC-32",IDC_OPT_CHK_CRC32, 13, 150, 54, 10, WS_TABSTOP
131+
AUTOCHECKBOX "C&RC-32",IDC_OPT_CHK_CRC32, 13, 150, 54, 10, WS_TABSTOP
132132
AUTOCHECKBOX "MD5",IDC_OPT_CHK_MD5, 13, 164, 54, 10, WS_TABSTOP
133133
AUTOCHECKBOX "SHA-1",IDC_OPT_CHK_SHA1, 13, 178, 54, 10, WS_TABSTOP
134134
AUTOCHECKBOX "SHA-256",IDC_OPT_CHK_SHA256, 100, 150, 54, 10, WS_TABSTOP

HashCheckCommon.c

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ HANDLE __fastcall CreateThreadCRT( PVOID pThreadProc, PVOID pvParam )
2828
pcmnctx->cHandledMsgs = 0;
2929
pcmnctx->hWndPBTotal = GetDlgItem(pcmnctx->hWnd, IDC_PROG_TOTAL);
3030
pcmnctx->hWndPBFile = GetDlgItem(pcmnctx->hWnd, IDC_PROG_FILE);
31+
if (pcmnctx->hUnpauseEvent == NULL)
32+
pcmnctx->hUnpauseEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
3133
SendMessage(pcmnctx->hWndPBFile, PBM_SETRANGE, 0, MAKELPARAM(0, PROGRESS_BAR_STEPS));
3234

3335
pThreadProc = WorkerThreadStartup;
@@ -138,7 +140,8 @@ VOID WINAPI SetProgressBarPause( PCOMMONCONTEXT pcmnctx, WPARAM iState )
138140
// Vista's progress bar is buggy--if you pause it while it is animating,
139141
// the color will not change (but it will stop animating), so it may
140142
// be necessary to send another PBM_SETSTATE to get it right
141-
SetTimer(pcmnctx->hWnd, TIMER_ID_PAUSE, 750, NULL);
143+
if (iState == PBST_PAUSED)
144+
SetTimer(pcmnctx->hWnd, TIMER_ID_PAUSE, 750, NULL);
142145
}
143146
}
144147

@@ -188,7 +191,7 @@ VOID WINAPI WorkerThreadStop( PCOMMONCONTEXT pcmnctx )
188191
}
189192

190193
// Disable the control buttons
191-
if (!(pcmnctx->dwFlags & HCF_EXIT_PENDING))
194+
if (! (pcmnctx->dwFlags & (HCF_EXIT_PENDING | HCF_RESTARTING)))
192195
{
193196
EnableWindow(GetDlgItem(pcmnctx->hWnd, IDC_PAUSE), FALSE);
194197
EnableWindow(GetDlgItem(pcmnctx->hWnd, IDC_STOP), FALSE);
@@ -203,11 +206,11 @@ VOID WINAPI WorkerThreadCleanup( PCOMMONCONTEXT pcmnctx )
203206
// There are only two times this function gets called:
204207
// Case 1: The worker thread has exited on its own, and this function
205208
// was invoked in response to the thread's exit notification.
206-
// Case 2: A forced abort was requested (app exit, system shutdown, etc.),
209+
// Case 2: A forced abort was requested (app exit, settings change, etc.),
207210
// where this is called right after calling WorkerThreadStop to signal the
208211
// thread to exit.
209212

210-
if (pcmnctx->hThread)
213+
if (pcmnctx->hThread != NULL)
211214
{
212215
if (pcmnctx->status != INACTIVE)
213216
{
@@ -220,12 +223,19 @@ VOID WINAPI WorkerThreadCleanup( PCOMMONCONTEXT pcmnctx )
220223
}
221224

222225
CloseHandle(pcmnctx->hThread);
223-
CloseHandle(pcmnctx->hUnpauseEvent);
226+
pcmnctx->hThread = NULL;
224227
}
225228

229+
// If we're done with the Unpause event and it's open, close it
230+
if (!(pcmnctx->dwFlags & HCF_RESTARTING) && pcmnctx->hUnpauseEvent != NULL)
231+
{
232+
CloseHandle(pcmnctx->hUnpauseEvent);
233+
pcmnctx->hUnpauseEvent = NULL;
234+
}
235+
226236
pcmnctx->status = CLEANUP_COMPLETED;
227237

228-
if (!(pcmnctx->dwFlags & HCF_EXIT_PENDING))
238+
if (! (pcmnctx->dwFlags & (HCF_EXIT_PENDING | HCF_RESTARTING)))
229239
{
230240
static const UINT16 arCtrls[] =
231241
{
@@ -244,13 +254,11 @@ VOID WINAPI WorkerThreadCleanup( PCOMMONCONTEXT pcmnctx )
244254

245255
DWORD WINAPI WorkerThreadStartup( PCOMMONCONTEXT pcmnctx )
246256
{
247-
pcmnctx->hUnpauseEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
248-
249257
pcmnctx->pfnWorkerMain(pcmnctx);
250258

251259
pcmnctx->status = INACTIVE;
252260

253-
if (!(pcmnctx->dwFlags & HCF_EXIT_PENDING))
261+
if (! (pcmnctx->dwFlags & (HCF_EXIT_PENDING | HCF_RESTARTING)))
254262
PostMessage(pcmnctx->hWnd, HM_WORKERTHREAD_DONE, (WPARAM)pcmnctx, 0);
255263

256264
return(0);
@@ -323,7 +331,7 @@ __inline VOID UpdateProgressBar( HWND hWndPBFile, PCRITICAL_SECTION pCritSec,
323331
}
324332
}
325333

326-
VOID WINAPI WorkerThreadHashFile( PCOMMONCONTEXT pcmnctx, PCTSTR pszPath, PBOOL pbSuccess,
334+
VOID WINAPI WorkerThreadHashFile( PCOMMONCONTEXT pcmnctx, PCTSTR pszPath,
327335
PWHCTXEX pwhctx, PWHRESULTEX pwhres, PBYTE pbuffer,
328336
PFILESIZE pFileSize, LPARAM lParam,
329337
PCRITICAL_SECTION pUpdateCritSec, volatile ULONGLONG* pcbCurrentMaxSize
@@ -334,8 +342,6 @@ VOID WINAPI WorkerThreadHashFile( PCOMMONCONTEXT pcmnctx, PCTSTR pszPath, PBOOL
334342
{
335343
HANDLE hFile;
336344

337-
*pbSuccess = FALSE;
338-
339345
// If the worker thread is working so fast that the UI cannot catch up,
340346
// pause for a bit to let things settle down
341347
while (pcmnctx->cSentMsgs > pcmnctx->cHandledMsgs + 50)
@@ -347,6 +353,17 @@ VOID WINAPI WorkerThreadHashFile( PCOMMONCONTEXT pcmnctx, PCTSTR pszPath, PBOOL
347353
return;
348354
}
349355

356+
// This can happen if a user changes the hash selection in HashProp (if no
357+
// new hashes were selected; we still want to run the throttling code above)
358+
if (pwhctx->dwFlags == 0)
359+
{
360+
#ifdef _TIMED
361+
if (pdwElapsed)
362+
*pdwElapsed = 0;
363+
#endif
364+
return;
365+
}
366+
350367
// Indicate that we want lower-case results (TODO: make this an option)
351368
pwhctx->uCaseMode = WHFMT_LOWERCASE;
352369

@@ -413,8 +430,11 @@ VOID WINAPI WorkerThreadHashFile( PCOMMONCONTEXT pcmnctx, PCTSTR pszPath, PBOOL
413430
if (pdwElapsed)
414431
*pdwElapsed = GetTickCount() - dwStarted;
415432
#endif
416-
if (cbFileRead == cbFileSize)
417-
*pbSuccess = TRUE;
433+
// If we encountered a file read error
434+
if (cbFileRead != cbFileSize)
435+
// Clear the valid-results bits for the hashes we just calculated
436+
// (they are set by WHFinishEx, but they're apparently *not* valid)
437+
pwhres->dwFlags &= ~pwhctx->dwFlags;
418438

419439
if (bUpdateProgress)
420440
UpdateProgressBar(pcmnctx->hWndPBFile, pUpdateCritSec, &bCurrentlyUpdating,

HashCheckCommon.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,15 @@ extern "C" {
3535
#define THREAD_SUSPEND_ERROR ((DWORD)-1)
3636
#define TIMER_ID_PAUSE 1
3737

38-
// Flags
39-
#define HCF_EXIT_PENDING 0x0001
40-
#define HCF_MARQUEE 0x0002
41-
#define HVF_HAS_SET_TYPE 0x0004
42-
#define HVF_ITEM_HILITE 0x0008
43-
#define HPF_HAS_RESIZED 0x0004
38+
// Flags of DWORD width (which is an unsigned long)
39+
#define HCF_EXIT_PENDING 0x0001UL
40+
#define HCF_MARQUEE 0x0002UL
41+
#define HCF_RESTARTING 0x0004UL
42+
#define HVF_HAS_SET_TYPE 0x0008UL
43+
#define HVF_ITEM_HILITE 0x0010UL
44+
#define HPF_HAS_RESIZED 0x0008UL
45+
#define HPF_HLIST_PREPPED 0x0010UL
46+
#define HPF_INTERRUPTED 0x0020UL
4447

4548
// Messages
4649
#define HM_WORKERTHREAD_DONE (WM_APP + 0) // wParam = ctx, lParam = 0
@@ -100,7 +103,7 @@ VOID WINAPI WorkerThreadCleanup( PCOMMONCONTEXT pcmnctx );
100103

101104
// Worker thread functions
102105
DWORD WINAPI WorkerThreadStartup( PCOMMONCONTEXT pcmnctx );
103-
VOID WINAPI WorkerThreadHashFile( PCOMMONCONTEXT pcmnctx, PCTSTR pszPath, PBOOL pbSuccess,
106+
VOID WINAPI WorkerThreadHashFile( PCOMMONCONTEXT pcmnctx, PCTSTR pszPath,
104107
PWHCTXEX pwhctx, PWHRESULTEX pwhres, PBYTE pbuffer,
105108
PFILESIZE pFileSize, LPARAM lParam,
106109
PCRITICAL_SECTION pUpdateCritSec, volatile ULONGLONG* pcbCurrentMaxSize

HashCheckTranslations.rc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ STRINGTABLE LANGUAGE LANG_CZECH, SUBLANG_DEFAULT
229229
IDS_OPT_TITLE "HashCheck nastavení"
230230
IDS_OPT_CM "Integrace"
231231
IDS_OPT_CM_ALWAYS "&Zobraz v kontextovém menu"
232-
IDS_OPT_CM_EXTENDED "Zobraz v &rozšířeném kontextovém menu"
232+
IDS_OPT_CM_EXTENDED "Zobraz v rozšířeném &kontextovém menu"
233233
IDS_OPT_CM_NEVER "&Nezobrazuj v kontextovém menu"
234234
IDS_OPT_ENCODING "Znaková sada souboru kontrolního součtu"
235235
IDS_OPT_ENCODING_UTF8 "Ulož v U&TF-8"

0 commit comments

Comments
 (0)