├── JkDefrag ├── JkDefrag2005.vcproj ├── JkDefrag2008.vcproj ├── Manifest │ └── JkDefrag.manifest ├── Resource │ ├── JkDefrag.aps │ ├── JkDefrag.ico │ ├── JkDefrag.rc │ └── resource.h └── Source │ ├── JKDefrag.h │ ├── JKDefragLog.cpp │ ├── JKDefragLog.h │ ├── JKDefragStruct.cpp │ ├── JKDefragStruct.h │ ├── JkDefrag.cpp │ ├── JkDefragGui.cpp │ ├── JkDefragGui.h │ ├── JkDefragLib.cpp │ ├── JkDefragLib.h │ ├── ScanFat.cpp │ ├── ScanFat.h │ ├── ScanNtfs.cpp │ ├── ScanNtfs.h │ ├── StdAfx.cpp │ └── StdAfx.h ├── JkDefrag2005.sln ├── JkDefrag2008.sln ├── LICENSE └── README.md /JkDefrag/JkDefrag2005.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 26 | 29 | 32 | 35 | 38 | 41 | 54 | 57 | 60 | 63 | 73 | 76 | 79 | 82 | 85 | 88 | 91 | 94 | 95 | 103 | 106 | 109 | 112 | 115 | 118 | 127 | 130 | 133 | 136 | 148 | 151 | 154 | 157 | 160 | 163 | 166 | 169 | 170 | 171 | 172 | 173 | 174 | 179 | 182 | 183 | 186 | 187 | 190 | 191 | 194 | 195 | 198 | 199 | 202 | 203 | 206 | 207 | 210 | 213 | 217 | 218 | 221 | 225 | 226 | 227 | 228 | 233 | 236 | 237 | 240 | 241 | 244 | 245 | 248 | 249 | 252 | 253 | 256 | 257 | 260 | 261 | 264 | 265 | 266 | 269 | 272 | 273 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | -------------------------------------------------------------------------------- /JkDefrag/JkDefrag2008.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 26 | 29 | 32 | 35 | 38 | 41 | 54 | 57 | 60 | 63 | 73 | 76 | 79 | 82 | 85 | 88 | 91 | 94 | 95 | 103 | 106 | 109 | 112 | 115 | 118 | 127 | 130 | 133 | 136 | 148 | 151 | 154 | 157 | 160 | 163 | 166 | 169 | 170 | 171 | 172 | 173 | 174 | 179 | 182 | 183 | 186 | 187 | 190 | 191 | 194 | 195 | 198 | 199 | 202 | 203 | 206 | 207 | 210 | 213 | 217 | 218 | 221 | 225 | 226 | 227 | 228 | 233 | 236 | 237 | 240 | 241 | 244 | 245 | 248 | 249 | 252 | 253 | 256 | 257 | 260 | 261 | 264 | 265 | 266 | 269 | 272 | 273 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | -------------------------------------------------------------------------------- /JkDefrag/Manifest/JkDefrag.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /JkDefrag/Resource/JkDefrag.aps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dasmurphy/JKDefrag-Original/4e8e60e9eb0108a740b46e7e775776547642b0cd/JkDefrag/Resource/JkDefrag.aps -------------------------------------------------------------------------------- /JkDefrag/Resource/JkDefrag.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dasmurphy/JKDefrag-Original/4e8e60e9eb0108a740b46e7e775776547642b0cd/JkDefrag/Resource/JkDefrag.ico -------------------------------------------------------------------------------- /JkDefrag/Resource/JkDefrag.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "afxres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // Neutral resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) 19 | #ifdef _WIN32 20 | LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL 21 | #pragma code_page(1252) 22 | #endif //_WIN32 23 | 24 | ///////////////////////////////////////////////////////////////////////////// 25 | // 26 | // Dialog 27 | // 28 | 29 | DLG_SCRNSAVECONFIGURE DIALOG 6, 18, 200, 147 30 | STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU 31 | CAPTION "JkDefrag screensaver setup" 32 | FONT 8, "MS Shell Dlg" 33 | BEGIN 34 | CTEXT "JkDefrag screensaver v3.36",1001,0,2,200,8 35 | LTEXT "Commandline options (optional):",1002,6,20,170,8 36 | EDITTEXT 1003,5,30,189,13,ES_AUTOHSCROLL 37 | LTEXT "Screensaver to run after JkDefrag has finished:",1004,5,51,180,8 38 | COMBOBOX 1005,5,60,135,200,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP 39 | PUSHBUTTON "Settings...",1006,144,60,51,13 40 | LTEXT "Do not defrag if last run was less than",1007,5,85,180,8 41 | COMBOBOX 1008,127,83,30,200,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP 42 | LTEXT "hours ago.",1009,160,85,50,8 43 | LTEXT "Status bar:",1010,5,107,50,8 44 | COMBOBOX 1011,45,105,150,200,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP 45 | PUSHBUTTON "OK",1012,80,128,40,13 46 | END 47 | 48 | 49 | ///////////////////////////////////////////////////////////////////////////// 50 | // 51 | // String Table 52 | // 53 | 54 | STRINGTABLE 55 | BEGIN 56 | 1 "JkDefrag screensaver" 57 | IDS_STRING2 "Version 2.2" 58 | END 59 | 60 | #endif // Neutral resources 61 | ///////////////////////////////////////////////////////////////////////////// 62 | 63 | 64 | ///////////////////////////////////////////////////////////////////////////// 65 | // English (U.S.) resources 66 | 67 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 68 | #ifdef _WIN32 69 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 70 | #pragma code_page(1252) 71 | #endif //_WIN32 72 | 73 | ///////////////////////////////////////////////////////////////////////////// 74 | // 75 | // Icon 76 | // 77 | 78 | // Icon with lowest ID value placed first to ensure application icon 79 | // remains consistent on all systems. 80 | 1 ICON "jkdefrag.ico" 81 | 82 | ///////////////////////////////////////////////////////////////////////////// 83 | // 84 | // Version 85 | // 86 | 87 | 1 VERSIONINFO 88 | FILEVERSION 3,36,0,2 89 | PRODUCTVERSION 3,36,0,2 90 | FILEFLAGSMASK 0x0L 91 | #ifdef _DEBUG 92 | FILEFLAGS 0x1L 93 | #else 94 | FILEFLAGS 0x0L 95 | #endif 96 | FILEOS 0x0L 97 | FILETYPE 0x0L 98 | FILESUBTYPE 0x0L 99 | BEGIN 100 | BLOCK "StringFileInfo" 101 | BEGIN 102 | BLOCK "040904e4" 103 | BEGIN 104 | VALUE "ProductName", "JkDefrag" 105 | VALUE "ProductVersion", "3.36" 106 | VALUE "LegalCopyright", "GNU General Public License" 107 | VALUE "CompanyName", "J.C. Kessels" 108 | VALUE "FileDescription", "JkDefrag - disk defragmentation and optimization tool" 109 | VALUE "FileVersion", "3.36" 110 | VALUE "InternalName", "JkDefrag" 111 | VALUE "OriginalFilename", "JkDefrag.exe" 112 | END 113 | END 114 | BLOCK "VarFileInfo" 115 | BEGIN 116 | VALUE "Translation", 0x409, 1252 117 | END 118 | END 119 | 120 | 121 | #ifdef APSTUDIO_INVOKED 122 | ///////////////////////////////////////////////////////////////////////////// 123 | // 124 | // TEXTINCLUDE 125 | // 126 | 127 | 1 TEXTINCLUDE 128 | BEGIN 129 | "resource.\0" 130 | END 131 | 132 | 133 | 3 TEXTINCLUDE 134 | BEGIN 135 | "\r\0" 136 | END 137 | 138 | #endif // APSTUDIO_INVOKED 139 | 140 | #endif // English (U.S.) resources 141 | ///////////////////////////////////////////////////////////////////////////// 142 | 143 | 144 | 145 | #ifndef APSTUDIO_INVOKED 146 | ///////////////////////////////////////////////////////////////////////////// 147 | // 148 | // Generated from the TEXTINCLUDE 3 resource. 149 | // 150 | 151 | 152 | ///////////////////////////////////////////////////////////////////////////// 153 | #endif // not APSTUDIO_INVOKED 154 | 155 | -------------------------------------------------------------------------------- /JkDefrag/Source/JKDefrag.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef JKDEFRAG 3 | #define JKDEFRAG 4 | 5 | class JKDefrag 6 | { 7 | public: 8 | JKDefrag(); 9 | ~JKDefrag(); 10 | 11 | // Get instance of the class 12 | static JKDefrag *getInstance(); 13 | static void releaseInstance(); 14 | 15 | WPARAM startProgram(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); 16 | 17 | static DWORD WINAPI DefragThread(LPVOID); 18 | 19 | #ifdef _DEBUG 20 | 21 | static LONG __stdcall CrashReport(EXCEPTION_POINTERS *ExceptionInfo); 22 | 23 | #endif 24 | 25 | int AlreadyRunning(void); 26 | 27 | protected: 28 | private: 29 | int Running; /* If not RUNNING then stop defragging. */ 30 | int IamRunning; 31 | 32 | /* Debug level. 33 | 0: Fatal errors. 34 | 1: Warning messages. 35 | 2: General progress messages. 36 | 3: Detailed progress messages. 37 | 4: Detailed file information. 38 | 5: Detailed gap-filling messages. 39 | 6: Detailed gap-finding messages. 40 | */ 41 | int Debug; 42 | 43 | JKDefragGui *m_jkGui; 44 | JKDefragLib *m_jkLib; 45 | JKDefragLog *m_jkLog; 46 | JKDefragStruct *m_jkStruct; 47 | 48 | // static member that is an instance of itself 49 | static JKDefrag *m_jkDefrag; 50 | }; 51 | 52 | #endif -------------------------------------------------------------------------------- /JkDefrag/Source/JKDefragLog.cpp: -------------------------------------------------------------------------------- 1 | #include "StdAfx.h" 2 | 3 | JKDefragLog::JKDefragLog() 4 | { 5 | WCHAR *p1; 6 | 7 | m_jkLib = JKDefragLib::getInstance(); 8 | 9 | GetModuleFileNameW(NULL,MyName,MAX_PATH); 10 | GetShortPathNameW(MyName,MyShortName,MAX_PATH); 11 | GetLongPathNameW(MyShortName,MyName,MAX_PATH); 12 | 13 | /* Determine default path to logfile. */ 14 | swprintf_s(LogFile,MAX_PATH,L"%s",MyName); 15 | 16 | p1 = m_jkLib->stristrW(LogFile,L".exe"); 17 | 18 | if (p1 == NULL) p1 = m_jkLib->stristrW(LogFile,L".scr"); 19 | 20 | if (p1 != NULL) 21 | { 22 | *p1 = '\0'; 23 | 24 | wcscat_s(LogFile,MAX_PATH,L".log"); 25 | _wunlink(LogFile); 26 | 27 | } 28 | else 29 | { 30 | *LogFile = '\0'; 31 | } 32 | } 33 | 34 | void JKDefragLog::SetLogFilename(WCHAR *fileName) 35 | { 36 | /* Determine default path to logfile. */ 37 | swprintf_s(LogFile,MAX_PATH,L"%s",fileName); 38 | _wunlink(LogFile); 39 | } 40 | 41 | WCHAR *JKDefragLog::GetLogFilename() 42 | { 43 | return LogFile; 44 | } 45 | 46 | /* Write a text to the logfile. The parameters are the same as for the "printf" 47 | functions, a Format string and a series of parameters. */ 48 | void JKDefragLog::LogMessage(WCHAR *Format, ...) 49 | { 50 | va_list VarArgs; 51 | FILE *Fout; 52 | int Result; 53 | time_t Now; 54 | struct tm NowTm; 55 | 56 | /* If there is no message then return. */ 57 | if (Format == NULL) return; 58 | 59 | /* If there is no logfile then return. */ 60 | if (*LogFile == '\0') return; 61 | 62 | /* Open the logfile. */ 63 | Result = _wfopen_s(&Fout,LogFile,L"a, ccs=UTF-8"); 64 | if ((Result != 0) || (Fout == NULL)) return; 65 | 66 | /* Write the string to the logfile. */ 67 | time(&Now); 68 | Result = localtime_s(&NowTm,&Now); 69 | fwprintf_s(Fout,L"%02lu:%02lu:%02lu ",NowTm.tm_hour,NowTm.tm_min,NowTm.tm_sec); 70 | va_start(VarArgs,Format); 71 | vfwprintf_s(Fout,Format,VarArgs); 72 | va_end(VarArgs); 73 | fwprintf_s(Fout,L"\n"); 74 | 75 | /* Close the logfile. */ 76 | fflush(Fout); 77 | fclose(Fout); 78 | } 79 | 80 | -------------------------------------------------------------------------------- /JkDefrag/Source/JKDefragLog.h: -------------------------------------------------------------------------------- 1 | #ifndef __JKDEFRAGLOG_H__ 2 | #define __JKDEFRAGLOG_H__ 3 | 4 | class JKDefragLog 5 | { 6 | public: 7 | JKDefragLog(); 8 | void LogMessage(WCHAR *Format, ...); 9 | void SetLogFilename(WCHAR *fileName); 10 | 11 | WCHAR MyName[MAX_PATH]; 12 | WCHAR MyShortName[MAX_PATH]; 13 | 14 | WCHAR *GetLogFilename(); 15 | 16 | protected: 17 | private: 18 | WCHAR LogFile[MAX_PATH]; 19 | 20 | JKDefragLib *m_jkLib; 21 | }; 22 | 23 | #endif -------------------------------------------------------------------------------- /JkDefrag/Source/JKDefragStruct.cpp: -------------------------------------------------------------------------------- 1 | #include "StdAfx.h" 2 | 3 | #include "JKDefragStruct.h" 4 | 5 | JKDefragStruct::JKDefragStruct() 6 | { 7 | wcsncpy_s(VERSIONTEXT, L"JkDefrag 3.36", 100); 8 | } 9 | 10 | JKDefragStruct::~JKDefragStruct() 11 | { 12 | } -------------------------------------------------------------------------------- /JkDefrag/Source/JKDefragStruct.h: -------------------------------------------------------------------------------- 1 | #ifndef __JKDEFRAGSTRUCT_H__ 2 | #define __JKDEFRAGSTRUCT_H__ 3 | 4 | /* The colors used by the defragger. */ 5 | //#define COLOREMPTY 0 /* Empty diskspace. */ 6 | //#define COLORALLOCATED 1 /* Used diskspace / system files. */ 7 | //#define COLORUNFRAGMENTED 2 /* Unfragmented files. */ 8 | //#define COLORUNMOVABLE 3 /* Unmovable files. */ 9 | //#define COLORFRAGMENTED 4 /* Fragmented files. */ 10 | //#define COLORBUSY 5 /* Busy color. */ 11 | //#define COLORMFT 6 /* MFT reserved zones. */ 12 | //#define COLORSPACEHOG 7 /* Spacehogs. */ 13 | //#define COLORBACK 8 /* Background color. */ 14 | 15 | class JKDefragStruct 16 | { 17 | public: 18 | JKDefragStruct(); 19 | ~JKDefragStruct(); 20 | 21 | WCHAR VERSIONTEXT[100]; 22 | 23 | enum colors { 24 | COLOREMPTY, 25 | COLORALLOCATED, 26 | COLORUNFRAGMENTED, 27 | COLORUNMOVABLE, 28 | COLORFRAGMENTED, 29 | COLORBUSY, 30 | COLORMFT, 31 | COLORSPACEHOG, 32 | COLORBACK 33 | }; 34 | 35 | protected: 36 | 37 | private: 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /JkDefrag/Source/JkDefrag.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | JkDefrag -- Defragment and optimize all harddisks. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | For the full text of the license see the "License gpl.txt" file. 15 | 16 | Jeroen C. Kessels 17 | Internet Engineer 18 | http://www.kessels.com/ 19 | */ 20 | 21 | #include "StdAfx.h" 22 | 23 | #include "JKDefrag.h" 24 | 25 | JKDefrag *JKDefrag::m_jkDefrag = 0; 26 | 27 | JKDefrag::JKDefrag() 28 | { 29 | Running = STOPPED; 30 | 31 | Debug = 1; 32 | 33 | m_jkGui = JKDefragGui::getInstance(); 34 | m_jkLib = JKDefragLib::getInstance(); 35 | 36 | m_jkLog = new JKDefragLog(); 37 | m_jkStruct = new JKDefragStruct(); 38 | } 39 | 40 | JKDefrag::~JKDefrag() 41 | { 42 | delete m_jkLog; 43 | delete m_jkStruct; 44 | 45 | delete m_jkDefrag; 46 | } 47 | 48 | JKDefrag *JKDefrag::getInstance() 49 | { 50 | if (m_jkDefrag == NULL) 51 | { 52 | m_jkDefrag = new JKDefrag(); 53 | } 54 | 55 | return m_jkDefrag; 56 | } 57 | 58 | void JKDefrag::releaseInstance() 59 | { 60 | if (m_jkDefrag != NULL) 61 | { 62 | delete m_jkDefrag; 63 | } 64 | } 65 | 66 | WPARAM JKDefrag::startProgram(HINSTANCE hInstance, 67 | HINSTANCE hPrevInstance, 68 | LPSTR lpCmdLine, 69 | int nCmdShow) 70 | { 71 | IamRunning = RUNNING; 72 | 73 | /* Test if another instance is already running. */ 74 | if (AlreadyRunning() == YES) return(0); 75 | 76 | #ifdef _DEBUG 77 | /* Setup crash report handler. */ 78 | SetUnhandledExceptionFilter(&JKDefrag::CrashReport); 79 | #endif 80 | 81 | m_jkGui->Initialize(hInstance,nCmdShow, m_jkLog, Debug); 82 | 83 | /* Start up the defragmentation and timer threads. */ 84 | if (CreateThread(NULL, 0, &JKDefrag::DefragThread, NULL, 0, NULL) == NULL) return(0); 85 | 86 | WPARAM wParam = m_jkGui->DoModal(); 87 | 88 | /* If the defragger is still running then ask & wait for it to stop. */ 89 | IamRunning = STOPPED; 90 | 91 | m_jkLib->StopJkDefrag(&Running,0); 92 | 93 | return wParam; 94 | } 95 | 96 | 97 | #ifdef _DEBUG 98 | 99 | /* 100 | 101 | Write a crash report to the log. 102 | To test the crash handler add something like this: 103 | char *p1; 104 | p1 = 0; 105 | *p1 = 0; 106 | 107 | */ 108 | 109 | LONG __stdcall JKDefrag::CrashReport(EXCEPTION_POINTERS *ExceptionInfo) 110 | { 111 | IMAGEHLP_LINE64 SourceLine; 112 | DWORD LineDisplacement; 113 | STACKFRAME64 StackFrame; 114 | DWORD ImageType; 115 | BOOL Result; 116 | int FrameNumber; 117 | char s1[BUFSIZ]; 118 | WCHAR s2[BUFSIZ]; 119 | 120 | JKDefragLog *jkLog = m_jkDefrag->m_jkLog; 121 | JKDefragLib *jkLib = m_jkDefrag->m_jkLib; 122 | 123 | /* Exit if we're running inside a debugger. */ 124 | // if (IsDebuggerPresent() == TRUE) return(EXCEPTION_EXECUTE_HANDLER); 125 | 126 | jkLog->LogMessage(L"I have crashed!"); 127 | jkLog->LogMessage(L" Command line: %s",GetCommandLineW()); 128 | 129 | /* Show the type of exception. */ 130 | switch(ExceptionInfo->ExceptionRecord->ExceptionCode) { 131 | case EXCEPTION_ACCESS_VIOLATION : strcpy_s(s1,BUFSIZ,"ACCESS_VIOLATION (the memory could not be read or written)"); break; 132 | case EXCEPTION_DATATYPE_MISALIGNMENT : strcpy_s(s1,BUFSIZ,"DATATYPE_MISALIGNMENT (a datatype misalignment error was detected in a load or store instruction)"); break; 133 | case EXCEPTION_BREAKPOINT : strcpy_s(s1,BUFSIZ,"BREAKPOINT"); break; 134 | case EXCEPTION_SINGLE_STEP : strcpy_s(s1,BUFSIZ,"SINGLE_STEP"); break; 135 | case EXCEPTION_ARRAY_BOUNDS_EXCEEDED : strcpy_s(s1,BUFSIZ,"ARRAY_BOUNDS_EXCEEDED"); break; 136 | case EXCEPTION_FLT_DENORMAL_OPERAND : strcpy_s(s1,BUFSIZ,"FLT_DENORMAL_OPERAND"); break; 137 | case EXCEPTION_FLT_DIVIDE_BY_ZERO : strcpy_s(s1,BUFSIZ,"FLT_DIVIDE_BY_ZERO"); break; 138 | case EXCEPTION_FLT_INEXACT_RESULT : strcpy_s(s1,BUFSIZ,"FLT_INEXACT_RESULT"); break; 139 | case EXCEPTION_FLT_INVALID_OPERATION : strcpy_s(s1,BUFSIZ,"FLT_INVALID_OPERATION"); break; 140 | case EXCEPTION_FLT_OVERFLOW : strcpy_s(s1,BUFSIZ,"FLT_OVERFLOW"); break; 141 | case EXCEPTION_FLT_STACK_CHECK : strcpy_s(s1,BUFSIZ,"FLT_STACK_CHECK"); break; 142 | case EXCEPTION_FLT_UNDERFLOW : strcpy_s(s1,BUFSIZ,"FLT_UNDERFLOW"); break; 143 | case EXCEPTION_INT_DIVIDE_BY_ZERO : strcpy_s(s1,BUFSIZ,"INT_DIVIDE_BY_ZERO"); break; 144 | case EXCEPTION_INT_OVERFLOW : strcpy_s(s1,BUFSIZ,"INT_OVERFLOW"); break; 145 | case EXCEPTION_PRIV_INSTRUCTION : strcpy_s(s1,BUFSIZ,"PRIV_INSTRUCTION"); break; 146 | case EXCEPTION_IN_PAGE_ERROR : strcpy_s(s1,BUFSIZ,"IN_PAGE_ERROR"); break; 147 | case EXCEPTION_ILLEGAL_INSTRUCTION : strcpy_s(s1,BUFSIZ,"ILLEGAL_INSTRUCTION"); break; 148 | case EXCEPTION_NONCONTINUABLE_EXCEPTION : strcpy_s(s1,BUFSIZ,"NONCONTINUABLE_EXCEPTION"); break; 149 | case EXCEPTION_STACK_OVERFLOW : strcpy_s(s1,BUFSIZ,"STACK_OVERFLOW"); break; 150 | case EXCEPTION_INVALID_DISPOSITION : strcpy_s(s1,BUFSIZ,"INVALID_DISPOSITION"); break; 151 | case EXCEPTION_GUARD_PAGE : strcpy_s(s1,BUFSIZ,"GUARD_PAGE"); break; 152 | case EXCEPTION_INVALID_HANDLE : strcpy_s(s1,BUFSIZ,"INVALID_HANDLE"); break; 153 | case CONTROL_C_EXIT : strcpy_s(s1,BUFSIZ,"STATUS_CONTROL_C_EXIT"); break; 154 | case DBG_TERMINATE_THREAD : strcpy_s(s1,BUFSIZ,"DBG_TERMINATE_THREAD (Debugger terminated thread)"); break; 155 | case DBG_TERMINATE_PROCESS : strcpy_s(s1,BUFSIZ,"DBG_TERMINATE_PROCESS (Debugger terminated process)"); break; 156 | case DBG_CONTROL_C : strcpy_s(s1,BUFSIZ,"DBG_CONTROL_C (Debugger got control C)"); break; 157 | case DBG_CONTROL_BREAK : strcpy_s(s1,BUFSIZ,"DBG_CONTROL_BREAK (Debugger received control break)"); break; 158 | case DBG_COMMAND_EXCEPTION : strcpy_s(s1,BUFSIZ,"DBG_COMMAND_EXCEPTION (Debugger command communication exception)"); break; 159 | default : strcpy_s(s1,BUFSIZ,"(unknown exception)"); 160 | } 161 | 162 | jkLog->LogMessage(L" Exception: %S",s1); 163 | 164 | /* Try to show the linenumber of the sourcefile. */ 165 | SymSetOptions(SymGetOptions() || SYMOPT_LOAD_LINES); 166 | Result = SymInitialize(GetCurrentProcess(),NULL,TRUE); 167 | 168 | if (Result == FALSE) 169 | { 170 | jkLib->SystemErrorStr(GetLastError(),s2,BUFSIZ); 171 | 172 | jkLog->LogMessage(L" Failed to initialize SymInitialize(): %s",s2); 173 | 174 | return(EXCEPTION_EXECUTE_HANDLER); 175 | } 176 | 177 | ZeroMemory(&StackFrame,sizeof(StackFrame)); 178 | 179 | #ifdef _M_IX86 180 | ImageType = IMAGE_FILE_MACHINE_I386; 181 | StackFrame.AddrPC.Offset = ExceptionInfo->ContextRecord->Eip; 182 | StackFrame.AddrPC.Mode = AddrModeFlat; 183 | StackFrame.AddrFrame.Offset = ExceptionInfo->ContextRecord->Ebp; 184 | StackFrame.AddrFrame.Mode = AddrModeFlat; 185 | StackFrame.AddrStack.Offset = ExceptionInfo->ContextRecord->Esp; 186 | StackFrame.AddrStack.Mode = AddrModeFlat; 187 | #elif _M_X64 188 | ImageType = IMAGE_FILE_MACHINE_AMD64; 189 | StackFrame.AddrPC.Offset = ExceptionInfo->ContextRecord->Rip; 190 | StackFrame.AddrPC.Mode = AddrModeFlat; 191 | StackFrame.AddrFrame.Offset = ExceptionInfo->ContextRecord->Rsp; 192 | StackFrame.AddrFrame.Mode = AddrModeFlat; 193 | StackFrame.AddrStack.Offset = ExceptionInfo->ContextRecord->Rsp; 194 | StackFrame.AddrStack.Mode = AddrModeFlat; 195 | #elif _M_IA64 196 | ImageType = IMAGE_FILE_MACHINE_IA64; 197 | StackFrame.AddrPC.Offset = ExceptionInfo->ContextRecord->StIIP; 198 | StackFrame.AddrPC.Mode = AddrModeFlat; 199 | StackFrame.AddrFrame.Offset = ExceptionInfo->ContextRecord->IntSp; 200 | StackFrame.AddrFrame.Mode = AddrModeFlat; 201 | StackFrame.AddrBStore.Offset = ExceptionInfo->ContextRecord->RsBSP; 202 | StackFrame.AddrBStore.Mode = AddrModeFlat; 203 | StackFrame.AddrStack.Offset = ExceptionInfo->ContextRecord->IntSp; 204 | StackFrame.AddrStack.Mode = AddrModeFlat; 205 | #endif 206 | for (FrameNumber = 1; ; FrameNumber++) { 207 | Result = StackWalk64(ImageType,GetCurrentProcess(),GetCurrentThread(),&StackFrame, 208 | ExceptionInfo->ContextRecord,NULL,SymFunctionTableAccess64,SymGetModuleBase64,NULL); 209 | if (Result == FALSE) break; 210 | if (StackFrame.AddrPC.Offset == StackFrame.AddrReturn.Offset) break; 211 | if (StackFrame.AddrPC.Offset != 0) { 212 | LineDisplacement = 0; 213 | ZeroMemory(&SourceLine,sizeof(SourceLine)); 214 | SourceLine.SizeOfStruct = sizeof(SourceLine); 215 | Result = SymGetLineFromAddr64(GetCurrentProcess(),StackFrame.AddrPC.Offset, 216 | &LineDisplacement,&SourceLine); 217 | if (Result == TRUE) { 218 | jkLog->LogMessage(L" %i. At line %d in '%S'",FrameNumber,SourceLine.LineNumber,SourceLine.FileName); 219 | } else { 220 | jkLog->LogMessage(L" %i. At line (unknown) in (unknown)",FrameNumber); 221 | /* 222 | SystemErrorStr(GetLastError(),s2,BUFSIZ); 223 | LogMessage(L" Error executing SymGetLineFromAddr64(): %s",s2); 224 | */ 225 | } 226 | } 227 | } 228 | 229 | /* Possible return values: 230 | EXCEPTION_CONTINUE_SEARCH = popup a window about the error, user has to click. 231 | EXCEPTION_CONTINUE_EXECUTION = infinite loop 232 | EXCEPTION_EXECUTE_HANDLER = stop program, do not run debugger 233 | */ 234 | return(EXCEPTION_EXECUTE_HANDLER); 235 | } 236 | #endif 237 | 238 | /* 239 | 240 | The main thread that performs all the work. Interpret the commandline 241 | parameters and call the defragger library. 242 | 243 | */ 244 | DWORD WINAPI JKDefrag::DefragThread(LPVOID) 245 | { 246 | int QuitOnFinish; 247 | int OptimizeMode; /* 1...11 */ 248 | int Speed; /* 0...100 */ 249 | double FreeSpace; /* 0...100 */ 250 | WCHAR **Excludes; 251 | WCHAR **SpaceHogs; 252 | int DoAllVolumes; 253 | LPWSTR *argv; 254 | int argc; 255 | time_t Now; 256 | struct tm NowTm; 257 | OSVERSIONINFO OsVersion; 258 | int i; 259 | 260 | JKDefragLog *jkLog = m_jkDefrag->m_jkLog; 261 | JKDefragStruct *jkStruct = m_jkDefrag->m_jkStruct; 262 | JKDefragGui *jkGui = m_jkDefrag->m_jkGui; 263 | JKDefragLib *jkLib = m_jkDefrag->m_jkLib; 264 | 265 | /* Setup the defaults. */ 266 | OptimizeMode = 2; 267 | Speed = 100; 268 | FreeSpace = 1; 269 | Excludes = NULL; 270 | SpaceHogs = NULL; 271 | QuitOnFinish = NO; 272 | 273 | /* Fetch the commandline. */ 274 | argv = CommandLineToArgvW(GetCommandLineW(),&argc); 275 | 276 | /* Scan the commandline arguments for "-l" and setup the logfile. */ 277 | if (argc > 1) 278 | { 279 | for (i = 1; i < argc; i++) 280 | { 281 | if (wcscmp(argv[i],L"-l") == 0) 282 | { 283 | i++; 284 | if (i >= argc) continue; 285 | 286 | jkLog->SetLogFilename(argv[i]); 287 | 288 | continue; 289 | } 290 | if ((wcsncmp(argv[i],L"-l",2) == 0) && (wcslen(argv[i]) >= 3)) 291 | { 292 | jkLog->SetLogFilename(&argv[i][2]); 293 | 294 | continue; 295 | } 296 | } 297 | } 298 | 299 | /* Show some standard information in the logfile. */ 300 | jkLog->LogMessage(jkStruct->VERSIONTEXT); 301 | time(&Now); 302 | 303 | localtime_s(&NowTm,&Now); 304 | jkLog->LogMessage(L"Date: %04lu/%02lu/%02lu",1900 + NowTm.tm_year,1 + NowTm.tm_mon,NowTm.tm_mday); 305 | 306 | ZeroMemory(&OsVersion,sizeof(OSVERSIONINFO)); 307 | OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 308 | 309 | if (GetVersionEx(&OsVersion) != 0) 310 | { 311 | jkLog->LogMessage(L"Windows version: v%lu.%lu build %lu %S",OsVersion.dwMajorVersion, 312 | OsVersion.dwMinorVersion,OsVersion.dwBuildNumber,OsVersion.szCSDVersion); 313 | } 314 | 315 | /* Scan the commandline again for all the other arguments. */ 316 | if (argc > 1) 317 | { 318 | for (i = 1; i < argc; i++) 319 | { 320 | if (wcscmp(argv[i],L"-a") == 0) 321 | { 322 | i++; 323 | 324 | if (i >= argc) 325 | { 326 | jkGui->ShowDebug(0,NULL,L"Error: you have not specified a number after the \"-a\" commandline argument."); 327 | 328 | continue; 329 | } 330 | 331 | OptimizeMode = _wtol(argv[i]); 332 | 333 | if ((OptimizeMode < 1) || (OptimizeMode > 11)) 334 | { 335 | jkGui->ShowDebug(0,NULL,L"Error: the number after the \"-a\" commandline argument is invalid."); 336 | 337 | OptimizeMode = 3; 338 | } 339 | 340 | OptimizeMode = OptimizeMode - 1; 341 | 342 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-a' accepted, optimizemode = %u",OptimizeMode+1); 343 | 344 | continue; 345 | } 346 | 347 | if (wcsncmp(argv[i],L"-a",2) == 0) 348 | { 349 | OptimizeMode = _wtol(&argv[i][2]); 350 | 351 | if ((OptimizeMode < 1) || (OptimizeMode > 11)) 352 | { 353 | jkGui->ShowDebug(0,NULL,L"Error: the number after the \"-a\" commandline argument is invalid."); 354 | 355 | OptimizeMode = 3; 356 | } 357 | 358 | OptimizeMode = OptimizeMode - 1; 359 | 360 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-a' accepted, optimizemode = %u",OptimizeMode+1); 361 | 362 | continue; 363 | } 364 | 365 | if (wcscmp(argv[i],L"-s") == 0) 366 | { 367 | i++; 368 | 369 | if (i >= argc) 370 | { 371 | jkGui->ShowDebug(0,NULL,L"Error: you have not specified a number after the \"-s\" commandline argument."); 372 | 373 | continue; 374 | } 375 | 376 | Speed = _wtol(argv[i]); 377 | 378 | if ((Speed < 1) || (Speed > 100)) 379 | { 380 | jkGui->ShowDebug(0,NULL,L"Error: the number after the \"-s\" commandline argument is invalid."); 381 | 382 | Speed = 100; 383 | } 384 | 385 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-s' accepted, speed = %u%%",Speed); 386 | 387 | continue; 388 | } 389 | 390 | if ((wcsncmp(argv[i],L"-s",2) == 0) && (wcslen(argv[i]) >= 3)) 391 | { 392 | Speed = _wtol(&argv[i][2]); 393 | 394 | if ((Speed < 1) || (Speed > 100)) 395 | { 396 | jkGui->ShowDebug(0,NULL,L"Error: the number after the \"-s\" commandline argument is invalid."); 397 | 398 | Speed = 100; 399 | } 400 | 401 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-s' accepted, speed = %u%%",Speed); 402 | 403 | continue; 404 | } 405 | 406 | if (wcscmp(argv[i],L"-f") == 0) 407 | { 408 | i++; 409 | 410 | if (i >= argc) 411 | { 412 | jkGui->ShowDebug(0,NULL,L"Error: you have not specified a number after the \"-f\" commandline argument."); 413 | 414 | continue; 415 | } 416 | 417 | FreeSpace = _wtof(argv[i]); 418 | 419 | if ((FreeSpace < 0) || (FreeSpace > 100)) 420 | { 421 | jkGui->ShowDebug(0,NULL,L"Error: the number after the \"-f\" commandline argument is invalid."); 422 | 423 | FreeSpace = 1; 424 | } 425 | 426 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-f' accepted, freespace = %0.1f%%",FreeSpace); 427 | 428 | continue; 429 | } 430 | 431 | if ((wcsncmp(argv[i],L"-f",2) == 0) && (wcslen(argv[i]) >= 3)) 432 | { 433 | FreeSpace = _wtof(&argv[i][2]); 434 | 435 | if ((FreeSpace < 0) || (FreeSpace > 100)) 436 | { 437 | jkGui->ShowDebug(0,NULL,L"Error: the number after the \"-f\" command line argument is invalid."); 438 | 439 | FreeSpace = 1; 440 | } 441 | 442 | jkGui->ShowDebug(0,NULL,L"Command line argument '-f' accepted, free space = %0.1f%%",FreeSpace); 443 | 444 | continue; 445 | } 446 | 447 | if (wcscmp(argv[i],L"-d") == 0) 448 | { 449 | i++; 450 | 451 | if (i >= argc) 452 | { 453 | jkGui->ShowDebug(0,NULL,L"Error: you have not specified a number after the \"-d\" commandline argument."); 454 | 455 | continue; 456 | } 457 | 458 | m_jkDefrag->Debug = _wtol(argv[i]); 459 | 460 | if ((m_jkDefrag->Debug < 0) || (m_jkDefrag->Debug > 6)) 461 | { 462 | jkGui->ShowDebug(0,NULL,L"Error: the number after the \"-d\" commandline argument is invalid."); 463 | 464 | m_jkDefrag->Debug = 1; 465 | } 466 | 467 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-d' accepted, debug = %u",m_jkDefrag->Debug); 468 | 469 | continue; 470 | } 471 | 472 | if ((wcsncmp(argv[i],L"-d",2) == 0) && (wcslen(argv[i]) == 3) && 473 | (argv[i][2] >= '0') && (argv[i][2] <= '6')) 474 | { 475 | m_jkDefrag->Debug = _wtol(&argv[i][2]); 476 | 477 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-d' accepted, debug = %u",m_jkDefrag->Debug); 478 | 479 | continue; 480 | } 481 | 482 | if (wcscmp(argv[i],L"-l") == 0) 483 | { 484 | i++; 485 | 486 | if (i >= argc) 487 | { 488 | jkGui->ShowDebug(0,NULL,L"Error: you have not specified a filename after the \"-l\" commandline argument."); 489 | 490 | continue; 491 | } 492 | 493 | WCHAR *LogFile = jkLog->GetLogFilename(); 494 | 495 | if (*LogFile != '\0') 496 | { 497 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-l' accepted, logfile = %s",LogFile); 498 | } 499 | else 500 | { 501 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-l' accepted, logfile turned off"); 502 | } 503 | 504 | continue; 505 | } 506 | 507 | if ((wcsncmp(argv[i],L"-l",2) == 0) && (wcslen(argv[i]) >= 3)) 508 | { 509 | WCHAR *LogFile = jkLog->GetLogFilename(); 510 | 511 | if (*LogFile != '\0') 512 | { 513 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-l' accepted, logfile = %s",LogFile); 514 | } 515 | else 516 | { 517 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-l' accepted, logfile turned off"); 518 | } 519 | 520 | continue; 521 | } 522 | 523 | if (wcscmp(argv[i],L"-e") == 0) 524 | { 525 | i++; 526 | 527 | if (i >= argc) 528 | { 529 | jkGui->ShowDebug(0,NULL,L"Error: you have not specified a mask after the \"-e\" commandline argument."); 530 | 531 | continue; 532 | } 533 | 534 | Excludes = jkLib->AddArrayString(Excludes,argv[i]); 535 | 536 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-e' accepted, added '%s' to the excludes",argv[i]); 537 | 538 | continue; 539 | } 540 | 541 | if ((wcsncmp(argv[i],L"-e",2) == 0) && (wcslen(argv[i]) >= 3)) 542 | { 543 | Excludes = jkLib->AddArrayString(Excludes,&argv[i][2]); 544 | 545 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-e' accepted, added '%s' to the excludes",&argv[i][2]); 546 | 547 | continue; 548 | } 549 | 550 | if (wcscmp(argv[i],L"-u") == 0) 551 | { 552 | i++; 553 | 554 | if (i >= argc) 555 | { 556 | jkGui->ShowDebug(0,NULL,L"Error: you have not specified a mask after the \"-u\" commandline argument."); 557 | 558 | continue; 559 | } 560 | 561 | SpaceHogs = jkLib->AddArrayString(SpaceHogs,argv[i]); 562 | 563 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-u' accepted, added '%s' to the spacehogs",argv[i]); 564 | 565 | continue; 566 | } 567 | 568 | if ((wcsncmp(argv[i],L"-u",2) == 0) && (wcslen(argv[i]) >= 3)) 569 | { 570 | SpaceHogs = jkLib->AddArrayString(SpaceHogs,&argv[i][2]); 571 | 572 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-u' accepted, added '%s' to the spacehogs",&argv[i][2]); 573 | 574 | continue; 575 | } 576 | 577 | if (wcscmp(argv[i],L"-q") == 0) 578 | { 579 | QuitOnFinish = YES; 580 | 581 | jkGui->ShowDebug(0,NULL,L"Commandline argument '-q' accepted, quitonfinish = yes"); 582 | 583 | continue; 584 | } 585 | 586 | if (argv[i][0] == '-') 587 | { 588 | jkGui->ShowDebug(0,NULL,L"Error: commandline argument not recognised: %s",argv[i]); 589 | } 590 | } 591 | } 592 | 593 | /* Defragment all the paths that are specified on the commandline one by one. */ 594 | DoAllVolumes = YES; 595 | 596 | if (argc > 1) 597 | { 598 | for (i = 1; i < argc; i++) 599 | { 600 | if (m_jkDefrag->IamRunning != RUNNING) break; 601 | 602 | if ((wcscmp(argv[i],L"-a") == 0) || 603 | (wcscmp(argv[i],L"-e") == 0) || 604 | (wcscmp(argv[i],L"-u") == 0) || 605 | (wcscmp(argv[i],L"-s") == 0) || 606 | (wcscmp(argv[i],L"-f") == 0) || 607 | (wcscmp(argv[i],L"-d") == 0) || 608 | (wcscmp(argv[i],L"-l") == 0)) 609 | { 610 | i++; 611 | continue; 612 | } 613 | 614 | if (*argv[i] == '-') continue; 615 | if (*argv[i] == '\0') continue; 616 | 617 | jkLib->RunJkDefrag(argv[i],OptimizeMode,Speed,FreeSpace,Excludes,SpaceHogs,&m_jkDefrag->Running, 618 | /*&JKDefragGui::getInstance()->RedrawScreen,*/NULL); 619 | 620 | DoAllVolumes = NO; 621 | } 622 | } 623 | 624 | /* If no paths are specified on the commandline then defrag all fixed harddisks. */ 625 | if ((DoAllVolumes == YES) && (m_jkDefrag->IamRunning == RUNNING)) 626 | { 627 | jkLib->RunJkDefrag(NULL,OptimizeMode,Speed,FreeSpace,Excludes,SpaceHogs,&m_jkDefrag->Running, 628 | /*&JKDefragGui::getInstance()->RedrawScreen,*/NULL); 629 | } 630 | 631 | /* If the "-q" command line argument was specified then exit the program. */ 632 | if (QuitOnFinish == YES) exit(EXIT_SUCCESS); 633 | 634 | /* End of this thread. */ 635 | return(0); 636 | } 637 | 638 | /* 639 | 640 | If the defragger is not yet running then return NO. If it's already 641 | running or if there was an error getting the processlist then return 642 | YES. 643 | 644 | */ 645 | int JKDefrag::AlreadyRunning(void) 646 | { 647 | HANDLE Snapshot; 648 | PROCESSENTRY32 pe32; 649 | DWORD MyPid; 650 | char MyName[MAX_PATH]; 651 | WCHAR s1[BUFSIZ]; 652 | WCHAR s2[BUFSIZ]; 653 | 654 | /* Get a process-snapshot from the kernel. */ 655 | Snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); 656 | 657 | if (Snapshot == INVALID_HANDLE_VALUE) 658 | { 659 | m_jkLib->SystemErrorStr(GetLastError(),s1,BUFSIZ); 660 | 661 | swprintf_s(s2,BUFSIZ,L"Cannot get process snapshot: %s",s1); 662 | 663 | m_jkGui->ShowDebug(0,NULL,s2); 664 | 665 | return(YES); 666 | } 667 | 668 | pe32.dwSize = sizeof(PROCESSENTRY32); 669 | 670 | /* Get my own executable name. */ 671 | MyPid = GetCurrentProcessId(); 672 | *MyName = '\0'; 673 | 674 | if (Process32First(Snapshot,&pe32) != FALSE) 675 | { 676 | do 677 | { 678 | if (MyPid == pe32.th32ProcessID) 679 | { 680 | strcpy_s(MyName,MAX_PATH,pe32.szExeFile); 681 | break; 682 | } 683 | } while (Process32Next(Snapshot,&pe32)); 684 | } 685 | 686 | if (*MyName == '\0') 687 | { 688 | /* "Cannot find my own name in the process list: %s" */ 689 | swprintf_s(s1,BUFSIZ,L"Cannot find my own name in the process list: %s",MyName); 690 | 691 | m_jkGui->ShowDebug(0,NULL,s1); 692 | 693 | return(YES); 694 | } 695 | 696 | /* Search for any other process with the same executable name as 697 | myself. If found then return YES. */ 698 | Process32First(Snapshot,&pe32); 699 | 700 | do 701 | { 702 | if (MyPid == pe32.th32ProcessID) continue; /* Ignore myself. */ 703 | 704 | if ((_stricmp(pe32.szExeFile,MyName) == 0) || 705 | (_stricmp(pe32.szExeFile,"jkdefrag.exe") == 0) || 706 | (_stricmp(pe32.szExeFile,"jkdefragscreensaver.exe") == 0) || 707 | (_stricmp(pe32.szExeFile,"jkdefragcmd.exe") == 0)) 708 | { 709 | CloseHandle(Snapshot); 710 | 711 | swprintf_s(s1,BUFSIZ,L"I am already running: %S",pe32.szExeFile); 712 | 713 | m_jkGui->ShowDebug(0,NULL,s1); 714 | 715 | return(YES); 716 | } 717 | } while (Process32Next(Snapshot,&pe32)); 718 | 719 | /* Return NO, not yet running. */ 720 | CloseHandle(Snapshot); 721 | return(NO); 722 | } 723 | 724 | 725 | int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 726 | { 727 | JKDefrag *jkDefrag = JKDefrag::getInstance(); 728 | 729 | WPARAM retValue = 0; 730 | 731 | if (jkDefrag != NULL) 732 | { 733 | retValue = jkDefrag->startProgram(hInstance,hPrevInstance,lpCmdLine,nCmdShow); 734 | 735 | JKDefrag::releaseInstance(); 736 | } 737 | 738 | return((int)retValue); 739 | } 740 | -------------------------------------------------------------------------------- /JkDefrag/Source/JkDefragGui.cpp: -------------------------------------------------------------------------------- 1 | #include "StdAfx.h" 2 | 3 | /* 4 | #include "JKDefragStruct.h" 5 | #include "JKDefragLog.h" 6 | #include "JkDefragLib.h" 7 | #include "JkDefragGui.h" 8 | */ 9 | 10 | JKDefragGui *JKDefragGui::m_jkDefragGui = 0; 11 | 12 | JKDefragGui::JKDefragGui() 13 | { 14 | m_jkLib = JKDefragLib::getInstance(); 15 | 16 | m_bmp = NULL; 17 | 18 | jkStruct = new JKDefragStruct(); 19 | 20 | m_squareSize = 12; 21 | // m_clusterSquares = NULL; 22 | m_numDiskSquares = 0; 23 | 24 | m_offsetX = 26; 25 | m_offsetY = 16; 26 | 27 | clusterInfo = NULL; 28 | m_numClusters = 1; 29 | 30 | ProgressStartTime = 0; 31 | ProgressTime = 0; 32 | ProgressDone = 0; 33 | 34 | int i = 0; 35 | 36 | for (i = 0; i < 6; i++) *Messages[i] = '\0'; 37 | 38 | // RedrawScreen = 0; 39 | } 40 | 41 | JKDefragGui::~JKDefragGui() 42 | { 43 | delete jkStruct; 44 | delete m_jkDefragGui; 45 | 46 | /* 47 | if (m_jkDefragGui->m_clusterSquares != NULL) 48 | { 49 | delete[] m_jkDefragGui->m_clusterSquares; 50 | } 51 | */ 52 | 53 | if (m_bmp != NULL) 54 | { 55 | delete m_bmp; 56 | } 57 | } 58 | 59 | JKDefragGui *JKDefragGui::getInstance() 60 | { 61 | if (m_jkDefragGui == NULL) 62 | { 63 | m_jkDefragGui = new JKDefragGui(); 64 | } 65 | 66 | return m_jkDefragGui; 67 | } 68 | 69 | int JKDefragGui::Initialize(HINSTANCE hInstance, int nCmdShow, JKDefragLog *jkLog, int debugLevel) 70 | { 71 | ULONG_PTR gdiplusToken; 72 | 73 | GdiplusStartupInput gdiplusStartupInput; 74 | 75 | GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); 76 | 77 | m_jkLog = jkLog; 78 | m_debugLevel = debugLevel; 79 | 80 | m_displayMutex = CreateMutex(NULL,FALSE,"JKDefrag"); 81 | 82 | m_wndClass.cbClsExtra = 0; 83 | m_wndClass.cbWndExtra = 0; 84 | m_wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); 85 | m_wndClass.hCursor = LoadCursor(NULL,IDC_ARROW); 86 | m_wndClass.hIcon = LoadIcon(NULL,MAKEINTRESOURCE(1)); 87 | m_wndClass.hInstance = hInstance; 88 | m_wndClass.lpfnWndProc = (WNDPROC)JKDefragGui::ProcessMessagefn; 89 | m_wndClass.lpszClassName = "MyClass"; 90 | m_wndClass.lpszMenuName = NULL; 91 | m_wndClass.style = CS_HREDRAW | CS_VREDRAW; 92 | m_wndClass.cbSize = sizeof(WNDCLASSEX); 93 | m_wndClass.hIconSm = LoadIcon(hInstance,MAKEINTRESOURCE(1)); 94 | 95 | CHAR koko[100]; 96 | 97 | LoadString(hInstance,2,koko, 99); 98 | 99 | if (RegisterClassEx(&m_wndClass) == 0) 100 | { 101 | MessageBoxW(NULL,L"Cannot register class",jkStruct->VERSIONTEXT,MB_ICONEXCLAMATION | MB_OK); 102 | return(0); 103 | } 104 | 105 | m_hWnd = CreateWindowW(L"MyClass",jkStruct->VERSIONTEXT,WS_TILEDWINDOW, 106 | CW_USEDEFAULT,0,1024,768,NULL,NULL,hInstance,NULL); 107 | 108 | if (m_hWnd == NULL) 109 | { 110 | MessageBoxW(NULL,L"Cannot create window",jkStruct->VERSIONTEXT,MB_ICONEXCLAMATION | MB_OK); 111 | return(0); 112 | } 113 | 114 | /* Show the window in the state that Windows has specified, minimized or maximized. */ 115 | ShowWindow(m_hWnd,nCmdShow); 116 | UpdateWindow(m_hWnd); 117 | 118 | SetTimer(m_hWnd,1,300,NULL); 119 | 120 | // InvalidateRect(m_hWnd,NULL,FALSE); 121 | 122 | return 1; 123 | } 124 | 125 | WPARAM JKDefragGui::DoModal() 126 | { 127 | int GetMessageResult; 128 | 129 | /* The main message thread. */ 130 | while (TRUE) 131 | { 132 | GetMessageResult = GetMessage(&Message,NULL,0,0); 133 | 134 | if (GetMessageResult == 0) break; 135 | if (GetMessageResult == -1) break; 136 | if (Message.message == WM_QUIT) break; 137 | 138 | TranslateMessage(&Message); 139 | DispatchMessage(&Message); 140 | } 141 | 142 | return Message.wParam;; 143 | } 144 | 145 | void JKDefragGui::setDisplayData(HDC hdc) 146 | { 147 | Graphics graphics(hdc); 148 | 149 | Rect clientWindowSize; 150 | 151 | Status status = graphics.GetVisibleClipBounds(&clientWindowSize); 152 | 153 | m_clientWindowSize = clientWindowSize; 154 | 155 | /* 156 | if (m_clusterSquares != NULL) 157 | { 158 | delete[] m_clusterSquares; 159 | } 160 | */ 161 | 162 | m_topHeight = 33; 163 | 164 | if (m_debugLevel > 1) 165 | { 166 | m_topHeight = 49; 167 | } 168 | 169 | m_diskAreaSize.Width = clientWindowSize.Width - m_offsetX * 2; 170 | m_diskAreaSize.Height = clientWindowSize.Height - m_topHeight - m_offsetY * 2; 171 | 172 | m_numDiskSquaresX = (int)(m_diskAreaSize.Width / m_squareSize); 173 | m_numDiskSquaresY = (int)(m_diskAreaSize.Height / m_squareSize); 174 | 175 | m_numDiskSquares = m_numDiskSquaresX * m_numDiskSquaresY; 176 | 177 | // m_clusterSquares = new clusterSquareStruct[m_numDiskSquares]; 178 | 179 | for (int ii = 0; ii < m_numDiskSquares; ii++) 180 | { 181 | m_clusterSquares[ii].color = 0; 182 | m_clusterSquares[ii].dirty = true; 183 | } 184 | 185 | m_realOffsetX = (int)((m_clientWindowSize.Width - m_numDiskSquaresX * m_squareSize) * 0.5); 186 | m_realOffsetY = (int)((m_clientWindowSize.Height - m_topHeight - m_numDiskSquaresY * m_squareSize) * 0.5); 187 | 188 | if (m_bmp != NULL) 189 | { 190 | delete m_bmp; 191 | 192 | m_bmp = NULL; 193 | } 194 | 195 | m_bmp = new Bitmap(m_clientWindowSize.Width, m_clientWindowSize.Height); 196 | 197 | // Color bottomPartColor; 198 | // bottomPartColor.SetFromCOLORREF(RGB(255,255,255)); 199 | 200 | // SolidBrush bottomPartBrush(bottomPartColor); 201 | 202 | // Rect drawArea(0, 0, m_clientWindowSize.Width, m_clientWindowSize.Height); 203 | // graphics.FillRectangle(&bottomPartBrush, drawArea); 204 | 205 | /* Ask defragger to completely redraw the screen. */ 206 | // RedrawScreen = 0; 207 | } 208 | 209 | /* Callback: clear the screen. */ 210 | void JKDefragGui::ClearScreen(WCHAR *Format, ...) 211 | { 212 | va_list VarArgs; 213 | 214 | int i; 215 | 216 | /* If there is no message then return. */ 217 | if (Format == NULL) return; 218 | 219 | /* Clear all the messages. */ 220 | for (i = 0; i < 6; i++) *Messages[i] = '\0'; 221 | 222 | /* Save the message in Messages 0. */ 223 | va_start(VarArgs,Format); 224 | vswprintf_s(Messages[0],50000,Format,VarArgs); 225 | 226 | /* If there is no logfile then return. */ 227 | if (m_jkLog != NULL) 228 | { 229 | m_jkLog->LogMessage(Format, VarArgs); 230 | } 231 | 232 | va_end(VarArgs); 233 | 234 | PaintImage(m_hDC); 235 | // InvalidateRect(m_hWnd,NULL,FALSE); 236 | } 237 | 238 | /* Callback: whenever an item (file, directory) is moved on disk. */ 239 | void JKDefragGui::ShowMove(struct ItemStruct *Item, 240 | ULONG64 Clusters, 241 | ULONG64 FromLcn, 242 | ULONG64 ToLcn, 243 | ULONG64 FromVcn) 244 | { 245 | /* Save the message in Messages 3. */ 246 | if (Clusters == 1) 247 | { 248 | swprintf_s(Messages[3],50000,L"Moving 1 cluster from %I64d to %I64d.",FromLcn,ToLcn); 249 | } 250 | else 251 | { 252 | swprintf_s(Messages[3],50000,L"Moving %I64d clusters from %I64d to %I64d.", 253 | Clusters,FromLcn,ToLcn); 254 | } 255 | 256 | /* Save the name of the file in Messages 4. */ 257 | if ((Item != NULL) && (Item->LongPath != NULL)) 258 | { 259 | swprintf_s(Messages[4],50000,L"%s",Item->LongPath); 260 | } 261 | else 262 | { 263 | *(Messages[4]) = '\0'; 264 | } 265 | 266 | /* If debug mode then write a message to the logfile. */ 267 | if (m_debugLevel < 3) return; 268 | 269 | if (FromVcn > 0) 270 | { 271 | if (Clusters == 1) 272 | { 273 | m_jkLog->LogMessage(L"%s\n Moving 1 cluster from %I64d to %I64d, VCN=%I64d.", 274 | Item->LongPath,FromLcn,ToLcn,FromVcn); 275 | } 276 | else 277 | { 278 | m_jkLog->LogMessage(L"%s\n Moving %I64d clusters from %I64d to %I64d, VCN=%I64d.", 279 | Item->LongPath,Clusters,FromLcn,ToLcn,FromVcn); 280 | } 281 | } 282 | else 283 | { 284 | if (Clusters == 1) 285 | { 286 | m_jkLog->LogMessage(L"%s\n Moving 1 cluster from %I64d to %I64d.", 287 | Item->LongPath,FromLcn,ToLcn); 288 | } 289 | else 290 | { 291 | m_jkLog->LogMessage(L"%s\n Moving %I64d clusters from %I64d to %I64d.", 292 | Item->LongPath,Clusters,FromLcn,ToLcn); 293 | } 294 | } 295 | PaintImage(m_hDC); 296 | 297 | // InvalidateRect(m_hWnd,NULL,FALSE); 298 | } 299 | 300 | 301 | /* Callback: for every file during analysis. 302 | This subroutine is called one last time with Item=NULL when analysis has 303 | finished. */ 304 | void JKDefragGui::ShowAnalyze(struct DefragDataStruct *Data, struct ItemStruct *Item) 305 | { 306 | if ((Data != NULL) && (Data->CountAllFiles != 0)) 307 | { 308 | swprintf_s(Messages[3],50000,L"Files %I64d, Directories %I64d, Clusters %I64d", 309 | Data->CountAllFiles,Data->CountDirectories,Data->CountAllClusters); 310 | } 311 | else 312 | { 313 | swprintf_s(Messages[3],50000,L"Applying Exclude and SpaceHogs masks...."); 314 | } 315 | 316 | /* Save the name of the file in Messages 4. */ 317 | if ((Item != NULL) && (Item->LongPath != NULL)) 318 | { 319 | swprintf_s(Messages[4],50000,L"%s",Item->LongPath); 320 | } 321 | else 322 | { 323 | *(Messages[4]) = '\0'; 324 | } 325 | PaintImage(m_hDC); 326 | 327 | // InvalidateRect(m_hWnd,NULL,FALSE); 328 | } 329 | 330 | /* Callback: show a debug message. */ 331 | 332 | void JKDefragGui::ShowDebug(int Level, struct ItemStruct *Item, WCHAR *Format, ...) 333 | { 334 | va_list VarArgs; 335 | 336 | if (m_debugLevel < Level) return; 337 | 338 | /* Save the name of the file in Messages 4. */ 339 | if ((Item != NULL) && (Item->LongPath != NULL)) 340 | { 341 | swprintf_s(Messages[4],50000,L"%s",Item->LongPath); 342 | } 343 | 344 | /* If there is no message then return. */ 345 | if (Format == NULL) return; 346 | 347 | /* Save the debug message in Messages 5. */ 348 | va_start(VarArgs,Format); 349 | vswprintf_s(Messages[5],50000,Format,VarArgs); 350 | m_jkLog->LogMessage(Format, VarArgs); 351 | va_end(VarArgs); 352 | PaintImage(m_hDC); 353 | 354 | // InvalidateRect(m_hWnd,NULL,FALSE); 355 | } 356 | 357 | /* Callback: paint a cluster on the screen in a color. */ 358 | void JKDefragGui::DrawCluster(struct DefragDataStruct *Data, 359 | ULONG64 ClusterStart, 360 | ULONG64 ClusterEnd, 361 | int Color) 362 | { 363 | struct __timeb64 Now; 364 | 365 | Rect windowSize = m_clientWindowSize; 366 | 367 | /* Save the PhaseTodo and PhaseDone counters for later use by the progress counter. */ 368 | if (Data->PhaseTodo != 0) 369 | { 370 | _ftime64_s(&Now); 371 | 372 | ProgressTime = Now.time * 1000 + Now.millitm; 373 | ProgressDone = Data->PhaseDone; 374 | ProgressTodo = Data->PhaseTodo; 375 | } 376 | 377 | /* Sanity check. */ 378 | if (Data->TotalClusters == 0) return; 379 | if (m_hDC == NULL) return; 380 | if (ClusterStart == ClusterEnd) return; 381 | // if (ClusterStart > Data->TotalClusters) ClusterStart = 0; 382 | 383 | WaitForSingleObject(m_displayMutex,100); 384 | 385 | m_displayMutex = CreateMutex(NULL,FALSE,"JKDefrag"); 386 | 387 | if (m_numClusters != Data->TotalClusters || clusterInfo == NULL) 388 | { 389 | if (clusterInfo != NULL) 390 | { 391 | free(clusterInfo); 392 | 393 | clusterInfo = NULL; 394 | } 395 | 396 | m_numClusters = Data->TotalClusters; 397 | 398 | clusterInfo = (byte *)malloc((size_t)m_numClusters); 399 | 400 | for(int ii = 0; ii <= m_numClusters; ii++) 401 | { 402 | clusterInfo[ii] = JKDefragStruct::COLOREMPTY; 403 | } 404 | 405 | // RedrawScreen = 0; 406 | 407 | return; 408 | } 409 | 410 | 411 | for(ULONG64 ii = ClusterStart; ii <= ClusterEnd; ii++) 412 | { 413 | clusterInfo[ii] = Color; 414 | } 415 | 416 | float clusterPerSquare = (float)(m_numClusters / m_numDiskSquares); 417 | int clusterStartSquareNum = (int)((ULONG64)ClusterStart / (ULONG64)clusterPerSquare); 418 | int clusterEndSquareNum = (int)((ULONG64)ClusterEnd / (ULONG64)clusterPerSquare); 419 | 420 | FillSquares(clusterStartSquareNum, clusterEndSquareNum); 421 | 422 | ReleaseMutex(m_displayMutex); 423 | PaintImage(m_hDC); 424 | 425 | // InvalidateRect(m_hWnd,NULL,FALSE); 426 | } 427 | 428 | /* Callback: just before the defragger starts a new Phase, and when it finishes. */ 429 | void JKDefragGui::ShowStatus(struct DefragDataStruct *Data) 430 | { 431 | struct ItemStruct *Item; 432 | 433 | int Fragments; 434 | 435 | ULONG64 TotalFragments; 436 | ULONG64 TotalBytes; 437 | ULONG64 TotalClusters; 438 | 439 | struct ItemStruct *LargestItems[25]; 440 | 441 | int LastLargest; 442 | 443 | struct __timeb64 Now; 444 | 445 | int i; 446 | int j; 447 | 448 | /* Reset the progress counter. */ 449 | _ftime64_s(&Now); 450 | 451 | ProgressStartTime = Now.time * 1000 + Now.millitm; 452 | ProgressTime = ProgressStartTime; 453 | ProgressDone = 0; 454 | ProgressTodo = 0; 455 | 456 | /* Reset all the messages. */ 457 | for (i = 0; i < 6; i++) *(Messages[i]) = '\0'; 458 | 459 | /* Update Message 0 and 1. */ 460 | if (Data != NULL) 461 | { 462 | swprintf_s(Messages[0],50000,L"%s",Data->Disk.MountPoint); 463 | 464 | switch(Data->Phase) 465 | { 466 | case 1: wcscpy_s(Messages[1],50000,L"Phase 1: Analyze"); break; 467 | case 2: wcscpy_s(Messages[1],50000,L"Phase 2: Defragment"); break; 468 | case 3: wcscpy_s(Messages[1],50000,L"Phase 3: ForcedFill"); break; 469 | case 4: swprintf_s(Messages[1],50000,L"Zone %u: Sort",Data->Zone + 1); break; 470 | case 5: swprintf_s(Messages[1],50000,L"Zone %u: Fast Optimize",Data->Zone + 1); break; 471 | case 6: wcscpy_s(Messages[1],50000,L"Phase 3: Move Up"); break; 472 | case 7: 473 | wcscpy_s(Messages[1],50000,L"Finished."); 474 | swprintf_s(Messages[4],50000,L"Logfile: %s",m_jkLog->GetLogFilename()); 475 | break; 476 | case 8: wcscpy_s(Messages[1],50000,L"Phase 3: Fixup"); break; 477 | } 478 | 479 | m_jkLog->LogMessage(Messages[1]); 480 | } 481 | 482 | /* Write some statistics to the logfile. */ 483 | if ((Data != NULL) && (Data->Phase == 7)) 484 | { 485 | m_jkLog->LogMessage(L"- Total disk space: %I64d bytes (%.04f gigabytes), %I64d clusters", 486 | Data->BytesPerCluster * Data->TotalClusters, 487 | (double)(Data->BytesPerCluster * Data->TotalClusters) / (1024 * 1024 * 1024), 488 | Data->TotalClusters); 489 | 490 | m_jkLog->LogMessage(L"- Bytes per cluster: %I64d bytes",Data->BytesPerCluster); 491 | 492 | m_jkLog->LogMessage(L"- Number of files: %I64d",Data->CountAllFiles); 493 | m_jkLog->LogMessage(L"- Number of directories: %I64d",Data->CountDirectories); 494 | m_jkLog->LogMessage(L"- Total size of analyzed items: %I64d bytes (%.04f gigabytes), %I64d clusters", 495 | Data->CountAllClusters * Data->BytesPerCluster, 496 | (double)(Data->CountAllClusters * Data->BytesPerCluster) / (1024 * 1024 * 1024), 497 | Data->CountAllClusters); 498 | 499 | if (Data->CountAllFiles + Data->CountDirectories > 0) 500 | { 501 | m_jkLog->LogMessage(L"- Number of fragmented items: %I64d (%.04f%% of all items)", 502 | Data->CountFragmentedItems, 503 | (double)(Data->CountFragmentedItems * 100) / (Data->CountAllFiles + Data->CountDirectories)); 504 | } 505 | else 506 | { 507 | m_jkLog->LogMessage(L"- Number of fragmented items: %I64d",Data->CountFragmentedItems); 508 | } 509 | 510 | if ((Data->CountAllClusters > 0) && (Data->TotalClusters > 0)) 511 | { 512 | m_jkLog->LogMessage(L"- Total size of fragmented items: %I64d bytes, %I64d clusters, %.04f%% of all items, %.04f%% of disk", 513 | Data->CountFragmentedClusters * Data->BytesPerCluster, 514 | Data->CountFragmentedClusters, 515 | (double)(Data->CountFragmentedClusters * 100) / Data->CountAllClusters, 516 | (double)(Data->CountFragmentedClusters * 100) / Data->TotalClusters); 517 | } 518 | else 519 | { 520 | m_jkLog->LogMessage(L"- Total size of fragmented items: %I64d bytes, %I64d clusters", 521 | Data->CountFragmentedClusters * Data->BytesPerCluster, 522 | Data->CountFragmentedClusters); 523 | } 524 | 525 | if (Data->TotalClusters > 0) 526 | { 527 | m_jkLog->LogMessage(L"- Free disk space: %I64d bytes, %I64d clusters, %.04f%% of disk", 528 | Data->CountFreeClusters * Data->BytesPerCluster, 529 | Data->CountFreeClusters, 530 | (double)(Data->CountFreeClusters * 100) / Data->TotalClusters); 531 | } 532 | else 533 | { 534 | m_jkLog->LogMessage(L"- Free disk space: %I64d bytes, %I64d clusters", 535 | Data->CountFreeClusters * Data->BytesPerCluster, 536 | Data->CountFreeClusters); 537 | } 538 | 539 | m_jkLog->LogMessage(L"- Number of gaps: %I64d",Data->CountGaps); 540 | 541 | if (Data->CountGaps > 0) 542 | { 543 | m_jkLog->LogMessage(L"- Number of small gaps: %I64d (%.04f%% of all gaps)", 544 | Data->CountGapsLess16, 545 | (double)(Data->CountGapsLess16 * 100) / Data->CountGaps); 546 | } 547 | else 548 | { 549 | m_jkLog->LogMessage(L"- Number of small gaps: %I64d", 550 | Data->CountGapsLess16); 551 | } 552 | 553 | if (Data->CountFreeClusters > 0) 554 | { 555 | m_jkLog->LogMessage(L"- Size of small gaps: %I64d bytes, %I64d clusters, %.04f%% of free disk space", 556 | Data->CountClustersLess16 * Data->BytesPerCluster, 557 | Data->CountClustersLess16, 558 | (double)(Data->CountClustersLess16 * 100) / Data->CountFreeClusters); 559 | } 560 | else 561 | { 562 | m_jkLog->LogMessage(L"- Size of small gaps: %I64d bytes, %I64d clusters", 563 | Data->CountClustersLess16 * Data->BytesPerCluster, 564 | Data->CountClustersLess16); 565 | } 566 | 567 | if (Data->CountGaps > 0) 568 | { 569 | m_jkLog->LogMessage(L"- Number of big gaps: %I64d (%.04f%% of all gaps)", 570 | Data->CountGaps - Data->CountGapsLess16, 571 | (double)((Data->CountGaps - Data->CountGapsLess16) * 100) / Data->CountGaps); 572 | } 573 | else 574 | { 575 | m_jkLog->LogMessage(L"- Number of big gaps: %I64d", 576 | Data->CountGaps - Data->CountGapsLess16); 577 | } 578 | 579 | if (Data->CountFreeClusters > 0) 580 | { 581 | m_jkLog->LogMessage(L"- Size of big gaps: %I64d bytes, %I64d clusters, %.04f%% of free disk space", 582 | (Data->CountFreeClusters - Data->CountClustersLess16) * Data->BytesPerCluster, 583 | Data->CountFreeClusters - Data->CountClustersLess16, 584 | (double)((Data->CountFreeClusters - Data->CountClustersLess16) * 100) / Data->CountFreeClusters); 585 | } 586 | else 587 | { 588 | m_jkLog->LogMessage(L"- Size of big gaps: %I64d bytes, %I64d clusters", 589 | (Data->CountFreeClusters - Data->CountClustersLess16) * Data->BytesPerCluster, 590 | Data->CountFreeClusters - Data->CountClustersLess16); 591 | } 592 | 593 | if (Data->CountGaps > 0) 594 | { 595 | m_jkLog->LogMessage(L"- Average gap size: %.04f clusters", 596 | (double)(Data->CountFreeClusters) / Data->CountGaps); 597 | } 598 | 599 | if (Data->CountFreeClusters > 0) 600 | { 601 | m_jkLog->LogMessage(L"- Biggest gap: %I64d bytes, %I64d clusters, %.04f%% of free disk space", 602 | Data->BiggestGap * Data->BytesPerCluster, 603 | Data->BiggestGap, 604 | (double)(Data->BiggestGap * 100) / Data->CountFreeClusters); 605 | } 606 | else 607 | { 608 | m_jkLog->LogMessage(L"- Biggest gap: %I64d bytes, %I64d clusters", 609 | Data->BiggestGap * Data->BytesPerCluster, 610 | Data->BiggestGap); 611 | } 612 | 613 | if (Data->TotalClusters > 0) 614 | { 615 | m_jkLog->LogMessage(L"- Average end-begin distance: %.0f clusters, %.4f%% of volume size", 616 | Data->AverageDistance,100.0 * Data->AverageDistance / Data->TotalClusters); 617 | } 618 | else 619 | { 620 | m_jkLog->LogMessage(L"- Average end-begin distance: %.0f clusters",Data->AverageDistance); 621 | } 622 | 623 | for (Item = m_jkLib->TreeSmallest(Data->ItemTree); Item != NULL; Item = m_jkLib->TreeNext(Item)) 624 | { 625 | if (Item->Unmovable != YES) continue; 626 | if (Item->Exclude == YES) continue; 627 | if ((Item->Directory == YES) && (Data->CannotMoveDirs > 20)) continue; 628 | break; 629 | } 630 | 631 | if (Item != NULL) 632 | { 633 | m_jkLog->LogMessage(L"These items could not be moved:"); 634 | m_jkLog->LogMessage(L" Fragments Bytes Clusters Name"); 635 | 636 | TotalFragments = 0; 637 | TotalBytes = 0; 638 | TotalClusters = 0; 639 | 640 | for (Item = m_jkLib->TreeSmallest(Data->ItemTree); Item != NULL; Item = m_jkLib->TreeNext(Item)) 641 | { 642 | if (Item->Unmovable != YES) continue; 643 | if (Item->Exclude == YES) continue; 644 | if ((Item->Directory == YES) && (Data->CannotMoveDirs > 20)) continue; 645 | if ((Item->LongFilename != NULL) && 646 | ((_wcsicmp(Item->LongFilename,L"$BadClus") == 0) || 647 | (_wcsicmp(Item->LongFilename,L"$BadClus:$Bad:$DATA") == 0))) continue; 648 | 649 | Fragments = m_jkLib->FragmentCount(Item); 650 | 651 | if (Item->LongPath == NULL) 652 | { 653 | m_jkLog->LogMessage(L" %9lu %11I64u %9I64u [at cluster %I64u]",Fragments,Item->Bytes,Item->Clusters, 654 | m_jkLib->GetItemLcn(Item)); 655 | } 656 | else 657 | { 658 | m_jkLog->LogMessage(L" %9lu %11I64u %9I64u %s",Fragments,Item->Bytes,Item->Clusters,Item->LongPath); 659 | } 660 | 661 | TotalFragments = TotalFragments + Fragments; 662 | TotalBytes = TotalBytes + Item->Bytes; 663 | TotalClusters = TotalClusters + Item->Clusters; 664 | } 665 | 666 | m_jkLog->LogMessage(L" --------- ----------- --------- -----"); 667 | m_jkLog->LogMessage(L" %9I64u %11I64u %9I64u Total",TotalFragments,TotalBytes,TotalClusters); 668 | } 669 | 670 | for (Item = m_jkLib->TreeSmallest(Data->ItemTree); Item != NULL; Item = m_jkLib->TreeNext(Item)) 671 | { 672 | if (Item->Exclude == YES) continue; 673 | if ((Item->Directory == YES) && (Data->CannotMoveDirs > 20)) continue; 674 | 675 | Fragments = m_jkLib->FragmentCount(Item); 676 | 677 | if (Fragments <= 1) continue; 678 | 679 | break; 680 | } 681 | 682 | if (Item != NULL) 683 | { 684 | m_jkLog->LogMessage(L"These items are still fragmented:"); 685 | m_jkLog->LogMessage(L" Fragments Bytes Clusters Name"); 686 | 687 | TotalFragments = 0; 688 | TotalBytes = 0; 689 | TotalClusters = 0; 690 | 691 | for (Item = m_jkLib->TreeSmallest(Data->ItemTree); Item != NULL; Item = m_jkLib->TreeNext(Item)) 692 | { 693 | if (Item->Exclude == YES) continue; 694 | if ((Item->Directory == YES) && (Data->CannotMoveDirs > 20)) continue; 695 | 696 | Fragments = m_jkLib->FragmentCount(Item); 697 | 698 | if (Fragments <= 1) continue; 699 | 700 | if (Item->LongPath == NULL) 701 | { 702 | m_jkLog->LogMessage(L" %9lu %11I64u %9I64u [at cluster %I64u]",Fragments,Item->Bytes,Item->Clusters, 703 | m_jkLib->GetItemLcn(Item)); 704 | } 705 | else 706 | { 707 | m_jkLog->LogMessage(L" %9lu %11I64u %9I64u %s",Fragments,Item->Bytes,Item->Clusters,Item->LongPath); 708 | } 709 | 710 | TotalFragments = TotalFragments + Fragments; 711 | TotalBytes = TotalBytes + Item->Bytes; 712 | TotalClusters = TotalClusters + Item->Clusters; 713 | } 714 | 715 | m_jkLog->LogMessage(L" --------- ----------- --------- -----"); 716 | m_jkLog->LogMessage(L" %9I64u %11I64u %9I64u Total",TotalFragments,TotalBytes,TotalClusters); 717 | } 718 | 719 | LastLargest = 0; 720 | 721 | for (Item = m_jkLib->TreeSmallest(Data->ItemTree); Item != NULL; Item = m_jkLib->TreeNext(Item)) 722 | { 723 | if ((Item->LongFilename != NULL) && 724 | ((_wcsicmp(Item->LongFilename,L"$BadClus") == 0) || 725 | (_wcsicmp(Item->LongFilename,L"$BadClus:$Bad:$DATA") == 0))) 726 | { 727 | continue; 728 | } 729 | 730 | for (i = LastLargest - 1; i >= 0; i--) 731 | { 732 | if (Item->Clusters < LargestItems[i]->Clusters) break; 733 | 734 | if ((Item->Clusters == LargestItems[i]->Clusters) && 735 | (Item->Bytes < LargestItems[i]->Bytes)) break; 736 | 737 | if ((Item->Clusters == LargestItems[i]->Clusters) && 738 | (Item->Bytes == LargestItems[i]->Bytes) && 739 | (Item->LongPath != NULL) && 740 | (LargestItems[i]->LongPath != NULL) && 741 | (_wcsicmp(Item->LongPath,LargestItems[i]->LongPath) > 0)) break; 742 | } 743 | 744 | if (i < 24) 745 | { 746 | if (LastLargest < 25) LastLargest++; 747 | 748 | for (j = LastLargest - 1; j > i + 1; j--) 749 | { 750 | LargestItems[j] = LargestItems[j-1]; 751 | } 752 | 753 | LargestItems[i + 1] = Item; 754 | } 755 | } 756 | 757 | if (LastLargest > 0) 758 | { 759 | m_jkLog->LogMessage(L"The 25 largest items on disk:"); 760 | m_jkLog->LogMessage(L" Fragments Bytes Clusters Name"); 761 | 762 | for (i = 0; i < LastLargest; i++) 763 | { 764 | if (LargestItems[i]->LongPath == NULL) 765 | { 766 | m_jkLog->LogMessage(L" %9u %11I64u %9I64u [at cluster %I64u]",m_jkLib->FragmentCount(LargestItems[i]), 767 | LargestItems[i]->Bytes,LargestItems[i]->Clusters,m_jkLib->GetItemLcn(LargestItems[i])); 768 | } 769 | else 770 | { 771 | m_jkLog->LogMessage(L" %9u %11I64u %9I64u %s",m_jkLib->FragmentCount(LargestItems[i]), 772 | LargestItems[i]->Bytes,LargestItems[i]->Clusters,LargestItems[i]->LongPath); 773 | } 774 | } 775 | } 776 | } 777 | } 778 | 779 | 780 | void JKDefragGui::PaintImage(HDC hdc) 781 | { 782 | Graphics *graphics = Graphics::FromImage(m_bmp); 783 | 784 | double Done; 785 | 786 | Rect windowSize = m_clientWindowSize; 787 | Rect drawArea; 788 | 789 | float squareSizeUnit = (float)(1 / (float)m_squareSize); 790 | 791 | /* Reset the display idle timer (screen saver) and system idle timer (power saver). */ 792 | SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); 793 | 794 | if (ProgressTodo > 0) 795 | { 796 | Done = (double)((double)ProgressDone / (double)ProgressTodo); 797 | 798 | if (Done > 1) Done = 1; 799 | 800 | swprintf_s(Messages[2],50000,L"%.4f%%",100 * Done); 801 | } 802 | 803 | Color backColor1; 804 | backColor1.SetFromCOLORREF(RGB(0, 0, 255)); 805 | 806 | Color backColor2; 807 | backColor2.SetFromCOLORREF(RGB(255, 0, 0)); 808 | 809 | LinearGradientBrush bgBrush(windowSize,Color::DarkBlue,Color::LightBlue,LinearGradientModeForwardDiagonal); 810 | 811 | drawArea = windowSize; 812 | 813 | drawArea.Height = m_topHeight + 1; 814 | 815 | Color busyColor; 816 | busyColor.SetFromCOLORREF(Colors[JKDefragStruct::COLORBUSY]); 817 | 818 | SolidBrush busyBrush(busyColor); 819 | 820 | /* 821 | graphics->FillRectangle(&busyBrush, drawArea); 822 | */ 823 | graphics->FillRectangle(&bgBrush, drawArea); 824 | 825 | SolidBrush brush(Color::White); 826 | 827 | FontFamily fontFamily(L"Tahoma"); 828 | Font font(&fontFamily,12,FontStyleRegular, UnitPixel); 829 | WCHAR *text; 830 | PointF pointF(2.0f, 0.0f); 831 | 832 | text = Messages[0]; 833 | graphics->DrawString(text, -1, &font, pointF, &brush); 834 | 835 | pointF = PointF(40.0f, 0.0f); 836 | text = Messages[1]; 837 | graphics->DrawString(text, -1, &font, pointF, &brush); 838 | 839 | pointF = PointF(200.0f, 0.0f); 840 | text = Messages[2]; 841 | graphics->DrawString(text, -1, &font, pointF, &brush); 842 | 843 | pointF = PointF(280.0f, 0.0f); 844 | text = Messages[3]; 845 | graphics->DrawString(text, -1, &font, pointF, &brush); 846 | 847 | pointF = PointF(2.0f, 17.0f); 848 | text = Messages[4]; 849 | graphics->DrawString(text, -1, &font, pointF, &brush); 850 | 851 | if (m_debugLevel > 1) 852 | { 853 | pointF = PointF(2.0f, 33.0f); 854 | text = Messages[5]; 855 | graphics->DrawString(text, -1, &font, pointF, &brush); 856 | } 857 | 858 | 859 | int xx1 = m_realOffsetX - 1; 860 | int yy1 = m_realOffsetY + m_topHeight - 1; 861 | 862 | int xx2 = xx1 + m_numDiskSquaresX * m_squareSize + 1; 863 | int yy2 = yy1 + m_numDiskSquaresY * m_squareSize + 1; 864 | 865 | /* 866 | Color bottomPartColor; 867 | bottomPartColor.SetFromCOLORREF(Colors[JKDefragStruct::COLORBUSY]); 868 | 869 | SolidBrush bottomPartBrush(bottomPartColor); 870 | */ 871 | 872 | drawArea = Rect(0, m_topHeight + 1, m_clientWindowSize.Width, yy1 - m_topHeight - 2); 873 | /* 874 | graphics->FillRectangle(&bottomPartBrush, drawArea); 875 | */ 876 | graphics->FillRectangle(&bgBrush, drawArea); 877 | 878 | drawArea = Rect(0, yy2 + 2, m_clientWindowSize.Width, m_clientWindowSize.Height - yy2 - 2); 879 | /* 880 | graphics->FillRectangle(&bottomPartBrush, drawArea); 881 | */ 882 | graphics->FillRectangle(&bgBrush, drawArea); 883 | 884 | drawArea = Rect(0, yy1 - 1, xx1 - 1, yy2 - yy1 + 3); 885 | /* 886 | graphics->FillRectangle(&bottomPartBrush, drawArea); 887 | */ 888 | graphics->FillRectangle(&bgBrush, drawArea); 889 | 890 | drawArea = Rect(xx2, yy1 - 1, m_clientWindowSize.Width - xx2, yy2 - yy1 + 3); 891 | /* 892 | graphics->FillRectangle(&bottomPartBrush, drawArea); 893 | */ 894 | graphics->FillRectangle(&bgBrush, drawArea); 895 | 896 | Pen pen1(Color(0,0,0)); 897 | Pen pen2(Color(255,255,255)); 898 | 899 | graphics->DrawLine(&pen1, xx1, yy2, xx1, yy1); 900 | graphics->DrawLine(&pen1, xx1, yy1, xx2, yy1); 901 | graphics->DrawLine(&pen1, xx2, yy1, xx2, yy2); 902 | graphics->DrawLine(&pen1, xx2, yy2, xx1, yy2); 903 | 904 | graphics->DrawLine(&pen2, xx1 - 1, yy2 + 1, xx1 - 1, yy1 - 1); 905 | graphics->DrawLine(&pen2, xx1 - 1, yy1 - 1, xx2 + 1, yy1 - 1); 906 | graphics->DrawLine(&pen2, xx2 + 1, yy1 - 1, xx2 + 1, yy2 + 1); 907 | graphics->DrawLine(&pen2, xx2 + 1, yy2 + 1, xx1 - 1, yy2 + 1); 908 | 909 | COLORREF colEmpty = Colors[JKDefragStruct::COLOREMPTY]; 910 | Color colorEmpty; 911 | colorEmpty.SetFromCOLORREF(colEmpty); 912 | 913 | Pen pen(Color(210,210,210)); 914 | Pen penEmpty(colorEmpty); 915 | 916 | for (int jj = 0; jj < m_numDiskSquares; jj++) 917 | { 918 | if (m_clusterSquares[jj].dirty == false) 919 | { 920 | continue; 921 | } 922 | 923 | m_clusterSquares[jj].dirty = false; 924 | 925 | int x1 = jj % m_numDiskSquaresX; 926 | int y1 = jj / m_numDiskSquaresX; 927 | 928 | int xx1 = m_realOffsetX + x1 * m_squareSize; 929 | int yy1 = m_realOffsetY + y1 * m_squareSize + m_topHeight; 930 | 931 | byte clusterEmpty = (m_clusterSquares[jj].color & (1 << 7)) >> 7; 932 | byte clusterAllocated = (m_clusterSquares[jj].color & (1 << 6)) >> 6; 933 | byte clusterUnfragmented = (m_clusterSquares[jj].color & (1 << 5)) >> 5; 934 | byte clusterUnmovable = (m_clusterSquares[jj].color & (1 << 4)) >> 4; 935 | byte clusterFragmented = (m_clusterSquares[jj].color & (1 << 3)) >> 3; 936 | byte clusterBusy = (m_clusterSquares[jj].color & (1 << 2)) >> 2; 937 | byte clusterMft = (m_clusterSquares[jj].color & (1 << 1)) >> 1; 938 | byte clusterSpacehog = (m_clusterSquares[jj].color & 1); 939 | 940 | COLORREF col = Colors[JKDefragStruct::COLOREMPTY]; 941 | 942 | int emptyCluster = true; 943 | 944 | if (clusterBusy == 1) 945 | { 946 | col = Colors[JKDefragStruct::COLORBUSY]; 947 | emptyCluster = false; 948 | } 949 | else if (clusterUnmovable == 1) 950 | { 951 | col = Colors[JKDefragStruct::COLORUNMOVABLE]; 952 | emptyCluster = false; 953 | } 954 | else if (clusterFragmented == 1) 955 | { 956 | col = Colors[JKDefragStruct::COLORFRAGMENTED]; 957 | emptyCluster = false; 958 | } 959 | else if (clusterMft == 1) 960 | { 961 | col = Colors[JKDefragStruct::COLORMFT]; 962 | emptyCluster = false; 963 | } 964 | else if (clusterUnfragmented == 1) 965 | { 966 | col = Colors[JKDefragStruct::COLORUNFRAGMENTED]; 967 | emptyCluster = false; 968 | } 969 | else if (clusterSpacehog == 1) 970 | { 971 | col = Colors[JKDefragStruct::COLORSPACEHOG]; 972 | emptyCluster = false; 973 | } 974 | 975 | Color C1; 976 | Color C2; 977 | 978 | C1.SetFromCOLORREF(col); 979 | 980 | int RR = GetRValue(col) + 200; 981 | RR = (RR > 255) ? 255 : RR; 982 | 983 | int GG = GetGValue(col) + 200; 984 | GG = (GG > 255) ? 255 : GG; 985 | 986 | int BB = GetBValue(col) + 100; 987 | BB = (BB > 255) ? 255 : BB; 988 | 989 | C2.SetFromCOLORREF(RGB((byte)RR, (byte)GG, (byte)BB)); 990 | 991 | if (emptyCluster) 992 | { 993 | Rect drawArea2(xx1, yy1, m_squareSize - 0, m_squareSize - 0); 994 | 995 | LinearGradientBrush BB2(drawArea2,C1,C2,LinearGradientModeVertical); 996 | graphics->FillRectangle(&BB2, drawArea2); 997 | 998 | int lineX1 = drawArea2.X; 999 | int lineY1 = drawArea2.Y; 1000 | int lineX2 = drawArea2.X + m_squareSize - 1; 1001 | int lineY2 = drawArea2.Y; 1002 | int lineX3 = drawArea2.X; 1003 | int lineY3 = drawArea2.Y + m_squareSize - 1; 1004 | int lineX4 = drawArea2.X + m_squareSize - 1; 1005 | int lineY4 = drawArea2.Y + m_squareSize - 1; 1006 | 1007 | graphics->DrawLine(&penEmpty, lineX1, lineY1, lineX2, lineY2); 1008 | graphics->DrawLine(&pen, lineX3, lineY3, lineX4, lineY4); 1009 | } 1010 | else 1011 | { 1012 | Rect drawArea2(xx1, yy1, m_squareSize - 0, m_squareSize - 0); 1013 | 1014 | LinearGradientBrush BB1(drawArea2,C2,C1,LinearGradientModeForwardDiagonal); 1015 | 1016 | graphics->FillRectangle(&BB1, drawArea2); 1017 | 1018 | int lineX1 = drawArea2.X; 1019 | int lineY1 = drawArea2.Y + m_squareSize - 1; 1020 | int lineX2 = drawArea2.X + m_squareSize - 1; 1021 | int lineY2 = drawArea2.Y; 1022 | int lineX3 = drawArea2.X + m_squareSize - 1; 1023 | int lineY3 = drawArea2.Y + m_squareSize - 1; 1024 | 1025 | graphics->DrawLine(&pen, lineX1, lineY1, lineX3, lineY3); 1026 | graphics->DrawLine(&pen, lineX2, lineY2, lineX3, lineY3); 1027 | } 1028 | 1029 | } 1030 | 1031 | delete graphics; 1032 | 1033 | } 1034 | 1035 | void JKDefragGui::OnPaint(HDC hdc) 1036 | { 1037 | /* 1038 | Bitmap bmp(m_clientWindowSize.Width, m_clientWindowSize.Height); 1039 | 1040 | Graphics *graphics2 = Graphics::FromImage(&bmp); 1041 | */ 1042 | 1043 | Graphics graphics(hdc); 1044 | 1045 | /* 1046 | graphics2->DrawImage(m_bmp,0,0); 1047 | */ 1048 | 1049 | /* 1050 | Color busyColor(128,128,128,128); 1051 | 1052 | SolidBrush busyBrush(busyColor); 1053 | 1054 | Rect rr = Rect(100, 100, 400, 100); 1055 | 1056 | graphics2->FillRectangle(&busyBrush, rr); 1057 | 1058 | SolidBrush brush(Color::White); 1059 | 1060 | FontFamily fontFamily(L"Tahoma"); 1061 | Font font(&fontFamily,12,FontStyleRegular, UnitPixel); 1062 | PointF pointF(132.0f, 120.0f); 1063 | 1064 | WCHAR *text; 1065 | 1066 | text = Messages[2]; 1067 | 1068 | graphics2->DrawString(text, -1, &font, pointF, &brush); 1069 | */ 1070 | 1071 | graphics.DrawImage(m_bmp,0,0); 1072 | 1073 | /* 1074 | delete graphics2; 1075 | */ 1076 | 1077 | return; 1078 | } 1079 | 1080 | /* Message handler. */ 1081 | LRESULT CALLBACK JKDefragGui::ProcessMessagefn(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) 1082 | { 1083 | switch(Message) 1084 | { 1085 | case WM_DESTROY: 1086 | PostQuitMessage(0); 1087 | return 0; 1088 | 1089 | case WM_TIMER: 1090 | 1091 | /* 1092 | if (wParam == 333) 1093 | { 1094 | PAINTSTRUCT ps; 1095 | 1096 | WaitForSingleObject(m_jkDefragGui->m_displayMutex,100); 1097 | 1098 | m_jkDefragGui->m_displayMutex = CreateMutex(NULL,FALSE,"JKDefrag"); 1099 | 1100 | m_jkDefragGui->m_hDC = BeginPaint(hWnd, &ps); 1101 | 1102 | m_jkDefragGui->setDisplayData(m_jkDefragGui->m_hDC); 1103 | 1104 | m_jkDefragGui->FillSquares( 0, m_jkDefragGui->m_numDiskSquares); 1105 | 1106 | m_jkDefragGui->PaintImage(m_jkDefragGui->m_hDC); 1107 | 1108 | EndPaint(hWnd, &ps); 1109 | 1110 | ReleaseMutex(m_jkDefragGui->m_displayMutex); 1111 | 1112 | KillTimer(m_jkDefragGui->m_hWnd, m_jkDefragGui->m_sizeTimer); 1113 | } 1114 | */ 1115 | 1116 | 1117 | InvalidateRect(hWnd,NULL,FALSE); 1118 | 1119 | return 0; 1120 | 1121 | case WM_PAINT: 1122 | { 1123 | /* Grab the display mutex, to make sure that we are the only thread changing the window. */ 1124 | WaitForSingleObject(m_jkDefragGui->m_displayMutex,100); 1125 | 1126 | m_jkDefragGui->m_displayMutex = CreateMutex(NULL,FALSE,"JKDefrag"); 1127 | 1128 | PAINTSTRUCT ps; 1129 | 1130 | m_jkDefragGui->m_hDC = BeginPaint(hWnd, &ps); 1131 | 1132 | m_jkDefragGui->OnPaint(m_jkDefragGui->m_hDC); 1133 | 1134 | EndPaint(hWnd, &ps); 1135 | 1136 | ReleaseMutex(m_jkDefragGui->m_displayMutex); 1137 | } 1138 | 1139 | return 0; 1140 | 1141 | 1142 | case WM_ERASEBKGND: 1143 | { 1144 | // m_jkDefragGui->RedrawScreen = 0; 1145 | InvalidateRect(m_jkDefragGui->m_hWnd,NULL,FALSE); 1146 | } 1147 | 1148 | return 0; 1149 | /* 1150 | case WM_WINDOWPOSCHANGED: 1151 | { 1152 | m_jkDefragGui->RedrawScreen = 0; 1153 | InvalidateRect(m_jkDefragGui->m_hWnd,NULL,FALSE); 1154 | } 1155 | 1156 | return 0; 1157 | */ 1158 | 1159 | case WM_SIZE: 1160 | { 1161 | 1162 | /* 1163 | m_jkDefragGui->m_sizeTimer = SetTimer(m_jkDefragGui->m_hWnd,333,500,NULL); 1164 | */ 1165 | 1166 | 1167 | 1168 | PAINTSTRUCT ps; 1169 | 1170 | WaitForSingleObject(m_jkDefragGui->m_displayMutex,100); 1171 | 1172 | m_jkDefragGui->m_displayMutex = CreateMutex(NULL,FALSE,"JKDefrag"); 1173 | 1174 | m_jkDefragGui->m_hDC = BeginPaint(hWnd, &ps); 1175 | 1176 | m_jkDefragGui->setDisplayData(m_jkDefragGui->m_hDC); 1177 | 1178 | m_jkDefragGui->FillSquares( 0, m_jkDefragGui->m_numDiskSquares); 1179 | 1180 | m_jkDefragGui->PaintImage(m_jkDefragGui->m_hDC); 1181 | 1182 | EndPaint(hWnd, &ps); 1183 | 1184 | ReleaseMutex(m_jkDefragGui->m_displayMutex); 1185 | 1186 | } 1187 | 1188 | return 0; 1189 | } 1190 | 1191 | return(DefWindowProc(hWnd,Message,wParam,lParam)); 1192 | } 1193 | 1194 | void JKDefragGui::FillSquares( int clusterStartSquareNum, int clusterEndSquareNum ) 1195 | { 1196 | float clusterPerSquare = (float)(m_numClusters / m_numDiskSquares); 1197 | 1198 | for(int ii = clusterStartSquareNum; ii <= clusterEndSquareNum; ii++) 1199 | { 1200 | byte currentColor = JKDefragStruct::COLOREMPTY; 1201 | 1202 | byte clusterEmpty = 0; 1203 | byte clusterAllocated = 0; 1204 | byte clusterUnfragmented = 0; 1205 | byte clusterUnmovable= 0; 1206 | byte clusterFragmented = 0; 1207 | byte clusterBusy = 0; 1208 | byte clusterMft = 0; 1209 | byte clusterSpacehog = 0; 1210 | 1211 | for(int kk = (int)(ii * clusterPerSquare); kk < m_numClusters && kk < (int)((ii + 1) * clusterPerSquare); kk++) 1212 | { 1213 | switch (clusterInfo[kk]) 1214 | { 1215 | case JKDefragStruct::COLOREMPTY: 1216 | clusterEmpty = 1; 1217 | break; 1218 | case JKDefragStruct::COLORALLOCATED: 1219 | clusterAllocated = 1; 1220 | break; 1221 | case JKDefragStruct::COLORUNFRAGMENTED: 1222 | clusterUnfragmented = 1; 1223 | break; 1224 | case JKDefragStruct::COLORUNMOVABLE: 1225 | clusterUnmovable = 1; 1226 | break; 1227 | case JKDefragStruct::COLORFRAGMENTED: 1228 | clusterFragmented = 1; 1229 | break; 1230 | case JKDefragStruct::COLORBUSY: 1231 | clusterBusy = 1; 1232 | break; 1233 | case JKDefragStruct::COLORMFT: 1234 | clusterMft = 1; 1235 | break; 1236 | case JKDefragStruct::COLORSPACEHOG: 1237 | clusterSpacehog = 1; 1238 | break; 1239 | } 1240 | } 1241 | 1242 | if (ii < m_numDiskSquares) 1243 | { 1244 | m_clusterSquares[ii].dirty = true; 1245 | m_clusterSquares[ii].color = //maxColor; 1246 | clusterEmpty << 7 | 1247 | clusterAllocated << 6 | 1248 | clusterUnfragmented << 5 | 1249 | clusterUnmovable << 4 | 1250 | clusterFragmented << 3 | 1251 | clusterBusy << 2 | 1252 | clusterMft << 1 | 1253 | clusterSpacehog; 1254 | } 1255 | } 1256 | } 1257 | 1258 | /* 1259 | 1260 | Show a map on the screen of all the clusters on disk. The map shows 1261 | which clusters are free and which are in use. 1262 | The Data->RedrawScreen flag controls redrawing of the screen. It is set 1263 | to "2" (busy) when the subroutine starts. If another thread changes it to 1264 | "1" (request) while the subroutine is busy then it will immediately exit 1265 | without completing the redraw. When redrawing is completely finished the 1266 | flag is set to "0" (no). 1267 | 1268 | */ 1269 | void JKDefragGui::ShowDiskmap(struct DefragDataStruct *Data) 1270 | { 1271 | struct ItemStruct *Item; 1272 | 1273 | STARTING_LCN_INPUT_BUFFER BitmapParam; 1274 | 1275 | struct 1276 | { 1277 | ULONG64 StartingLcn; 1278 | ULONG64 BitmapSize; 1279 | 1280 | BYTE Buffer[65536]; /* Most efficient if binary multiple. */ 1281 | } BitmapData; 1282 | 1283 | ULONG64 Lcn; 1284 | ULONG64 ClusterStart; 1285 | 1286 | DWORD ErrorCode; 1287 | 1288 | int Index; 1289 | int IndexMax; 1290 | 1291 | BYTE Mask; 1292 | 1293 | int InUse; 1294 | int PrevInUse; 1295 | 1296 | DWORD w; 1297 | 1298 | int i; 1299 | 1300 | // *Data->RedrawScreen = 2; /* Set the flag to "busy". */ 1301 | 1302 | /* Exit if the library is not processing a disk yet. */ 1303 | if (Data->Disk.VolumeHandle == NULL) 1304 | { 1305 | // *Data->RedrawScreen = 0; /* Set the flag to "no". */ 1306 | return; 1307 | } 1308 | 1309 | /* Clear screen. */ 1310 | ClearScreen(NULL); 1311 | 1312 | /* Show the map of all the clusters in use. */ 1313 | Lcn = 0; 1314 | ClusterStart = 0; 1315 | PrevInUse = 1; 1316 | 1317 | do 1318 | { 1319 | if (*Data->Running != RUNNING) break; 1320 | // if (*Data->RedrawScreen != 2) break; 1321 | if (Data->Disk.VolumeHandle == INVALID_HANDLE_VALUE) break; 1322 | 1323 | /* Fetch a block of cluster data. */ 1324 | BitmapParam.StartingLcn.QuadPart = Lcn; 1325 | 1326 | ErrorCode = DeviceIoControl(Data->Disk.VolumeHandle,FSCTL_GET_VOLUME_BITMAP, 1327 | &BitmapParam,sizeof(BitmapParam),&BitmapData,sizeof(BitmapData),&w,NULL); 1328 | 1329 | if (ErrorCode != 0) 1330 | { 1331 | ErrorCode = NO_ERROR; 1332 | } 1333 | else 1334 | { 1335 | ErrorCode = GetLastError(); 1336 | } 1337 | 1338 | if ((ErrorCode != NO_ERROR) && (ErrorCode != ERROR_MORE_DATA)) break; 1339 | 1340 | /* Sanity check. */ 1341 | if (Lcn >= BitmapData.StartingLcn + BitmapData.BitmapSize) break; 1342 | 1343 | /* Analyze the clusterdata. We resume where the previous block left off. */ 1344 | Lcn = BitmapData.StartingLcn; 1345 | Index = 0; 1346 | Mask = 1; 1347 | 1348 | IndexMax = sizeof(BitmapData.Buffer); 1349 | 1350 | if (BitmapData.BitmapSize / 8 < IndexMax) IndexMax = (int)(BitmapData.BitmapSize / 8); 1351 | 1352 | while ((Index < IndexMax) && (*Data->Running == RUNNING)) 1353 | { 1354 | InUse = (BitmapData.Buffer[Index] & Mask); 1355 | 1356 | /* If at the beginning of the disk then copy the InUse value as our 1357 | starting value. */ 1358 | if (Lcn == 0) PrevInUse = InUse; 1359 | 1360 | /* At the beginning and end of an Exclude draw the cluster. */ 1361 | if ((Lcn == Data->MftExcludes[0].Start) || (Lcn == Data->MftExcludes[0].End) || 1362 | (Lcn == Data->MftExcludes[1].Start) || (Lcn == Data->MftExcludes[1].End) || 1363 | (Lcn == Data->MftExcludes[2].Start) || (Lcn == Data->MftExcludes[2].End)) 1364 | { 1365 | if ((Lcn == Data->MftExcludes[0].End) || 1366 | (Lcn == Data->MftExcludes[1].End) || 1367 | (Lcn == Data->MftExcludes[2].End)) 1368 | { 1369 | DrawCluster(Data,ClusterStart,Lcn,JKDefragStruct::COLORUNMOVABLE); 1370 | } 1371 | else 1372 | if (PrevInUse == 0) 1373 | { 1374 | DrawCluster(Data,ClusterStart,Lcn,JKDefragStruct::COLOREMPTY); 1375 | } 1376 | else 1377 | { 1378 | DrawCluster(Data,ClusterStart,Lcn,JKDefragStruct::COLORALLOCATED); 1379 | } 1380 | 1381 | InUse = 1; 1382 | PrevInUse = 1; 1383 | ClusterStart = Lcn; 1384 | } 1385 | 1386 | if ((PrevInUse == 0) && (InUse != 0)) /* Free */ 1387 | { 1388 | DrawCluster(Data,ClusterStart,Lcn,JKDefragStruct::COLOREMPTY); 1389 | 1390 | ClusterStart = Lcn; 1391 | } 1392 | 1393 | if ((PrevInUse != 0) && (InUse == 0)) /* In use */ 1394 | { 1395 | DrawCluster(Data,ClusterStart,Lcn,JKDefragStruct::COLORALLOCATED); 1396 | 1397 | ClusterStart = Lcn; 1398 | } 1399 | 1400 | PrevInUse = InUse; 1401 | 1402 | if (Mask == 128) 1403 | { 1404 | Mask = 1; 1405 | Index = Index + 1; 1406 | } 1407 | else 1408 | { 1409 | Mask = Mask << 1; 1410 | } 1411 | 1412 | Lcn = Lcn + 1; 1413 | } 1414 | } while ((ErrorCode == ERROR_MORE_DATA) && (Lcn < BitmapData.StartingLcn + BitmapData.BitmapSize)); 1415 | 1416 | if ((Lcn > 0)/* && (*Data->RedrawScreen == 2)*/) 1417 | { 1418 | if (PrevInUse == 0) /* Free */ 1419 | { 1420 | DrawCluster(Data,ClusterStart,Lcn,JKDefragStruct::COLOREMPTY); 1421 | } 1422 | 1423 | if (PrevInUse != 0) /* In use */ 1424 | { 1425 | DrawCluster(Data,ClusterStart,Lcn,JKDefragStruct::COLORALLOCATED); 1426 | } 1427 | } 1428 | 1429 | /* Show the MFT zones. */ 1430 | for (i = 0; i < 3; i++) 1431 | { 1432 | // if (*Data->RedrawScreen != 2) break; 1433 | if (Data->MftExcludes[i].Start <= 0) continue; 1434 | 1435 | DrawCluster(Data,Data->MftExcludes[i].Start, Data->MftExcludes[i].End, JKDefragStruct::COLORMFT); 1436 | } 1437 | 1438 | /* Colorize all the files on the screen. 1439 | Note: the "$BadClus" file on NTFS disks maps the entire disk, so we have to 1440 | ignore it. */ 1441 | for (Item = m_jkLib->TreeSmallest(Data->ItemTree); Item != NULL; Item = m_jkLib->TreeNext(Item)) 1442 | { 1443 | if (*Data->Running != RUNNING) break; 1444 | // if (*Data->RedrawScreen != 2) break; 1445 | 1446 | if ((Item->LongFilename != NULL) && 1447 | ((_wcsicmp(Item->LongFilename,L"$BadClus") == 0) || 1448 | (_wcsicmp(Item->LongFilename,L"$BadClus:$Bad:$DATA") == 0))) continue; 1449 | 1450 | m_jkLib->ColorizeItem(Data,Item,0,0,NO); 1451 | } 1452 | 1453 | /* Set the flag to "no". */ 1454 | // if (*Data->RedrawScreen == 2) *Data->RedrawScreen = 0; 1455 | } 1456 | -------------------------------------------------------------------------------- /JkDefrag/Source/JkDefragGui.h: -------------------------------------------------------------------------------- 1 | #ifndef __JKDEFRAGGUI_H__ 2 | #define __JKDEFRAGGUI_H__ 3 | 4 | const COLORREF Colors[9] = { 5 | RGB(150,150,150), /* 0 COLOREMPTY Empty diskspace. */ 6 | RGB(200,200,200), /* 1 COLORALLOCATED Used diskspace / system files. */ 7 | RGB(0,150,0), /* 2 COLORUNFRAGMENTED Unfragmented files. */ 8 | RGB(128,0,0), /* 3 COLORUNMOVABLE Unmovable files. */ 9 | RGB(200,100,60), /* 4 COLORFRAGMENTED Fragmented files. */ 10 | RGB(0,0,255), /* 5 COLORBUSY Busy color. */ 11 | RGB(255,0,255), /* 6 COLORMFT MFT reserved zones. */ 12 | RGB(0,150,150), /* 7 COLORSPACEHOG Spacehogs. */ 13 | RGB(255,255,255) /* 8 background */ 14 | }; 15 | 16 | struct clusterSquareStruct { 17 | bool dirty; 18 | byte color; 19 | } ; 20 | 21 | class JKDefragGui 22 | { 23 | public: 24 | 25 | // Constructor and destructor 26 | JKDefragGui(); 27 | ~JKDefragGui(); 28 | 29 | // Get instance of the class 30 | static JKDefragGui *getInstance(); 31 | 32 | void ClearScreen(WCHAR *Format, ...); 33 | void DrawCluster(struct DefragDataStruct *Data, ULONG64 ClusterStart, ULONG64 ClusterEnd, int Color); 34 | 35 | void FillSquares( int clusterStartSquareNum, int clusterEndSquareNum ); 36 | void ShowDebug(int Level, struct ItemStruct *Item, WCHAR *Format, ...); 37 | void ShowStatus(struct DefragDataStruct *Data); 38 | void ShowAnalyze(struct DefragDataStruct *Data, struct ItemStruct *Item); 39 | void ShowMove(struct ItemStruct *Item, ULONG64 Clusters, ULONG64 FromLcn, ULONG64 ToLcn, ULONG64 FromVcn); 40 | void ShowDiskmap(struct DefragDataStruct *Data); 41 | 42 | int Initialize(HINSTANCE hInstance, int nCmdShow, JKDefragLog *jkLog, int debugLevel); 43 | void setDisplayData(HDC hdc); 44 | 45 | WPARAM DoModal(); 46 | 47 | void OnPaint(HDC hdc); 48 | void PaintImage(HDC hdc); 49 | 50 | static LRESULT CALLBACK ProcessMessagefn(HWND WindowHandle, UINT Message, WPARAM wParam, LPARAM lParam); 51 | 52 | private: 53 | 54 | HWND m_hWnd; 55 | WNDCLASSEX m_wndClass; 56 | MSG Message; 57 | UINT_PTR m_sizeTimer; 58 | 59 | WCHAR Messages[6][50000]; /* The texts displayed on the screen. */ 60 | int m_debugLevel; 61 | 62 | ULONG64 ProgressStartTime; /* The time at percentage zero. */ 63 | ULONG64 ProgressTime; /* When ProgressDone/ProgressTodo were last updated. */ 64 | ULONG64 ProgressTodo; /* Number of clusters to do. */ 65 | ULONG64 ProgressDone; /* Number of clusters already done. */ 66 | 67 | JKDefragStruct *jkStruct; 68 | 69 | /* graphics data */ 70 | 71 | int m_topHeight; 72 | int m_squareSize; 73 | 74 | // Offsets of drawing area of disk 75 | int m_offsetX; 76 | int m_offsetY; 77 | 78 | // Calculated offset of drawing area of disk 79 | int m_realOffsetX; 80 | int m_realOffsetY; 81 | 82 | // Size of drawing area of disk 83 | Rect m_diskAreaSize; 84 | 85 | // Number of squares in horizontal direction of disk area 86 | int m_numDiskSquaresX; 87 | 88 | // Number of squares in horizontal direction of disk area 89 | int m_numDiskSquaresY; 90 | 91 | // Total number of squares in disk area 92 | int m_numDiskSquares; 93 | 94 | // Color of each square in disk area and status if it is "dirty" 95 | struct clusterSquareStruct m_clusterSquares[1000000]; 96 | 97 | // Color of each disk cluster 98 | byte *clusterInfo; 99 | 100 | // Number of disk clusters 101 | UINT64 m_numClusters; 102 | 103 | /* 0:no, 1:request, 2: busy. */ 104 | // int RedrawScreen; 105 | 106 | // Current window size 107 | Rect m_clientWindowSize; 108 | 109 | // Mutex to make the display single-threaded. 110 | HANDLE m_displayMutex; 111 | 112 | // Handle to graphics device context 113 | HDC m_hDC; 114 | 115 | // Bitmap used for double buffering 116 | Bitmap *m_bmp; 117 | 118 | // pointer to logger 119 | JKDefragLog *m_jkLog; 120 | 121 | // pointer to library 122 | JKDefragLib *m_jkLib; 123 | 124 | // static member that is an instance of itself 125 | static JKDefragGui *m_jkDefragGui; 126 | }; 127 | 128 | 129 | #endif -------------------------------------------------------------------------------- /JkDefrag/Source/JkDefragLib.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The JkDefragDll library. 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Lesser General Public License for more details. 14 | 15 | For the full text of the license see the "License lgpl.txt" file. 16 | 17 | Jeroen C. Kessels 18 | Internet Engineer 19 | http://www.kessels.com/ 20 | 21 | */ 22 | 23 | 24 | /* Include guard */ 25 | 26 | #ifndef JKDEFRAGLIB 27 | #define JKDEFRAGLIB 28 | 29 | #define NO 0 30 | #define YES 1 31 | 32 | #define VIRTUALFRAGMENT 18446744073709551615 /* _UI64_MAX - 1 */ 33 | 34 | /* The three running states. */ 35 | #define RUNNING 0 36 | #define STOPPING 1 37 | #define STOPPED 2 38 | 39 | /* List in memory of the fragments of a file. */ 40 | 41 | struct FragmentListStruct 42 | { 43 | ULONG64 Lcn; /* Logical cluster number, location on disk. */ 44 | ULONG64 NextVcn; /* Virtual cluster number of next fragment. */ 45 | struct FragmentListStruct *Next; 46 | }; 47 | 48 | /* List in memory of all the files on disk, sorted by LCN (Logical Cluster Number). */ 49 | 50 | struct ItemStruct 51 | { 52 | struct ItemStruct *Parent; /* Parent item. */ 53 | struct ItemStruct *Smaller; /* Next smaller item. */ 54 | struct ItemStruct *Bigger; /* Next bigger item. */ 55 | 56 | WCHAR *LongFilename; /* Long filename. */ 57 | WCHAR *LongPath; /* Full path on disk, long filenames. */ 58 | WCHAR *ShortFilename; /* Short filename (8.3 DOS). */ 59 | WCHAR *ShortPath; /* Full path on disk, short filenames. */ 60 | 61 | ULONG64 Bytes; /* Total number of bytes. */ 62 | ULONG64 Clusters; /* Total number of clusters. */ 63 | ULONG64 CreationTime; /* 1 second = 10000000 */ 64 | ULONG64 MftChangeTime; 65 | ULONG64 LastAccessTime; 66 | 67 | struct FragmentListStruct *Fragments; /* List of fragments. */ 68 | 69 | ULONG64 ParentInode; /* The Inode number of the parent directory. */ 70 | 71 | struct ItemStruct *ParentDirectory; 72 | 73 | BOOL Directory; /* YES: it's a directory. */ 74 | BOOL Unmovable; /* YES: file can't/couldn't be moved. */ 75 | BOOL Exclude; /* YES: file is not to be defragged/optimized. */ 76 | BOOL SpaceHog; /* YES: file to be moved to end of disk. */ 77 | }; 78 | 79 | enum DiskType 80 | { 81 | UnknownType = 0, 82 | NTFS = 1, 83 | FAT12 = 12, 84 | FAT16 = 16, 85 | FAT32 = 32 86 | }; 87 | 88 | /* Information about a disk volume. */ 89 | 90 | struct DiskStruct 91 | { 92 | HANDLE VolumeHandle; 93 | 94 | WCHAR *MountPoint; /* Example: "c:" */ 95 | WCHAR *MountPointSlash; /* Example: "c:\" */ 96 | WCHAR VolumeName[52]; /* Example: "\\?\Volume{08439462-3004-11da-bbca-806d6172696f}" */ 97 | WCHAR VolumeNameSlash[52]; /* Example: "\\?\Volume{08439462-3004-11da-bbca-806d6172696f}\" */ 98 | 99 | DiskType Type; 100 | 101 | ULONG64 MftLockedClusters; /* Number of clusters at begin of MFT that cannot be moved. */ 102 | }; 103 | 104 | /* List of clusters used by the MFT. */ 105 | 106 | struct ExcludesStruct 107 | { 108 | ULONG64 Start; 109 | ULONG64 End; 110 | }; 111 | 112 | /* The big data struct that holds all the defragger's variables for a single thread. */ 113 | 114 | struct DefragDataStruct 115 | { 116 | int Phase; /* The current Phase (1...3). */ 117 | int Zone; /* The current Zone (0..2) for Phase 3. */ 118 | int *Running; /* If not RUNNING then stop defragging. */ 119 | // int *RedrawScreen; /* 0:no, 1:request, 2: busy. */ 120 | BOOL UseLastAccessTime; /* If TRUE then use LastAccessTime for SpaceHogs. */ 121 | int CannotMoveDirs; /* If bigger than 20 then do not move dirs. */ 122 | 123 | WCHAR *IncludeMask; /* Example: "c:\t1\*" */ 124 | struct DiskStruct Disk; 125 | 126 | double FreeSpace; /* Percentage of total disk size 0..100. */ 127 | 128 | /* Tree in memory with information about all the files. */ 129 | 130 | struct ItemStruct *ItemTree; 131 | int BalanceCount; 132 | WCHAR **Excludes; /* Array with exclude masks. */ 133 | BOOL UseDefaultSpaceHogs; /* TRUE: use the built-in SpaceHogs. */ 134 | WCHAR **SpaceHogs; /* Array with SpaceHog masks. */ 135 | ULONG64 Zones[4]; /* Begin (LCN) of the zones. */ 136 | 137 | struct ExcludesStruct MftExcludes[3]; /* List of clusters reserved for the MFT. */ 138 | 139 | /* Counters filled before Phase 1. */ 140 | 141 | ULONG64 TotalClusters; /* Size of the volume, in clusters. */ 142 | ULONG64 BytesPerCluster; /* Number of bytes per cluster. */ 143 | 144 | /* Counters updated before/after every Phase. */ 145 | 146 | ULONG64 CountFreeClusters; /* Number of free clusters. */ 147 | ULONG64 CountGaps; /* Number of gaps. */ 148 | ULONG64 BiggestGap; /* Size of biggest gap, in clusters. */ 149 | ULONG64 CountGapsLess16; /* Number of gaps smaller than 16 clusters. */ 150 | ULONG64 CountClustersLess16; /* Number of clusters in gaps that are smaller than 16 clusters. */ 151 | 152 | /* Counters updated after every Phase, but not before Phase 1 (analyze). */ 153 | 154 | ULONG64 CountDirectories; /* Number of analysed subdirectories. */ 155 | ULONG64 CountAllFiles; /* Number of analysed files. */ 156 | ULONG64 CountFragmentedItems; /* Number of fragmented files. */ 157 | ULONG64 CountAllBytes; /* Bytes in analysed files. */ 158 | ULONG64 CountFragmentedBytes; /* Bytes in fragmented files. */ 159 | ULONG64 CountAllClusters; /* Clusters in analysed files. */ 160 | ULONG64 CountFragmentedClusters; /* Clusters in fragmented files. */ 161 | double AverageDistance; /* Between end and begin of files. */ 162 | 163 | /* Counters used to calculate the percentage of work done. */ 164 | 165 | ULONG64 PhaseTodo; /* Number of items to do in this Phase. */ 166 | ULONG64 PhaseDone; /* Number of items already done in this Phase. */ 167 | 168 | /* Variables used to throttle the speed. */ 169 | 170 | int Speed; /* Speed as a percentage 1..100. */ 171 | LONG64 StartTime; 172 | LONG64 RunningTime; 173 | LONG64 LastCheckpoint; 174 | 175 | /* The array with error messages. */ 176 | WCHAR **DebugMsg; 177 | }; 178 | 179 | 180 | class JKDefragLib 181 | { 182 | public: 183 | JKDefragLib(); 184 | ~JKDefragLib(); 185 | 186 | static JKDefragLib *getInstance(); 187 | 188 | /* Run the defragger/optimizer. 189 | 190 | The parameters: 191 | 192 | Path: 193 | The name of a disk, mountpoint, directory, or file. It may contain 194 | wildcards '*' and '?'. If Path is empty or NULL then defrag all the 195 | mounted, writable, fixed disks on the computer. Some examples: 196 | 197 | c: 198 | c:\xyz 199 | c:\xyz\*.txt 200 | \\?\Volume{08439462-3004-11da-bbca-806d6172696f} 201 | 202 | Mode: 203 | 0 = Analyze only, do not defragment and do not optimize. 204 | 1 = Analyze and fixup, do not optimize. 205 | 2 = Analyze, fixup, and fast optimization (default). 206 | 3 = Deprecated. Analyze, fixup, and full optimization. 207 | 4 = Analyze and force together. 208 | 5 = Analyze and move to end of disk. 209 | 6 = Analyze and sort files by name. 210 | 7 = Analyze and sort files by size (smallest first). 211 | 8 = Analyze and sort files by last access (newest first). 212 | 9 = Analyze and sort files by last change (oldest first). 213 | 10 = Analyze and sort files by creation time (oldest first). 214 | 215 | Speed: 216 | Percentage 0...100 of the normal speed. The defragger will slow down 217 | by inserting sleep periods so that the wall time is 100% and the 218 | actual processing time is this percentage. Specify 100 (or zero) to 219 | run at maximum speed. 220 | 221 | FreeSpace: 222 | Percentage 0...100 of the total volume space that must be kept 223 | free after the MFT and directories. 224 | 225 | Excludes: 226 | Array of strings. Each string contains a mask, last string must be 227 | NULL. If an item (disk, file, directory) matches one of the strings 228 | in this array then it will be ignored (skipped). Specify NULL to 229 | disable this feature. 230 | 231 | SpaceHogs: 232 | Array of strings. Each string contains a mask, last string must be 233 | NULL. If an item (file, directory) matches one of the strings in 234 | this array then it will be marked as a space hog and moved to the end 235 | of the disk. A build-in list of spacehogs will be added to this list, 236 | except if one of the strings in the array is "DisableDefaults". 237 | 238 | Running: 239 | Pointer to an integer. It is used by the StopJkDefrag() subroutine 240 | to stop the defragger. If the pointer is NULL then this feature is 241 | disabled. 242 | 243 | RedrawScreen: 244 | Pointer to an integer. It can be used by other threads to signal the 245 | defragger that it must redraw the screen, for example when the window 246 | is resized. If the pointer is NULL then this feature is disabled. 247 | 248 | ShowStatus: 249 | Callback subroutine. Is called just before the defragger starts a 250 | new Phase, and when it finishes a volume. Specify NULL if the callback 251 | is not needed. 252 | 253 | ShowMove: 254 | Callback subroutine. Is called whenever an item (file, directory) is 255 | moved on disk. Specify NULL if the callback is not needed. 256 | 257 | ShowAnalyze: 258 | Callback subroutine. Is called for every file during analysis. 259 | This subroutine is called one last time with Item=NULL when analysis 260 | has finished. Specify NULL if the callback is not needed. 261 | 262 | ShowDebug: 263 | Callback subroutine. Is called for every message to show. Specify NULL 264 | if the callback is not needed. 265 | 266 | DrawCluster: 267 | Callback subroutine. Is called to paint a fragment on the screen in 268 | a color. There are 7 colors, see the .h file. Specify NULL if the 269 | callback is not needed. 270 | 271 | ClearScreen: 272 | Callback subroutine. Is called when the defragger wants to clear the 273 | diskmap on the screen. Specify NULL if the callback is not needed. 274 | 275 | DebugMsg: 276 | Array of textmessages, used when the defragger wants to show a debug 277 | message. Specify NULL to use the internal default array of english text 278 | messages. 279 | */ 280 | 281 | __declspec(dllexport) void RunJkDefrag(WCHAR *Path, int Mode, int Speed, double FreeSpace, WCHAR **Excludes, WCHAR **SpaceHogs, int *Running, 282 | /*int *RedrawScreen, */WCHAR **DebugMsg); 283 | 284 | /* 285 | 286 | Stop the defragger. Wait for a maximum of TimeOut milliseconds for the 287 | defragger to stop. If TimeOut is zero then wait indefinitely. If TimeOut is 288 | negative then immediately return without waiting. 289 | Note: The "Running" variable must be the same as what was given to the 290 | RunJkDefrag() subroutine. 291 | 292 | */ 293 | __declspec(dllexport) void StopJkDefrag(int *Running, int TimeOut); 294 | 295 | /* Other exported functions that might be useful in programs that use JkDefrag. */ 296 | 297 | __declspec(dllexport) char *stristr(char *Haystack, char *Needle); 298 | __declspec(dllexport) WCHAR *stristrW(WCHAR *Haystack, WCHAR *Needle); 299 | 300 | __declspec(dllexport) void SystemErrorStr(DWORD ErrorCode, WCHAR *Out, size_t Width); 301 | __declspec(dllexport) void ShowHex(struct DefragDataStruct *Data, BYTE *Buffer, ULONG64 Count); 302 | 303 | __declspec(dllexport) int MatchMask(WCHAR *String, WCHAR *Mask); 304 | 305 | __declspec(dllexport) WCHAR **AddArrayString(WCHAR **Array, WCHAR *NewString); 306 | __declspec(dllexport) WCHAR *GetShortPath(struct DefragDataStruct *Data, struct ItemStruct *Item); 307 | __declspec(dllexport) WCHAR *GetLongPath(struct DefragDataStruct *Data, struct ItemStruct *Item); 308 | 309 | __declspec(dllexport) void SlowDown(struct DefragDataStruct *Data); 310 | 311 | __declspec(dllexport) ULONG64 GetItemLcn(struct ItemStruct *Item); 312 | 313 | __declspec(dllexport) struct ItemStruct *TreeSmallest(struct ItemStruct *Top); 314 | __declspec(dllexport) struct ItemStruct *TreeBiggest(struct ItemStruct *Top); 315 | __declspec(dllexport) struct ItemStruct *TreeFirst(struct ItemStruct *Top, int Direction); 316 | __declspec(dllexport) struct ItemStruct *TreePrev(struct ItemStruct *Here); 317 | __declspec(dllexport) struct ItemStruct *TreeNext(struct ItemStruct *Here); 318 | __declspec(dllexport) struct ItemStruct *TreeNextPrev(struct ItemStruct *Here, int Direction); 319 | 320 | __declspec(dllexport) void TreeInsert(struct DefragDataStruct *Data, struct ItemStruct *New); 321 | __declspec(dllexport) void TreeDetach(struct DefragDataStruct *Data, struct ItemStruct *Item); 322 | __declspec(dllexport) void DeleteItemTree(struct ItemStruct *Top); 323 | __declspec(dllexport) int FragmentCount(struct ItemStruct *Item); 324 | __declspec(dllexport) int IsFragmented(struct ItemStruct *Item, ULONG64 Offset, ULONG64 Size); 325 | __declspec(dllexport) void ColorizeItem(struct DefragDataStruct *Data,struct ItemStruct *Item, ULONG64 BusyOffset, ULONG64 BusySize, int UnDraw); 326 | /* 327 | __declspec(dllexport) void ShowDiskmap(struct DefragDataStruct *Data); 328 | */ 329 | __declspec(dllexport) void CallShowStatus(struct DefragDataStruct *Data, int Phase, int Zone); 330 | 331 | private: 332 | WCHAR LowerCase(WCHAR c); 333 | 334 | void AppendToShortPath(struct ItemStruct *Item, WCHAR *Path, size_t Length); 335 | void AppendToLongPath(struct ItemStruct *Item, WCHAR *Path, size_t Length); 336 | ULONG64 FindFragmentBegin(struct ItemStruct *Item, ULONG64 Lcn); 337 | 338 | struct ItemStruct *FindItemAtLcn(struct DefragDataStruct *Data, ULONG64 Lcn); 339 | HANDLE OpenItemHandle(struct DefragDataStruct *Data, struct ItemStruct *Item); 340 | int GetFragments(struct DefragDataStruct *Data, struct ItemStruct *Item, HANDLE FileHandle); 341 | 342 | int FindGap(struct DefragDataStruct *Data, 343 | ULONG64 MinimumLcn, /* Gap must be at or above this LCN. */ 344 | ULONG64 MaximumLcn, /* Gap must be below this LCN. */ 345 | ULONG64 MinimumSize, /* Gap must be at least this big. */ 346 | int MustFit, /* YES: gap must be at least MinimumSize. */ 347 | int FindHighestGap, /* YES: return the last gap that fits. */ 348 | ULONG64 *BeginLcn, /* Result, LCN of begin of cluster. */ 349 | ULONG64 *EndLcn, /* Result, LCN of end of cluster. */ 350 | BOOL IgnoreMftExcludes); 351 | 352 | void CalculateZones(struct DefragDataStruct *Data); 353 | 354 | DWORD MoveItem1(struct DefragDataStruct *Data, 355 | HANDLE FileHandle, 356 | struct ItemStruct *Item, 357 | ULONG64 NewLcn, /* Where to move to. */ 358 | ULONG64 Offset, /* Number of first cluster to be moved. */ 359 | ULONG64 Size); /* Number of clusters to be moved. */ 360 | 361 | DWORD MoveItem2(struct DefragDataStruct *Data, 362 | HANDLE FileHandle, 363 | struct ItemStruct *Item, 364 | ULONG64 NewLcn, /* Where to move to. */ 365 | ULONG64 Offset, /* Number of first cluster to be moved. */ 366 | ULONG64 Size); /* Number of clusters to be moved. */ 367 | 368 | int MoveItem3(struct DefragDataStruct *Data, 369 | struct ItemStruct *Item, 370 | HANDLE FileHandle, 371 | ULONG64 NewLcn, /* Where to move to. */ 372 | ULONG64 Offset, /* Number of first cluster to be moved. */ 373 | ULONG64 Size, /* Number of clusters to be moved. */ 374 | int Strategy); /* 0: move in one part, 1: move individual fragments. */ 375 | 376 | int MoveItem4(struct DefragDataStruct *Data, 377 | struct ItemStruct *Item, 378 | HANDLE FileHandle, 379 | ULONG64 NewLcn, /* Where to move to. */ 380 | ULONG64 Offset, /* Number of first cluster to be moved. */ 381 | ULONG64 Size, /* Number of clusters to be moved. */ 382 | int Direction); /* 0: move up, 1: move down. */ 383 | 384 | int MoveItem(struct DefragDataStruct *Data, 385 | struct ItemStruct *Item, 386 | ULONG64 NewLcn, /* Where to move to. */ 387 | ULONG64 Offset, /* Number of first cluster to be moved. */ 388 | ULONG64 Size, /* Number of clusters to be moved. */ 389 | int Direction); /* 0: move up, 1: move down. */ 390 | 391 | struct ItemStruct *FindHighestItem(struct DefragDataStruct *Data, 392 | ULONG64 ClusterStart, 393 | ULONG64 ClusterEnd, 394 | int Direction, 395 | int Zone); 396 | 397 | struct ItemStruct *FindBestItem(struct DefragDataStruct *Data, 398 | ULONG64 ClusterStart, 399 | ULONG64 ClusterEnd, 400 | int Direction, 401 | int Zone); 402 | 403 | void CompareItems(struct DefragDataStruct *Data, struct ItemStruct *Item); 404 | 405 | int CompareItems(struct ItemStruct *Item1, struct ItemStruct *Item2, int SortField); 406 | 407 | void ScanDir(struct DefragDataStruct *Data, WCHAR *Mask, struct ItemStruct *ParentDirectory); 408 | 409 | void AnalyzeVolume(struct DefragDataStruct *Data); 410 | 411 | void Fixup(struct DefragDataStruct *Data); 412 | 413 | void Defragment(struct DefragDataStruct *Data); 414 | 415 | void ForcedFill(struct DefragDataStruct *Data); 416 | 417 | void Vacate(struct DefragDataStruct *Data, ULONG64 Lcn, ULONG64 Clusters, BOOL IgnoreMftExcludes); 418 | 419 | void MoveMftToBeginOfDisk(struct DefragDataStruct *Data); 420 | 421 | void OptimizeVolume(struct DefragDataStruct *Data); 422 | 423 | void OptimizeSort(struct DefragDataStruct *Data, int SortField); 424 | 425 | void OptimizeUp(struct DefragDataStruct *Data); 426 | 427 | void DefragOnePath(struct DefragDataStruct *Data, WCHAR *Path, int Mode); 428 | 429 | void DefragMountpoints(struct DefragDataStruct *Data, WCHAR *MountPoint, int Mode); 430 | /* 431 | JKScanFat *m_jkScanFat; 432 | JKScanNtfs *m_jkScanNtfs; 433 | */ 434 | 435 | // static member that is an instance of itself 436 | static JKDefragLib *m_jkDefragLib; 437 | }; 438 | 439 | #endif /* Include guard */ -------------------------------------------------------------------------------- /JkDefrag/Source/ScanFat.cpp: -------------------------------------------------------------------------------- 1 | #include "StdAfx.h" 2 | 3 | /* 4 | #include "JkDefragLib.h" 5 | #include "JKDefragStruct.h" 6 | #include "JKDefragLog.h" 7 | #include "JkDefragGui.h" 8 | #include "ScanFat.h" 9 | */ 10 | 11 | JKScanFat *JKScanFat::m_jkScanFat = 0; 12 | 13 | JKScanFat::JKScanFat() 14 | { 15 | m_jkLib = JKDefragLib::getInstance(); 16 | } 17 | 18 | JKScanFat::~JKScanFat() 19 | { 20 | delete m_jkScanFat; 21 | } 22 | 23 | JKScanFat *JKScanFat::getInstance() 24 | { 25 | if (m_jkScanFat == NULL) 26 | { 27 | m_jkScanFat = new JKScanFat(); 28 | } 29 | 30 | return m_jkScanFat; 31 | } 32 | 33 | /* Calculate the checksum of 8.3 filename. */ 34 | UCHAR JKScanFat::CalculateShortNameCheckSum(UCHAR *Name) 35 | { 36 | short Index; 37 | UCHAR CheckSum; 38 | 39 | CheckSum = 0; 40 | 41 | for (Index = 11; Index != 0; Index--) 42 | { 43 | CheckSum = ((CheckSum & 1) ? 0x80 : 0) + (CheckSum >> 1) + *Name++; 44 | } 45 | 46 | return(CheckSum); 47 | } 48 | 49 | /* 50 | 51 | Convert the FAT time fields into a ULONG64 time. 52 | Note: the FAT stores times in local time, not in GMT time. This subroutine converts 53 | that into GMT time, to be compatible with the NTFS date/times. 54 | 55 | */ 56 | ULONG64 JKScanFat::ConvertTime(USHORT Date, USHORT Time, USHORT Time10) 57 | { 58 | FILETIME Time1; 59 | FILETIME Time2; 60 | 61 | ULARGE_INTEGER Time3; 62 | 63 | if (DosDateTimeToFileTime(Date,Time,&Time1) == 0) return(0); 64 | if (LocalFileTimeToFileTime(&Time1,&Time2) == 0) return(0); 65 | 66 | Time3.LowPart = Time2.dwLowDateTime; 67 | Time3.HighPart = Time2.dwHighDateTime; 68 | Time3.QuadPart = Time3.QuadPart + Time10 * 100000; 69 | 70 | return(Time3.QuadPart); 71 | } 72 | 73 | /* 74 | 75 | Determine the number of clusters in an item and translate the FAT clusterlist 76 | into a FragmentList. 77 | - The first cluster number of an item is recorded in it's directory entry. The second 78 | and next cluster numbers are recorded in the FAT, which is simply an array of "next" 79 | cluster numbers. 80 | - A zero-length file has a first cluster number of 0. 81 | - The FAT contains either an EOC mark (End Of Clusterchain) or the cluster number of 82 | the next cluster of the file. 83 | 84 | */ 85 | void JKScanFat::MakeFragmentList(struct DefragDataStruct *Data, struct FatDiskInfoStruct *DiskInfo, struct ItemStruct *Item, ULONG64 Cluster) 86 | { 87 | struct FragmentListStruct *NewFragment; 88 | struct FragmentListStruct *LastFragment; 89 | 90 | ULONG64 FirstCluster; 91 | ULONG64 LastCluster; 92 | ULONG64 Vcn; 93 | 94 | int MaxIterate; 95 | 96 | JKDefragGui *jkGui = JKDefragGui::getInstance(); 97 | 98 | Item->Clusters = 0; 99 | Item->Fragments = NULL; 100 | 101 | /* If cluster is zero then return zero. */ 102 | if (Cluster == 0) return; 103 | 104 | /* Loop through the FAT cluster list, counting the clusters and creating items in the fragment list. */ 105 | FirstCluster = Cluster; 106 | LastCluster = 0; 107 | Vcn = 0; 108 | 109 | for (MaxIterate = 0; MaxIterate < DiskInfo->CountofClusters + 1; MaxIterate++) 110 | { 111 | /* Exit the loop when we have reached the end of the cluster list. */ 112 | if ((Data->Disk.Type == FAT12) && (Cluster >= 0xFF8)) break; 113 | if ((Data->Disk.Type == FAT16) && (Cluster >= 0xFFF8)) break; 114 | if ((Data->Disk.Type == FAT32) && (Cluster >= 0xFFFFFF8)) break; 115 | 116 | /* Sanity check, test if the cluster is within the range of valid cluster numbers. */ 117 | if (Cluster < 2) break; 118 | if (Cluster > DiskInfo->CountofClusters + 1) break; 119 | 120 | /* Increment the cluster counter. */ 121 | Item->Clusters = Item->Clusters + 1; 122 | 123 | /* If this is a new fragment then create a record for the previous fragment. If not then 124 | add the cluster to the counters and continue. */ 125 | if ((Cluster != LastCluster + 1) && (LastCluster != 0)) 126 | { 127 | NewFragment = (struct FragmentListStruct *)malloc(sizeof(struct FragmentListStruct)); 128 | 129 | if (NewFragment == NULL) 130 | { 131 | jkGui->ShowDebug(2,NULL,L"Error: malloc() returned NULL."); 132 | return; 133 | } 134 | 135 | NewFragment->Lcn = FirstCluster - 2; 136 | Vcn = Vcn + LastCluster - FirstCluster + 1; 137 | NewFragment->NextVcn = Vcn; 138 | NewFragment->Next = NULL; 139 | 140 | if (Item->Fragments == NULL) 141 | { 142 | Item->Fragments = NewFragment; 143 | } 144 | else 145 | { 146 | if (LastFragment != NULL) LastFragment->Next = NewFragment; 147 | } 148 | 149 | LastFragment = NewFragment; 150 | FirstCluster = Cluster; 151 | } 152 | 153 | LastCluster = Cluster; 154 | 155 | /* Get next cluster from FAT. */ 156 | switch (Data->Disk.Type) 157 | { 158 | case FAT12: 159 | if ((Cluster & 1) == 1) 160 | { 161 | Cluster = *((WORD *)&DiskInfo->FatData.FAT12[Cluster + Cluster / 2]) >> 4; 162 | } 163 | else 164 | { 165 | Cluster = *((WORD *)&DiskInfo->FatData.FAT12[Cluster + Cluster / 2]) & 0xFFF; 166 | } 167 | 168 | break; 169 | 170 | case FAT16: Cluster = DiskInfo->FatData.FAT16[Cluster]; break; 171 | case FAT32: Cluster = DiskInfo->FatData.FAT32[Cluster] & 0xFFFFFFF; break; 172 | } 173 | } 174 | 175 | /* If too many iterations (infinite loop in FAT) then exit. */ 176 | if (MaxIterate >= DiskInfo->CountofClusters + 1) 177 | { 178 | jkGui->ShowDebug(2,NULL,L"Infinite loop in FAT detected, perhaps the disk is corrupted."); 179 | 180 | return; 181 | } 182 | 183 | /* Create the last fragment. */ 184 | if (LastCluster != 0) 185 | { 186 | NewFragment = (struct FragmentListStruct *)malloc(sizeof(struct FragmentListStruct)); 187 | 188 | if (NewFragment == NULL) 189 | { 190 | jkGui->ShowDebug(2,NULL,L"Error: malloc() returned NULL."); 191 | 192 | return; 193 | } 194 | 195 | NewFragment->Lcn = FirstCluster - 2; 196 | Vcn = Vcn + LastCluster - FirstCluster + 1; 197 | NewFragment->NextVcn = Vcn; 198 | NewFragment->Next = NULL; 199 | 200 | if (Item->Fragments == NULL) 201 | { 202 | Item->Fragments = NewFragment; 203 | } 204 | else 205 | { 206 | if (LastFragment != NULL) LastFragment->Next = NewFragment; 207 | } 208 | } 209 | } 210 | 211 | /* Load a directory from disk into a new memory buffer. Return NULL if error. 212 | Note: the caller is responsible for free'ing the buffer. */ 213 | BYTE *JKScanFat::LoadDirectory(struct DefragDataStruct *Data, struct FatDiskInfoStruct *DiskInfo, ULONG64 StartCluster, ULONG64 *OutLength) 214 | { 215 | BYTE *Buffer; 216 | 217 | ULONG64 BufferLength; 218 | ULONG64 BufferOffset; 219 | ULONG64 Cluster; 220 | ULONG64 FirstCluster; 221 | ULONG64 LastCluster; 222 | ULONG64 FragmentLength; 223 | 224 | OVERLAPPED gOverlapped; 225 | 226 | ULARGE_INTEGER Trans; 227 | 228 | DWORD BytesRead; 229 | 230 | int Result; 231 | int MaxIterate; 232 | 233 | WCHAR s1[BUFSIZ]; 234 | 235 | JKDefragGui *jkGui = JKDefragGui::getInstance(); 236 | 237 | /* Reset the OutLength to zero, in case we exit for an error. */ 238 | if (OutLength != NULL) *OutLength = 0; 239 | 240 | /* If cluster is zero then return NULL. */ 241 | if (StartCluster == 0) return(NULL); 242 | 243 | /* Count the size of the directory. */ 244 | BufferLength = 0; 245 | Cluster = StartCluster; 246 | 247 | for (MaxIterate = 0; MaxIterate < DiskInfo->CountofClusters + 1; MaxIterate++) 248 | { 249 | /* Exit the loop when we have reached the end of the cluster list. */ 250 | if ((Data->Disk.Type == FAT12) && (Cluster >= 0xFF8)) break; 251 | if ((Data->Disk.Type == FAT16) && (Cluster >= 0xFFF8)) break; 252 | if ((Data->Disk.Type == FAT32) && (Cluster >= 0xFFFFFF8)) break; 253 | 254 | /* Sanity check, test if the cluster is within the range of valid cluster numbers. */ 255 | if (Cluster < 2) return(NULL); 256 | if (Cluster > DiskInfo->CountofClusters + 1) return(NULL); 257 | 258 | /* Increment the BufferLength counter. */ 259 | BufferLength = BufferLength + DiskInfo->SectorsPerCluster * DiskInfo->BytesPerSector; 260 | 261 | /* Get next cluster from FAT. */ 262 | switch (Data->Disk.Type) 263 | { 264 | case FAT12: 265 | if ((Cluster & 1) == 1) 266 | { 267 | Cluster = *((WORD *)&DiskInfo->FatData.FAT12[Cluster + Cluster / 2]) >> 4; 268 | } 269 | else 270 | { 271 | Cluster = *((WORD *)&DiskInfo->FatData.FAT12[Cluster + Cluster / 2]) & 0xFFF; 272 | } 273 | break; 274 | 275 | case FAT16: Cluster = DiskInfo->FatData.FAT16[Cluster]; break; 276 | case FAT32: Cluster = DiskInfo->FatData.FAT32[Cluster] & 0xFFFFFFF; break; 277 | } 278 | } 279 | 280 | /* If too many iterations (infinite loop in FAT) then return NULL. */ 281 | if (MaxIterate >= DiskInfo->CountofClusters + 1) 282 | { 283 | jkGui->ShowDebug(2,NULL,L"Infinite loop in FAT detected, perhaps the disk is corrupted."); 284 | 285 | return(NULL); 286 | } 287 | 288 | /* Allocate buffer. */ 289 | if (BufferLength > UINT_MAX) 290 | { 291 | jkGui->ShowDebug(2,NULL,L"Directory is too big, %I64u bytes",BufferLength); 292 | 293 | return(NULL); 294 | } 295 | 296 | Buffer = (BYTE *)malloc((size_t)BufferLength); 297 | 298 | if (Buffer == NULL) 299 | { 300 | jkGui->ShowDebug(2,NULL,L"Error: malloc() returned NULL."); 301 | 302 | return(NULL); 303 | } 304 | 305 | /* Loop through the FAT cluster list and load all fragments from disk into the buffer. */ 306 | BufferOffset = 0; 307 | Cluster = StartCluster; 308 | FirstCluster = Cluster; 309 | LastCluster = 0; 310 | 311 | for (MaxIterate = 0; MaxIterate < DiskInfo->CountofClusters + 1; MaxIterate++) 312 | { 313 | /* Exit the loop when we have reached the end of the cluster list. */ 314 | if ((Data->Disk.Type == FAT12) && (Cluster >= 0xFF8)) break; 315 | if ((Data->Disk.Type == FAT16) && (Cluster >= 0xFFF8)) break; 316 | if ((Data->Disk.Type == FAT32) && (Cluster >= 0xFFFFFF8)) break; 317 | 318 | /* Sanity check, test if the cluster is within the range of valid cluster numbers. */ 319 | if (Cluster < 2) break; 320 | if (Cluster > DiskInfo->CountofClusters + 1) break; 321 | 322 | /* If this is a new fragment then load the previous fragment from disk. If not then 323 | add to the counters and continue. */ 324 | if ((Cluster != LastCluster + 1) && (LastCluster != 0)) 325 | { 326 | FragmentLength = (LastCluster - FirstCluster + 1) * DiskInfo->SectorsPerCluster * DiskInfo->BytesPerSector; 327 | Trans.QuadPart = (DiskInfo->FirstDataSector + (FirstCluster - 2) * DiskInfo->SectorsPerCluster) * DiskInfo->BytesPerSector; 328 | 329 | gOverlapped.Offset = Trans.LowPart; 330 | gOverlapped.OffsetHigh = Trans.HighPart; 331 | gOverlapped.hEvent = NULL; 332 | 333 | jkGui->ShowDebug(6,NULL,L"Reading directory fragment, %I64u bytes at offset=%I64u.", FragmentLength,Trans.QuadPart); 334 | 335 | Result = ReadFile(Data->Disk.VolumeHandle,&Buffer[BufferOffset],(DWORD)FragmentLength,&BytesRead,&gOverlapped); 336 | 337 | if (Result == 0) 338 | { 339 | m_jkLib->SystemErrorStr(GetLastError(),s1,BUFSIZ); 340 | 341 | jkGui->ShowDebug(2,NULL,L"Error: %s",s1); 342 | 343 | free(Buffer); 344 | 345 | return(NULL); 346 | } 347 | 348 | //ShowHex(Data,Buffer,256); 349 | BufferOffset = BufferOffset + FragmentLength; 350 | FirstCluster = Cluster; 351 | } 352 | 353 | LastCluster = Cluster; 354 | 355 | /* Get next cluster from FAT. */ 356 | switch (Data->Disk.Type) 357 | { 358 | case FAT12: 359 | if ((Cluster & 1) == 1) 360 | { 361 | Cluster = *((WORD *)&DiskInfo->FatData.FAT12[Cluster + Cluster / 2]) >> 4; 362 | } else { 363 | Cluster = *((WORD *)&DiskInfo->FatData.FAT12[Cluster + Cluster / 2]) & 0xFFF; 364 | } 365 | 366 | break; 367 | case FAT16: Cluster = DiskInfo->FatData.FAT16[Cluster]; break; 368 | case FAT32: Cluster = DiskInfo->FatData.FAT32[Cluster] & 0xFFFFFFF; break; 369 | } 370 | } 371 | 372 | /* Load the last fragment. */ 373 | if (LastCluster != 0) 374 | { 375 | FragmentLength = (LastCluster - FirstCluster + 1) * DiskInfo->SectorsPerCluster * DiskInfo->BytesPerSector; 376 | Trans.QuadPart = (DiskInfo->FirstDataSector + (FirstCluster - 2) * DiskInfo->SectorsPerCluster) * DiskInfo->BytesPerSector; 377 | 378 | gOverlapped.Offset = Trans.LowPart; 379 | gOverlapped.OffsetHigh = Trans.HighPart; 380 | gOverlapped.hEvent = NULL; 381 | 382 | jkGui->ShowDebug(6,NULL,L"Reading directory fragment, %I64u bytes at offset=%I64u.", FragmentLength,Trans.QuadPart); 383 | 384 | Result = ReadFile(Data->Disk.VolumeHandle,&Buffer[BufferOffset],(DWORD)FragmentLength, &BytesRead,&gOverlapped); 385 | 386 | if (Result == 0) 387 | { 388 | m_jkLib->SystemErrorStr(GetLastError(),s1,BUFSIZ); 389 | 390 | jkGui->ShowDebug(2,NULL,L"Error: %s",s1); 391 | 392 | free(Buffer); 393 | 394 | return(NULL); 395 | } 396 | } 397 | 398 | if (OutLength != NULL) *OutLength = BufferLength; 399 | 400 | return(Buffer); 401 | } 402 | 403 | /* Analyze a directory and add all the items to the item tree. */ 404 | void JKScanFat::AnalyzeFatDirectory(struct DefragDataStruct *Data, struct FatDiskInfoStruct *DiskInfo, BYTE *Buffer, ULONG64 Length, struct ItemStruct *ParentDirectory) 405 | { 406 | struct FatDirStruct *Dir; 407 | struct FatLongNameDirStruct *LDir; 408 | struct ItemStruct *Item; 409 | 410 | DWORD Index; 411 | 412 | WCHAR ShortName[13]; 413 | WCHAR LongName[820]; 414 | 415 | int LastLongNameSection; 416 | 417 | UCHAR LongNameChecksum; 418 | 419 | BYTE *SubDirBuf; 420 | 421 | ULONG64 SubDirLength; 422 | ULONG64 StartCluster; 423 | 424 | WCHAR *p1; 425 | 426 | int i; 427 | 428 | JKDefragGui *jkGui = JKDefragGui::getInstance(); 429 | 430 | /* Sanity check. */ 431 | if ((Buffer == NULL) || (Length == 0)) return; 432 | 433 | /* Slow the program down to the percentage that was specified on the 434 | command line. */ 435 | m_jkLib->SlowDown(Data); 436 | 437 | //ShowHex(Data,Buffer,256); 438 | 439 | /* Walk through all the directory entries, extract the info, and store in memory 440 | in the ItemTree. */ 441 | LastLongNameSection = 0; 442 | 443 | for (Index = 0; Index + 31 < Length; Index = Index + 32) 444 | { 445 | if (*Data->Running != RUNNING) break; 446 | 447 | Dir = (struct FatDirStruct *)&Buffer[Index]; 448 | 449 | /* Ignore free (not used) entries. */ 450 | if (Dir->DIR_Name[0] == 0xE5) 451 | { 452 | jkGui->ShowDebug(6,NULL,L"%u.\tFree (not used)",Index/32); 453 | 454 | continue; 455 | } 456 | 457 | /* Exit at the end of the directory. */ 458 | if (Dir->DIR_Name[0] == 0) 459 | { 460 | jkGui->ShowDebug(6,NULL,L"%u.\tFree (not used), end of directory.",Index/32); 461 | 462 | break; 463 | } 464 | 465 | /* If this is a long filename component then save the string and loop. */ 466 | if ((Dir->DIR_Attr & ATTR_LONG_NAME_MASK) == ATTR_LONG_NAME) 467 | { 468 | LDir = (struct FatLongNameDirStruct *)&Buffer[Index]; 469 | 470 | jkGui->ShowDebug(6,NULL,L"%u.\tLong filename part.",Index/32); 471 | 472 | i = (LDir->LDIR_Ord & 0x3F); 473 | 474 | if (i == 0) 475 | { 476 | LastLongNameSection = 0; 477 | 478 | continue; 479 | } 480 | 481 | if ((LDir->LDIR_Ord & 0x40) == 0x40) 482 | { 483 | wmemset(LongName,L'\0',820); 484 | 485 | LastLongNameSection = i; 486 | LongNameChecksum = LDir->LDIR_Chksum; 487 | } 488 | else 489 | { 490 | if ((i + 1 != LastLongNameSection) || (LongNameChecksum != LDir->LDIR_Chksum)) 491 | { 492 | LastLongNameSection = 0; 493 | 494 | continue; 495 | } 496 | 497 | LastLongNameSection = i; 498 | } 499 | 500 | i = (i - 1) * 13; 501 | 502 | LongName[i++] = LDir->LDIR_Name1[0]; 503 | LongName[i++] = LDir->LDIR_Name1[1]; 504 | LongName[i++] = LDir->LDIR_Name1[2]; 505 | LongName[i++] = LDir->LDIR_Name1[3]; 506 | LongName[i++] = LDir->LDIR_Name1[4]; 507 | LongName[i++] = LDir->LDIR_Name2[0]; 508 | LongName[i++] = LDir->LDIR_Name2[1]; 509 | LongName[i++] = LDir->LDIR_Name2[2]; 510 | LongName[i++] = LDir->LDIR_Name2[3]; 511 | LongName[i++] = LDir->LDIR_Name2[4]; 512 | LongName[i++] = LDir->LDIR_Name2[5]; 513 | LongName[i++] = LDir->LDIR_Name3[0]; 514 | LongName[i++] = LDir->LDIR_Name3[1]; 515 | 516 | continue; 517 | } 518 | 519 | /* If we are here and the long filename counter is not 1 then something is wrong 520 | with the long filename. Ignore the long filename. */ 521 | if (LastLongNameSection != 1) 522 | { 523 | LongName[0] = '\0'; 524 | } 525 | else 526 | if (CalculateShortNameCheckSum(Dir->DIR_Name) != LongNameChecksum) 527 | { 528 | jkGui->ShowDebug(0,NULL,L"%u.\tError: long filename is out of sync"); 529 | LongName[0] = '\0'; 530 | } 531 | 532 | LastLongNameSection = 0; 533 | 534 | /* Extract the short name. */ 535 | for (i = 0; i < 8; i++) ShortName[i] = Dir->DIR_Name[i]; 536 | 537 | for (i = 7; i > 0; i--) if (ShortName[i] != ' ') break; 538 | 539 | if (ShortName[i] != ' ') i++; 540 | 541 | ShortName[i] = '.'; 542 | ShortName[i+1] = Dir->DIR_Name[8]; 543 | ShortName[i+2] = Dir->DIR_Name[9]; 544 | ShortName[i+3] = Dir->DIR_Name[10]; 545 | 546 | if (ShortName[i+3] != ' ') 547 | { 548 | ShortName[i+4] = '\0'; 549 | } 550 | else 551 | if (ShortName[i+2] != ' ') 552 | { 553 | ShortName[i+3] = '\0'; 554 | } 555 | else 556 | if (ShortName[i+1] != ' ') 557 | { 558 | ShortName[i+2] = '\0'; 559 | } 560 | else 561 | { 562 | ShortName[i] = '\0'; 563 | } 564 | if (ShortName[0] == 0x05) ShortName[0] = 0xE5; 565 | 566 | /* If this is a VolumeID then loop. We have no use for it. */ 567 | if ((Dir->DIR_Attr & (ATTR_DIRECTORY | ATTR_VOLUME_ID)) == ATTR_VOLUME_ID) 568 | { 569 | p1 = wcschr(ShortName,L'.'); 570 | 571 | if (p1 != NULL) wcscpy_s(p1,wcslen(p1),p1+1); 572 | 573 | jkGui->ShowDebug(6,NULL,L"%u.\t'%s' (volume ID)",Index/32,ShortName); 574 | 575 | continue; 576 | } 577 | 578 | if ((Dir->DIR_Attr & (ATTR_DIRECTORY | ATTR_VOLUME_ID)) == (ATTR_DIRECTORY | ATTR_VOLUME_ID)) 579 | { 580 | jkGui->ShowDebug(6,NULL,L"%u.\tInvalid directory entry"); 581 | 582 | continue; 583 | } 584 | 585 | /* Ignore "." and "..". */ 586 | if (wcscmp(ShortName,L".") == 0) 587 | { 588 | jkGui->ShowDebug(6,NULL,L"%u.\t'.'",Index/32); 589 | 590 | continue; 591 | } 592 | 593 | if (wcscmp(ShortName,L"..") == 0) 594 | { 595 | jkGui->ShowDebug(6,NULL,L"%u.\t'..'",Index/32); 596 | 597 | continue; 598 | } 599 | 600 | /* Create and fill a new item record in memory. */ 601 | Item = (struct ItemStruct *)malloc(sizeof(struct ItemStruct)); 602 | 603 | if (Item == NULL) 604 | { 605 | jkGui->ShowDebug(2,NULL,L"Error: malloc() returned NULL."); 606 | break; 607 | } 608 | 609 | if (wcscmp(ShortName,L".") == 0) 610 | { 611 | Item->ShortFilename = NULL; 612 | } 613 | else 614 | { 615 | Item->ShortFilename = _wcsdup(ShortName); 616 | jkGui->ShowDebug(6,NULL,L"%u.\t'%s'",Index/32,ShortName); 617 | } 618 | 619 | if (LongName[0] == '\0') 620 | { 621 | Item->LongFilename = NULL; 622 | } 623 | else 624 | { 625 | Item->LongFilename = _wcsdup(LongName); 626 | jkGui->ShowDebug(6,NULL,L"\tLong filename = '%s'",LongName); 627 | } 628 | 629 | if ((Item->LongFilename != NULL) && 630 | (Item->ShortFilename != NULL) && 631 | (_wcsicmp(Item->LongFilename,Item->ShortFilename) == 0)) 632 | { 633 | free(Item->ShortFilename); 634 | 635 | Item->ShortFilename = Item->LongFilename; 636 | } 637 | 638 | if ((Item->LongFilename == NULL) && (Item->ShortFilename != NULL)) Item->LongFilename = Item->ShortFilename; 639 | if ((Item->LongFilename != NULL) && (Item->ShortFilename == NULL)) Item->ShortFilename = Item->LongFilename; 640 | 641 | Item->ShortPath = NULL; 642 | Item->LongPath = NULL; 643 | Item->Bytes = Dir->DIR_FileSize; 644 | 645 | if (Data->Disk.Type == FAT32) 646 | { 647 | StartCluster = MAKELONG(Dir->DIR_FstClusLO,Dir->DIR_FstClusHI); 648 | } 649 | else 650 | { 651 | StartCluster = Dir->DIR_FstClusLO; 652 | } 653 | 654 | MakeFragmentList(Data,DiskInfo,Item,StartCluster); 655 | 656 | Item->CreationTime = ConvertTime(Dir->DIR_CrtDate,Dir->DIR_CrtTime,Dir->DIR_CrtTimeTenth); 657 | Item->MftChangeTime = ConvertTime(Dir->DIR_WrtDate,Dir->DIR_WrtTime,0); 658 | Item->LastAccessTime = ConvertTime(Dir->DIR_LstAccDate,0,0); 659 | Item->ParentInode = 0; 660 | Item->ParentDirectory = ParentDirectory; 661 | Item->Directory = NO; 662 | 663 | if ((Dir->DIR_Attr & (ATTR_DIRECTORY | ATTR_VOLUME_ID)) == ATTR_DIRECTORY) Item->Directory = YES; 664 | 665 | Item->Unmovable = NO; 666 | Item->Exclude = NO; 667 | Item->SpaceHog = NO; 668 | 669 | jkGui->ShowDebug(6,NULL,L"\tSize = %I64u clusters, %I64u bytes",Item->Clusters,Item->Bytes); 670 | 671 | /* Add the item record to the sorted item tree in memory. */ 672 | m_jkLib->TreeInsert(Data,Item); 673 | 674 | /* Draw the item on the screen. */ 675 | jkGui->ShowAnalyze(Data,Item); 676 | // if (*Data->RedrawScreen == NO) { 677 | m_jkLib->ColorizeItem(Data,Item,0,0,NO); 678 | // } else { 679 | // ShowDiskmap(Data); 680 | // } 681 | 682 | /* Increment counters. */ 683 | if (Item->Directory == YES) 684 | { 685 | Data->CountDirectories = Data->CountDirectories + 1; 686 | } 687 | 688 | Data->CountAllFiles = Data->CountAllFiles + 1; 689 | Data->CountAllBytes = Data->CountAllBytes + Item->Bytes; 690 | Data->CountAllClusters = Data->CountAllClusters + Item->Clusters; 691 | 692 | if (m_jkLib->FragmentCount(Item) > 1) 693 | { 694 | Data->CountFragmentedItems = Data->CountFragmentedItems + 1; 695 | Data->CountFragmentedBytes = Data->CountFragmentedBytes + Item->Bytes; 696 | Data->CountFragmentedClusters = Data->CountFragmentedClusters + Item->Clusters; 697 | } 698 | 699 | /* If this is a directory then iterate. */ 700 | if (Item->Directory == YES) 701 | { 702 | SubDirBuf = LoadDirectory(Data,DiskInfo,StartCluster,&SubDirLength); 703 | 704 | AnalyzeFatDirectory(Data,DiskInfo,SubDirBuf,SubDirLength,Item); 705 | 706 | free(SubDirBuf); 707 | 708 | jkGui->ShowDebug(6,NULL,L"Finished with subdirectory."); 709 | } 710 | } 711 | } 712 | 713 | /* Analyze a FAT disk and load into the ItemTree in memory. Return FALSE if the disk is 714 | not a FAT disk, or could not be analyzed. 715 | 716 | - The maximum valid cluster number for the volume is CountofClusters + 1 717 | - The FAT runs from cluster 0 to CountofClusters + 1. 718 | - The size of the FAT is: 719 | FAT12: (CountofClusters + 1) * 1.5 720 | FAT16: (CountofClusters + 1) * 2 721 | FAT32: (CountofClusters + 1) * 4 722 | - Compute the sector number for data cluster number N: 723 | FirstSectorOfCluster = ((N - 2) * BootSector.BPB_SecPerClus) + DiskInfo.FirstDataSector; 724 | - Calculate location in the FAT for a given cluster number N: 725 | switch (FATType) { 726 | case FAT12: FATOffset = N + (N / 2); break; // 12 bit is 1.5 bytes. 727 | case FAT16: FATOffset = N * 2; break; // 16 bit is 2 bytes. 728 | case FAT32: FATOffset = N * 4; break; // 32 bit is 4 bytes. 729 | } 730 | ThisFATSecNum = BPB_ResvdSecCnt + (FATOffset / BPB_BytsPerSec); 731 | ThisFATEntOffset = FATOffset % BPB_BytsPerSec; 732 | - Determine End Of Clusterchain: 733 | IsEOC = FALSE; 734 | switch (FATType) { 735 | case FAT12: if (FATContent >= 0x0FF8) IsEOC = TRUE; break; 736 | case FAT16: if (FATContent >= 0xFFF8) IsEOC = TRUE; break; 737 | case FAT32: if (FATContent >= 0x0FFFFFF8) IsEOC = TRUE; break; 738 | } 739 | - Determine Bad Cluster: 740 | IsBadCluster = FALSE; 741 | switch (FATType) { 742 | case FAT12: if (FATContent == 0x0FF7) IsBadCluster = TRUE; break; 743 | case FAT16: if (FATContent == 0xFFF7) IsBadCluster = TRUE; break; 744 | case FAT32: if (FATContent == 0x0FFFFFF7) IsBadCluster = TRUE; break; 745 | } 746 | */ 747 | BOOL JKScanFat::AnalyzeFatVolume(struct DefragDataStruct *Data) 748 | { 749 | struct FatBootSectorStruct BootSector; 750 | struct FatDiskInfoStruct DiskInfo; 751 | 752 | ULARGE_INTEGER Trans; 753 | 754 | OVERLAPPED gOverlapped; 755 | 756 | DWORD BytesRead; 757 | 758 | size_t FatSize; 759 | 760 | BYTE *RootDirectory; 761 | 762 | ULONG64 RootStart; 763 | ULONG64 RootLength; 764 | 765 | int Result; 766 | 767 | WCHAR s1[BUFSIZ]; 768 | 769 | char s2[BUFSIZ]; 770 | 771 | JKDefragGui *jkGui = JKDefragGui::getInstance(); 772 | 773 | /* Read the boot block from the disk. */ 774 | gOverlapped.Offset = 0; 775 | gOverlapped.OffsetHigh = 0; 776 | gOverlapped.hEvent = NULL; 777 | 778 | Result = ReadFile(Data->Disk.VolumeHandle,&BootSector,sizeof(struct FatBootSectorStruct),&BytesRead,&gOverlapped); 779 | 780 | if ((Result == 0) || (BytesRead != 512)) 781 | { 782 | m_jkLib->SystemErrorStr(GetLastError(),s1,BUFSIZ); 783 | 784 | jkGui->ShowDebug(2,NULL,L"Error while reading bootblock: %s",s1); 785 | 786 | return(FALSE); 787 | } 788 | 789 | /* Test if the boot block is a FAT boot block. */ 790 | if ((BootSector.Signature != 0xAA55) || 791 | (((BootSector.BS_jmpBoot[0] != 0xEB) || (BootSector.BS_jmpBoot[2] != 0x90)) && 792 | (BootSector.BS_jmpBoot[0] != 0xE9))) 793 | { 794 | jkGui->ShowDebug(2,NULL,L"This is not a FAT disk (different cookie)."); 795 | 796 | return(FALSE); 797 | } 798 | 799 | /* Fetch values from the bootblock and determine what FAT this is, FAT12, FAT16, or FAT32. */ 800 | DiskInfo.BytesPerSector = BootSector.BPB_BytsPerSec; 801 | 802 | if (DiskInfo.BytesPerSector == 0) 803 | { 804 | jkGui->ShowDebug(2,NULL,L"This is not a FAT disk (BytesPerSector is zero)."); 805 | 806 | return(FALSE); 807 | } 808 | 809 | DiskInfo.SectorsPerCluster = BootSector.BPB_SecPerClus; 810 | 811 | if (DiskInfo.SectorsPerCluster == 0) 812 | { 813 | jkGui->ShowDebug(2,NULL,L"This is not a FAT disk (SectorsPerCluster is zero)."); 814 | 815 | return(FALSE); 816 | } 817 | 818 | DiskInfo.TotalSectors = BootSector.BPB_TotSec16; 819 | 820 | if (DiskInfo.TotalSectors == 0) DiskInfo.TotalSectors = BootSector.BPB_TotSec32; 821 | 822 | DiskInfo.RootDirSectors = ((BootSector.BPB_RootEntCnt * 32) + (BootSector.BPB_BytsPerSec - 1)) / BootSector.BPB_BytsPerSec; 823 | 824 | DiskInfo.FATSz = BootSector.BPB_FATSz16; 825 | 826 | if (DiskInfo.FATSz == 0) DiskInfo.FATSz = BootSector.Fat32.BPB_FATSz32; 827 | 828 | DiskInfo.FirstDataSector = BootSector.BPB_RsvdSecCnt + (BootSector.BPB_NumFATs * DiskInfo.FATSz) + DiskInfo.RootDirSectors; 829 | 830 | DiskInfo.DataSec = DiskInfo.TotalSectors - (BootSector.BPB_RsvdSecCnt + (BootSector.BPB_NumFATs * DiskInfo.FATSz) + DiskInfo.RootDirSectors); 831 | 832 | DiskInfo.CountofClusters = DiskInfo.DataSec / BootSector.BPB_SecPerClus; 833 | 834 | if (DiskInfo.CountofClusters < 4085) 835 | { 836 | Data->Disk.Type = FAT12; 837 | 838 | jkGui->ShowDebug(0,NULL,L"This is a FAT12 disk."); 839 | } 840 | else 841 | if (DiskInfo.CountofClusters < 65525) 842 | { 843 | Data->Disk.Type = FAT16; 844 | 845 | jkGui->ShowDebug(0,NULL,L"This is a FAT16 disk."); 846 | } 847 | else 848 | { 849 | Data->Disk.Type = FAT32; 850 | 851 | jkGui->ShowDebug(0,NULL,L"This is a FAT32 disk."); 852 | } 853 | 854 | Data->BytesPerCluster = DiskInfo.BytesPerSector * DiskInfo.SectorsPerCluster; 855 | Data->TotalClusters = DiskInfo.CountofClusters; 856 | 857 | /* Output debug information. */ 858 | strncpy_s(s2,BUFSIZ,(char *)&BootSector.BS_OEMName[0],8); 859 | 860 | s2[8] = '\0'; 861 | 862 | jkGui->ShowDebug(2,NULL,L" OEMName: %S",s2); 863 | jkGui->ShowDebug(2,NULL,L" BytesPerSector: %I64u",DiskInfo.BytesPerSector); 864 | jkGui->ShowDebug(2,NULL,L" TotalSectors: %I64u",DiskInfo.TotalSectors); 865 | jkGui->ShowDebug(2,NULL,L" SectorsPerCluster: %I64u",DiskInfo.SectorsPerCluster); 866 | jkGui->ShowDebug(2,NULL,L" RootDirSectors: %I64u",DiskInfo.RootDirSectors); 867 | jkGui->ShowDebug(2,NULL,L" FATSz: %I64u",DiskInfo.FATSz); 868 | jkGui->ShowDebug(2,NULL,L" FirstDataSector: %I64u",DiskInfo.FirstDataSector); 869 | jkGui->ShowDebug(2,NULL,L" DataSec: %I64u",DiskInfo.DataSec); 870 | jkGui->ShowDebug(2,NULL,L" CountofClusters: %I64u",DiskInfo.CountofClusters); 871 | jkGui->ShowDebug(2,NULL,L" ReservedSectors: %lu",BootSector.BPB_RsvdSecCnt); 872 | jkGui->ShowDebug(2,NULL,L" NumberFATs: %lu",BootSector.BPB_NumFATs); 873 | jkGui->ShowDebug(2,NULL,L" RootEntriesCount: %lu",BootSector.BPB_RootEntCnt); 874 | jkGui->ShowDebug(2,NULL,L" MediaType: %X",BootSector.BPB_Media); 875 | jkGui->ShowDebug(2,NULL,L" SectorsPerTrack: %lu",BootSector.BPB_SecPerTrk); 876 | jkGui->ShowDebug(2,NULL,L" NumberOfHeads: %lu",BootSector.BPB_NumHeads); 877 | jkGui->ShowDebug(2,NULL,L" HiddenSectors: %lu",BootSector.BPB_HiddSec); 878 | 879 | if (Data->Disk.Type != FAT32) 880 | { 881 | jkGui->ShowDebug(2,NULL,L" BS_DrvNum: %u",BootSector.Fat16.BS_DrvNum); 882 | jkGui->ShowDebug(2,NULL,L" BS_BootSig: %u",BootSector.Fat16.BS_BootSig); 883 | jkGui->ShowDebug(2,NULL,L" BS_VolID: %u",BootSector.Fat16.BS_VolID); 884 | 885 | strncpy_s(s2,BUFSIZ,(char *)&BootSector.Fat16.BS_VolLab[0],11); 886 | 887 | s2[11] = '\0'; 888 | 889 | jkGui->ShowDebug(2,NULL,L" VolLab: %S",s2); 890 | 891 | strncpy_s(s2,BUFSIZ,(char *)&BootSector.Fat16.BS_FilSysType[0],8); 892 | 893 | s2[8] = '\0'; 894 | 895 | jkGui->ShowDebug(2,NULL,L" FilSysType: %S",s2); 896 | } 897 | else 898 | { 899 | jkGui->ShowDebug(2,NULL,L" FATSz32: %lu",BootSector.Fat32.BPB_FATSz32); 900 | jkGui->ShowDebug(2,NULL,L" ExtFlags: %lu",BootSector.Fat32.BPB_ExtFlags); 901 | jkGui->ShowDebug(2,NULL,L" FSVer: %lu",BootSector.Fat32.BPB_FSVer); 902 | jkGui->ShowDebug(2,NULL,L" RootClus: %lu",BootSector.Fat32.BPB_RootClus); 903 | jkGui->ShowDebug(2,NULL,L" FSInfo: %lu",BootSector.Fat32.BPB_FSInfo); 904 | jkGui->ShowDebug(2,NULL,L" BkBootSec: %lu",BootSector.Fat32.BPB_BkBootSec); 905 | jkGui->ShowDebug(2,NULL,L" DrvNum: %lu",BootSector.Fat32.BS_DrvNum); 906 | jkGui->ShowDebug(2,NULL,L" BootSig: %lu",BootSector.Fat32.BS_BootSig); 907 | jkGui->ShowDebug(2,NULL,L" VolID: %lu",BootSector.Fat32.BS_VolID); 908 | 909 | strncpy_s(s2,BUFSIZ,(char *)&BootSector.Fat32.BS_VolLab[0],11); 910 | 911 | s2[11] = '\0'; 912 | 913 | jkGui->ShowDebug(2,NULL,L" VolLab: %S",s2); 914 | 915 | strncpy_s(s2,BUFSIZ,(char *)&BootSector.Fat32.BS_FilSysType[0],8); 916 | 917 | s2[8] = '\0'; 918 | 919 | jkGui->ShowDebug(2,NULL,L" FilSysType: %S",s2); 920 | } 921 | 922 | /* Read the FAT from disk into memory. */ 923 | switch (Data->Disk.Type) 924 | { 925 | case FAT12: FatSize = (size_t)((DiskInfo.CountofClusters + 1) + (DiskInfo.CountofClusters + 1) / 2); break; 926 | case FAT16: FatSize = (size_t)((DiskInfo.CountofClusters + 1) * 2); break; 927 | case FAT32: FatSize = (size_t)((DiskInfo.CountofClusters + 1) * 4); break; 928 | } 929 | 930 | if (FatSize % DiskInfo.BytesPerSector > 0) 931 | { 932 | FatSize = (size_t)(FatSize + DiskInfo.BytesPerSector - FatSize % DiskInfo.BytesPerSector); 933 | } 934 | 935 | DiskInfo.FatData.FAT12 = (BYTE *)malloc(FatSize); 936 | 937 | if (DiskInfo.FatData.FAT12 == NULL) 938 | { 939 | jkGui->ShowDebug(2,NULL,L"Error: malloc() returned NULL."); 940 | 941 | return(FALSE); 942 | } 943 | 944 | Trans.QuadPart = BootSector.BPB_RsvdSecCnt * DiskInfo.BytesPerSector; 945 | gOverlapped.Offset = Trans.LowPart; 946 | gOverlapped.OffsetHigh = Trans.HighPart; 947 | gOverlapped.hEvent = NULL; 948 | 949 | jkGui->ShowDebug(2,NULL,L"Reading FAT, %lu bytes at offset=%I64u",FatSize,Trans.QuadPart); 950 | 951 | Result = ReadFile(Data->Disk.VolumeHandle,DiskInfo.FatData.FAT12,(DWORD)FatSize,&BytesRead,&gOverlapped); 952 | 953 | if (Result == 0) 954 | { 955 | m_jkLib->SystemErrorStr(GetLastError(),s1,BUFSIZ); 956 | 957 | jkGui->ShowDebug(2,NULL,L"Error: %s",s1); 958 | 959 | return(FALSE); 960 | } 961 | 962 | //ShowHex(Data,DiskInfo.FatData.FAT12,32); 963 | 964 | /* Read the root directory from disk into memory. */ 965 | if (Data->Disk.Type == FAT32) 966 | { 967 | RootDirectory = LoadDirectory(Data,&DiskInfo,BootSector.Fat32.BPB_RootClus,&RootLength); 968 | } 969 | else 970 | { 971 | RootStart = (BootSector.BPB_RsvdSecCnt + BootSector.BPB_NumFATs * DiskInfo.FATSz) * DiskInfo.BytesPerSector; 972 | RootLength = BootSector.BPB_RootEntCnt * 32; 973 | 974 | /* Sanity check. */ 975 | if (RootLength > UINT_MAX) 976 | { 977 | jkGui->ShowDebug(2,NULL,L"Root directory is too big, %I64u bytes",RootLength); 978 | 979 | free(DiskInfo.FatData.FAT12); 980 | 981 | return(FALSE); 982 | } 983 | 984 | if (RootStart > (DiskInfo.CountofClusters + 1) * DiskInfo.SectorsPerCluster * DiskInfo.BytesPerSector) 985 | { 986 | jkGui->ShowDebug(2,NULL,L"Trying to access %I64u, but the last sector is at %I64u", 987 | RootStart,(DiskInfo.CountofClusters + 1) * DiskInfo.SectorsPerCluster * DiskInfo.BytesPerSector); 988 | 989 | free(DiskInfo.FatData.FAT12); 990 | 991 | return(FALSE); 992 | } 993 | 994 | /* We have to round up the Length to the nearest sector. For some reason or other 995 | Microsoft has decided that raw reading from disk can only be done by whole sector, 996 | even though ReadFile() accepts it's parameters in bytes. */ 997 | BytesRead = (DWORD)RootLength; 998 | 999 | if (RootLength % DiskInfo.BytesPerSector > 0) 1000 | { 1001 | BytesRead = (DWORD)(RootLength + DiskInfo.BytesPerSector - RootLength % DiskInfo.BytesPerSector); 1002 | } 1003 | 1004 | /* Allocate buffer. */ 1005 | RootDirectory = (BYTE *)malloc(BytesRead); 1006 | if (RootDirectory == NULL) { 1007 | jkGui->ShowDebug(2,NULL,L"Error: malloc() returned NULL."); 1008 | free(DiskInfo.FatData.FAT12); 1009 | return(FALSE); 1010 | } 1011 | 1012 | /* Read data from disk. */ 1013 | Trans.QuadPart = RootStart; 1014 | 1015 | gOverlapped.Offset = Trans.LowPart; 1016 | gOverlapped.OffsetHigh = Trans.HighPart; 1017 | gOverlapped.hEvent = NULL; 1018 | 1019 | jkGui->ShowDebug(6,NULL,L"Reading root directory, %lu bytes at offset=%I64u.", BytesRead,Trans.QuadPart); 1020 | 1021 | Result = ReadFile(Data->Disk.VolumeHandle,RootDirectory,BytesRead,&BytesRead,&gOverlapped); 1022 | 1023 | if (Result == 0) 1024 | { 1025 | m_jkLib->SystemErrorStr(GetLastError(),s1,BUFSIZ); 1026 | 1027 | jkGui->ShowDebug(2,NULL,L"Error: %s",s1); 1028 | 1029 | free(DiskInfo.FatData.FAT12); 1030 | free(RootDirectory); 1031 | 1032 | return(FALSE); 1033 | } 1034 | } 1035 | 1036 | /* Analyze all the items in the root directory and add to the item tree. */ 1037 | AnalyzeFatDirectory(Data,&DiskInfo,RootDirectory,RootLength,NULL); 1038 | 1039 | /* Cleanup. */ 1040 | free(RootDirectory); 1041 | free(DiskInfo.FatData.FAT12); 1042 | 1043 | return(TRUE); 1044 | } 1045 | -------------------------------------------------------------------------------- /JkDefrag/Source/ScanFat.h: -------------------------------------------------------------------------------- 1 | #ifndef __SCANFAT_H__ 2 | #define __SCANFAT_H__ 3 | 4 | #pragma pack(push,1) /* Align to bytes. */ 5 | 6 | struct FatBootSectorStruct 7 | { 8 | UCHAR BS_jmpBoot[3]; // 0 9 | UCHAR BS_OEMName[8]; // 3 10 | USHORT BPB_BytsPerSec; // 11 11 | UCHAR BPB_SecPerClus; // 13 12 | USHORT BPB_RsvdSecCnt; // 14 13 | UCHAR BPB_NumFATs; // 16 14 | USHORT BPB_RootEntCnt; // 17 15 | USHORT BPB_TotSec16; // 19 16 | UCHAR BPB_Media; // 21 17 | USHORT BPB_FATSz16; // 22 18 | USHORT BPB_SecPerTrk; // 24 19 | USHORT BPB_NumHeads; // 26 20 | ULONG BPB_HiddSec; // 28 21 | ULONG BPB_TotSec32; // 32 22 | 23 | union 24 | { 25 | struct 26 | { 27 | UCHAR BS_DrvNum; // 36 28 | UCHAR BS_Reserved1; // 37 29 | UCHAR BS_BootSig; // 38 30 | ULONG BS_VolID; // 39 31 | UCHAR BS_VolLab[11]; // 43 32 | UCHAR BS_FilSysType[8]; // 54 33 | UCHAR BS_Reserved2[448]; // 62 34 | } Fat16; 35 | 36 | struct 37 | { 38 | ULONG BPB_FATSz32; // 36 39 | USHORT BPB_ExtFlags; // 40 40 | USHORT BPB_FSVer; // 42 41 | ULONG BPB_RootClus; // 44 42 | USHORT BPB_FSInfo; // 48 43 | USHORT BPB_BkBootSec; // 50 44 | UCHAR BPB_Reserved[12]; // 52 45 | UCHAR BS_DrvNum; // 64 46 | UCHAR BS_Reserved1; // 65 47 | UCHAR BS_BootSig; // 66 48 | ULONG BS_VolID; // 67 49 | UCHAR BS_VolLab[11]; // 71 50 | UCHAR BS_FilSysType[8]; // 82 51 | UCHAR BPB_Reserved2[420]; // 90 52 | } Fat32; 53 | }; 54 | 55 | USHORT Signature; // 510 56 | }; 57 | 58 | struct FatDirStruct 59 | { 60 | UCHAR DIR_Name[11]; // 0 File name, 8 + 3. 61 | UCHAR DIR_Attr; // 11 File attributes. 62 | UCHAR DIR_NTRes; // 12 Reserved. 63 | UCHAR DIR_CrtTimeTenth; // 13 Creation time, tenths of a second, 0...199. 64 | USHORT DIR_CrtTime; // 14 Creation time. 65 | USHORT DIR_CrtDate; // 16 Creation date. 66 | USHORT DIR_LstAccDate; // 18 Last access date. 67 | USHORT DIR_FstClusHI; // 20 First cluster number, high word. 68 | USHORT DIR_WrtTime; // 22 Last write time. 69 | USHORT DIR_WrtDate; // 24 Last write date. 70 | USHORT DIR_FstClusLO; // 26 First cluster number, low word. 71 | ULONG DIR_FileSize; // 28 File size in bytes. 72 | }; 73 | 74 | struct FatLongNameDirStruct 75 | { 76 | UCHAR LDIR_Ord; // 0 Sequence number 77 | WCHAR LDIR_Name1[5]; // 1 Characters 1-5 in name 78 | UCHAR LDIR_Attr; // 11 Attribute, must be ATTR_LONG_NAME 79 | UCHAR LDIR_Type; // 12 Always zero 80 | UCHAR LDIR_Chksum; // 13 Checksum 81 | WCHAR LDIR_Name2[6]; // 14 Characters 6-11 82 | UCHAR LDIR_FstClusLO[2]; // 26 Always zero 83 | WCHAR LDIR_Name3[2]; // 28 Characters 12-13 84 | }; 85 | 86 | #pragma pack(pop) /* Reset byte alignment. */ 87 | 88 | /* The attribute flags. */ 89 | #define ATTR_READ_ONLY 0x01 90 | #define ATTR_HIDDEN 0x02 91 | #define ATTR_SYSTEM 0x04 92 | #define ATTR_VOLUME_ID 0x08 93 | #define ATTR_DIRECTORY 0x10 94 | #define ATTR_ARCHIVE 0x20 95 | 96 | #define ATTR_LONG_NAME (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID) 97 | #define ATTR_LONG_NAME_MASK (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID | ATTR_DIRECTORY | ATTR_ARCHIVE) 98 | 99 | /* Struct used by the scanner to store disk information from the bootblock. */ 100 | struct FatDiskInfoStruct 101 | { 102 | ULONG64 BytesPerSector; 103 | ULONG64 SectorsPerCluster; 104 | ULONG64 TotalSectors; 105 | ULONG64 RootDirSectors; 106 | ULONG64 FirstDataSector; 107 | ULONG64 FATSz; 108 | ULONG64 DataSec; 109 | ULONG64 CountofClusters; 110 | 111 | union 112 | { 113 | BYTE *FAT12; 114 | USHORT *FAT16; 115 | ULONG *FAT32; 116 | } FatData; 117 | }; 118 | 119 | class JKScanFat 120 | { 121 | public: 122 | JKScanFat(); 123 | ~JKScanFat(); 124 | 125 | // Get instance of the class 126 | static JKScanFat *getInstance(); 127 | 128 | BOOL AnalyzeFatVolume(struct DefragDataStruct *Data); 129 | 130 | private: 131 | 132 | UCHAR CalculateShortNameCheckSum(UCHAR *Name); 133 | ULONG64 ConvertTime(USHORT Date, USHORT Time, USHORT Time10); 134 | void MakeFragmentList(struct DefragDataStruct *Data, struct FatDiskInfoStruct *DiskInfo, struct ItemStruct *Item, ULONG64 Cluster); 135 | BYTE *LoadDirectory(struct DefragDataStruct *Data, struct FatDiskInfoStruct *DiskInfo, ULONG64 StartCluster, ULONG64 *OutLength); 136 | void AnalyzeFatDirectory(struct DefragDataStruct *Data, struct FatDiskInfoStruct *DiskInfo, BYTE *Buffer, ULONG64 Length, struct ItemStruct *ParentDirectory); 137 | 138 | // static member that is an instance of itself 139 | static JKScanFat *m_jkScanFat; 140 | 141 | JKDefragLib *m_jkLib; 142 | }; 143 | 144 | #endif -------------------------------------------------------------------------------- /JkDefrag/Source/ScanNtfs.h: -------------------------------------------------------------------------------- 1 | #ifndef __SCANNTFS_H__ 2 | #define __SCANNTFS_H__ 3 | 4 | #define MFTBUFFERSIZE 256 * 1024 /* 256 KB seems to be the optimum. */ 5 | 6 | struct INODE_REFERENCE 7 | { 8 | ULONG InodeNumberLowPart; 9 | 10 | USHORT InodeNumberHighPart; 11 | USHORT SequenceNumber; 12 | }; 13 | 14 | struct NTFS_RECORD_HEADER 15 | { 16 | ULONG Type; /* File type, for example 'FILE' */ 17 | 18 | USHORT UsaOffset; /* Offset to the Update Sequence Array */ 19 | USHORT UsaCount; /* Size in words of Update Sequence Array */ 20 | 21 | USN Lsn; /* $LogFile Sequence Number (LSN) */ 22 | }; 23 | 24 | struct FILE_RECORD_HEADER 25 | { 26 | struct NTFS_RECORD_HEADER RecHdr; 27 | 28 | USHORT SequenceNumber; /* Sequence number */ 29 | USHORT LinkCount; /* Hard link count */ 30 | USHORT AttributeOffset; /* Offset to the first Attribute */ 31 | USHORT Flags; /* Flags. bit 1 = in use, bit 2 = directory, bit 4 & 8 = unknown. */ 32 | 33 | ULONG BytesInUse; /* Real size of the FILE record */ 34 | ULONG BytesAllocated; /* Allocated size of the FILE record */ 35 | 36 | INODE_REFERENCE BaseFileRecord; /* File reference to the base FILE record */ 37 | 38 | USHORT NextAttributeNumber; /* Next Attribute Id */ 39 | USHORT Padding; /* Align to 4 UCHAR boundary (XP) */ 40 | 41 | ULONG MFTRecordNumber; /* Number of this MFT Record (XP) */ 42 | 43 | USHORT UpdateSeqNum; /* */ 44 | }; 45 | 46 | enum ATTRIBUTE_TYPE 47 | { 48 | AttributeInvalid = 0x00, /* Not defined by Windows */ 49 | AttributeStandardInformation = 0x10, 50 | AttributeAttributeList = 0x20, 51 | AttributeFileName = 0x30, 52 | AttributeObjectId = 0x40, 53 | AttributeSecurityDescriptor = 0x50, 54 | AttributeVolumeName = 0x60, 55 | AttributeVolumeInformation = 0x70, 56 | AttributeData = 0x80, 57 | AttributeIndexRoot = 0x90, 58 | AttributeIndexAllocation = 0xA0, 59 | AttributeBitmap = 0xB0, 60 | AttributeReparsePoint = 0xC0, /* Reparse Point = Symbolic link */ 61 | AttributeEAInformation = 0xD0, 62 | AttributeEA = 0xE0, 63 | AttributePropertySet = 0xF0, 64 | AttributeLoggedUtilityStream = 0x100 65 | }; 66 | 67 | struct ATTRIBUTE 68 | { 69 | enum ATTRIBUTE_TYPE AttributeType; 70 | 71 | ULONG Length; 72 | 73 | BOOLEAN Nonresident; 74 | 75 | UCHAR NameLength; 76 | 77 | USHORT NameOffset; 78 | USHORT Flags; /* 0x0001 = Compressed, 0x4000 = Encrypted, 0x8000 = Sparse */ 79 | USHORT AttributeNumber; 80 | }; 81 | 82 | struct RESIDENT_ATTRIBUTE 83 | { 84 | struct ATTRIBUTE Attribute; 85 | 86 | ULONG ValueLength; 87 | 88 | USHORT ValueOffset; 89 | USHORT Flags; // 0x0001 = Indexed 90 | }; 91 | 92 | struct NONRESIDENT_ATTRIBUTE 93 | { 94 | struct ATTRIBUTE Attribute; 95 | 96 | ULONGLONG StartingVcn; 97 | ULONGLONG LastVcn; 98 | 99 | USHORT RunArrayOffset; 100 | 101 | UCHAR CompressionUnit; 102 | UCHAR AlignmentOrReserved[5]; 103 | 104 | ULONGLONG AllocatedSize; 105 | ULONGLONG DataSize; 106 | ULONGLONG InitializedSize; 107 | ULONGLONG CompressedSize; // Only when compressed 108 | }; 109 | 110 | struct STANDARD_INFORMATION 111 | { 112 | ULONG64 CreationTime; 113 | ULONG64 FileChangeTime; 114 | ULONG64 MftChangeTime; 115 | ULONG64 LastAccessTime; 116 | 117 | ULONG FileAttributes; /* READ_ONLY=0x01, HIDDEN=0x02, SYSTEM=0x04, VOLUME_ID=0x08, ARCHIVE=0x20, DEVICE=0x40 */ 118 | ULONG MaximumVersions; 119 | ULONG VersionNumber; 120 | ULONG ClassId; 121 | ULONG OwnerId; // NTFS 3.0 only 122 | ULONG SecurityId; // NTFS 3.0 only 123 | 124 | ULONGLONG QuotaCharge; // NTFS 3.0 only 125 | 126 | USN Usn; // NTFS 3.0 only 127 | }; 128 | 129 | struct ATTRIBUTE_LIST 130 | { 131 | enum ATTRIBUTE_TYPE AttributeType; 132 | 133 | USHORT Length; 134 | 135 | UCHAR NameLength; 136 | UCHAR NameOffset; 137 | 138 | ULONGLONG LowestVcn; 139 | 140 | INODE_REFERENCE FileReferenceNumber; 141 | 142 | USHORT Instance; 143 | USHORT AlignmentOrReserved[3]; 144 | }; 145 | 146 | struct FILENAME_ATTRIBUTE 147 | { 148 | struct INODE_REFERENCE ParentDirectory; 149 | 150 | ULONG64 CreationTime; 151 | ULONG64 ChangeTime; 152 | ULONG64 LastWriteTime; 153 | ULONG64 LastAccessTime; 154 | 155 | ULONGLONG AllocatedSize; 156 | ULONGLONG DataSize; 157 | 158 | ULONG FileAttributes; 159 | ULONG AlignmentOrReserved; 160 | 161 | UCHAR NameLength; 162 | UCHAR NameType; /* NTFS=0x01, DOS=0x02 */ 163 | 164 | WCHAR Name[1]; 165 | }; 166 | 167 | struct OBJECTID_ATTRIBUTE 168 | { 169 | GUID ObjectId; 170 | 171 | union 172 | { 173 | struct 174 | { 175 | GUID BirthVolumeId; 176 | GUID BirthObjectId; 177 | GUID DomainId; 178 | }; 179 | 180 | UCHAR ExtendedInfo[48]; 181 | }; 182 | }; 183 | 184 | struct VOLUME_INFORMATION 185 | { 186 | LONGLONG Reserved; 187 | 188 | UCHAR MajorVersion; 189 | UCHAR MinorVersion; 190 | 191 | USHORT Flags; /* DIRTY=0x01, RESIZE_LOG_FILE=0x02 */ 192 | }; 193 | 194 | struct DIRECTORY_INDEX 195 | { 196 | ULONG EntriesOffset; 197 | ULONG IndexBlockLength; 198 | ULONG AllocatedSize; 199 | ULONG Flags; /* SMALL=0x00, LARGE=0x01 */ 200 | }; 201 | 202 | struct DIRECTORY_ENTRY 203 | { 204 | ULONGLONG FileReferenceNumber; 205 | 206 | USHORT Length; 207 | USHORT AttributeLength; 208 | 209 | ULONG Flags; // 0x01 = Has trailing VCN, 0x02 = Last entry 210 | 211 | // FILENAME_ATTRIBUTE Name; 212 | // ULONGLONG Vcn; // VCN in IndexAllocation of earlier entries 213 | }; 214 | 215 | struct INDEX_ROOT 216 | { 217 | enum ATTRIBUTE_TYPE Type; 218 | 219 | ULONG CollationRule; 220 | ULONG BytesPerIndexBlock; 221 | ULONG ClustersPerIndexBlock; 222 | 223 | struct DIRECTORY_INDEX DirectoryIndex; 224 | }; 225 | 226 | struct INDEX_BLOCK_HEADER 227 | { 228 | struct NTFS_RECORD_HEADER Ntfs; 229 | 230 | ULONGLONG IndexBlockVcn; 231 | 232 | struct DIRECTORY_INDEX DirectoryIndex; 233 | }; 234 | 235 | struct REPARSE_POINT 236 | { 237 | ULONG ReparseTag; 238 | 239 | USHORT ReparseDataLength; 240 | USHORT Reserved; 241 | 242 | UCHAR ReparseData[1]; 243 | }; 244 | 245 | struct EA_INFORMATION 246 | { 247 | ULONG EaLength; 248 | ULONG EaQueryLength; 249 | }; 250 | 251 | struct EA_ATTRIBUTE 252 | { 253 | ULONG NextEntryOffset; 254 | 255 | UCHAR Flags; 256 | UCHAR EaNameLength; 257 | 258 | USHORT EaValueLength; 259 | 260 | CHAR EaName[1]; 261 | // UCHAR EaData[]; 262 | }; 263 | 264 | struct ATTRIBUTE_DEFINITION 265 | { 266 | WCHAR AttributeName[64]; 267 | 268 | ULONG AttributeNumber; 269 | ULONG Unknown[2]; 270 | ULONG Flags; 271 | 272 | ULONGLONG MinimumSize; 273 | ULONGLONG MaximumSize; 274 | }; 275 | 276 | /* 277 | The NTFS scanner will construct an ItemStruct list in memory, but needs some 278 | extra information while constructing it. The following structs wrap the ItemStruct 279 | into a new struct with some extra info, discarded when the ItemStruct list is 280 | ready. 281 | 282 | A single Inode can contain multiple streams of data. Every stream has it's own 283 | list of fragments. The name of a stream is the same as the filename plus two 284 | extensions separated by colons: 285 | filename:"stream name":"stream type" 286 | 287 | For example: 288 | myfile.dat:stream1:$DATA 289 | 290 | The "stream name" is an empty string for the default stream, which is the data 291 | of regular files. The "stream type" is one of the following strings: 292 | 0x10 $STANDARD_INFORMATION 293 | 0x20 $ATTRIBUTE_LIST 294 | 0x30 $FILE_NAME 295 | 0x40 NT $VOLUME_VERSION 296 | 0x40 2K $OBJECT_ID 297 | 0x50 $SECURITY_DESCRIPTOR 298 | 0x60 $VOLUME_NAME 299 | 0x70 $VOLUME_INFORMATION 300 | 0x80 $DATA 301 | 0x90 $INDEX_ROOT 302 | 0xA0 $INDEX_ALLOCATION 303 | 0xB0 $BITMAP 304 | 0xC0 NT $SYMBOLIC_LINK 305 | 0xC0 2K $REPARSE_POINT 306 | 0xD0 $EA_INFORMATION 307 | 0xE0 $EA 308 | 0xF0 NT $PROPERTY_SET 309 | 0x100 2K $LOGGED_UTILITY_STREAM 310 | */ 311 | 312 | struct StreamStruct 313 | { 314 | struct StreamStruct *Next; 315 | 316 | WCHAR *StreamName; /* "stream name" */ 317 | 318 | ATTRIBUTE_TYPE StreamType; /* "stream type" */ 319 | 320 | struct FragmentListStruct *Fragments; /* The fragments of the stream. */ 321 | 322 | ULONG64 Clusters; /* Total number of clusters. */ 323 | ULONG64 Bytes; /* Total number of bytes. */ 324 | }; 325 | 326 | struct InodeDataStruct 327 | { 328 | ULONG64 Inode; /* The Inode number. */ 329 | ULONG64 ParentInode; /* The Inode number of the parent directory. */ 330 | 331 | BOOL Directory; /* YES: it's a directory. */ 332 | 333 | WCHAR *LongFilename; /* Long filename. */ 334 | WCHAR *ShortFilename; /* Short filename (8.3 DOS). */ 335 | 336 | ULONG64 Bytes; /* Total number of bytes. */ 337 | ULONG64 CreationTime; /* 1 second = 10000000 */ 338 | ULONG64 MftChangeTime; 339 | ULONG64 LastAccessTime; 340 | 341 | struct StreamStruct *Streams; /* List of StreamStruct. */ 342 | struct FragmentListStruct *MftDataFragments; /* The Fragments of the $MFT::$DATA stream. */ 343 | 344 | ULONG64 MftDataBytes; /* Length of the $MFT::$DATA. */ 345 | 346 | struct FragmentListStruct *MftBitmapFragments; /* The Fragments of the $MFT::$BITMAP stream. */ 347 | 348 | ULONG64 MftBitmapBytes; /* Length of the $MFT::$BITMAP. */ 349 | }; 350 | 351 | struct NtfsDiskInfoStruct 352 | { 353 | ULONG64 BytesPerSector; 354 | ULONG64 SectorsPerCluster; 355 | ULONG64 TotalSectors; 356 | ULONG64 MftStartLcn; 357 | ULONG64 Mft2StartLcn; 358 | ULONG64 BytesPerMftRecord; 359 | ULONG64 ClustersPerIndexRecord; 360 | 361 | struct 362 | { 363 | BYTE Buffer[MFTBUFFERSIZE]; 364 | 365 | ULONG64 Offset; 366 | 367 | int Age; 368 | } Buffers[3]; 369 | }; 370 | 371 | class JKScanNtfs 372 | { 373 | public: 374 | JKScanNtfs(); 375 | ~JKScanNtfs(); 376 | 377 | // Get instance of the class 378 | static JKScanNtfs *getInstance(); 379 | 380 | BOOL AnalyzeNtfsVolume(struct DefragDataStruct *Data); 381 | 382 | private: 383 | WCHAR *StreamTypeNames(ATTRIBUTE_TYPE StreamType); 384 | 385 | BOOL FixupRawMftdata(struct DefragDataStruct *Data,struct NtfsDiskInfoStruct *DiskInfo, BYTE *Buffer, ULONG64 BufLength); 386 | 387 | BYTE *ReadNonResidentData(struct DefragDataStruct *Data, struct NtfsDiskInfoStruct *DiskInfo, BYTE *RunData, DWORD RunDataLength, 388 | ULONG64 Offset, ULONG64 WantedLength); 389 | 390 | BOOL TranslateRundataToFragmentlist( 391 | struct DefragDataStruct *Data, 392 | struct InodeDataStruct *InodeData, 393 | WCHAR *StreamName, 394 | ATTRIBUTE_TYPE StreamType, 395 | BYTE *RunData, 396 | DWORD RunDataLength, 397 | ULONG64 StartingVcn, 398 | ULONG64 Bytes); 399 | 400 | void CleanupStreams(struct InodeDataStruct *InodeData, BOOL CleanupFragments); 401 | 402 | WCHAR *ConstructStreamName(WCHAR *FileName1, WCHAR *FileName2, struct StreamStruct *Stream); 403 | 404 | BOOL ProcessAttributes( 405 | struct DefragDataStruct *Data, 406 | struct NtfsDiskInfoStruct *DiskInfo, 407 | struct InodeDataStruct *InodeData, 408 | BYTE *Buffer, 409 | ULONG64 BufLength, 410 | USHORT Instance, 411 | int Depth); 412 | 413 | void ProcessAttributeList( 414 | struct DefragDataStruct *Data, 415 | struct NtfsDiskInfoStruct *DiskInfo, 416 | struct InodeDataStruct *InodeData, 417 | BYTE *Buffer, 418 | ULONG64 BufLength, 419 | int Depth); 420 | 421 | BOOL InterpretMftRecord( 422 | struct DefragDataStruct *Data, 423 | struct NtfsDiskInfoStruct *DiskInfo, 424 | struct ItemStruct **InodeArray, 425 | ULONG64 InodeNumber, 426 | ULONG64 MaxInode, 427 | struct FragmentListStruct **MftDataFragments, 428 | ULONG64 *MftDataBytes, 429 | struct FragmentListStruct **MftBitmapFragments, 430 | ULONG64 *MftBitmapBytes, 431 | BYTE *Buffer, 432 | ULONG64 BufLength); 433 | 434 | // static member that is an instance of itself 435 | static JKScanNtfs *m_jkScanNtfs; 436 | 437 | // JKDefragGui *m_jkGui; 438 | JKDefragLib *m_jkLib; 439 | }; 440 | 441 | #endif 442 | -------------------------------------------------------------------------------- /JkDefrag/Source/StdAfx.cpp: -------------------------------------------------------------------------------- 1 | #include "StdAfx.h" 2 | -------------------------------------------------------------------------------- /JkDefrag/Source/StdAfx.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __STDAFX_H__ 3 | #define __STDAFX_H__ 4 | 5 | #define _WIN32_WINNT 0x0500 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace Gdiplus; 15 | 16 | #include /* CreateToolhelp32Snapshot() */ 17 | 18 | #ifdef _DEBUG 19 | #include /* SetUnhandledExceptionFilter() */ 20 | #endif 21 | 22 | #include "JKDefragStruct.h" 23 | #include "JkDefragLib.h" 24 | #include "JKDefragLog.h" 25 | #include "ScanFat.h" 26 | #include "ScanNtfs.h" 27 | #include "JkDefragGui.h" 28 | 29 | #endif // __STDAFX_H__ 30 | -------------------------------------------------------------------------------- /JkDefrag2005.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 9.00 3 | # Visual Studio 2005 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JkDefrag", "JkDefrag\JkDefrag2005.vcproj", "{205701F0-3E91-4DD7-AC0F-5A3E273BD3A6}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {205701F0-3E91-4DD7-AC0F-5A3E273BD3A6}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {205701F0-3E91-4DD7-AC0F-5A3E273BD3A6}.Debug|Win32.Build.0 = Debug|Win32 14 | {205701F0-3E91-4DD7-AC0F-5A3E273BD3A6}.Release|Win32.ActiveCfg = Release|Win32 15 | {205701F0-3E91-4DD7-AC0F-5A3E273BD3A6}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /JkDefrag2008.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JkDefrag", "JkDefrag\JkDefrag2008.vcproj", "{205701F0-3E91-4DD7-AC0F-5A3E273BD3A6}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {205701F0-3E91-4DD7-AC0F-5A3E273BD3A6}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {205701F0-3E91-4DD7-AC0F-5A3E273BD3A6}.Debug|Win32.Build.0 = Debug|Win32 14 | {205701F0-3E91-4DD7-AC0F-5A3E273BD3A6}.Release|Win32.ActiveCfg = Release|Win32 15 | {205701F0-3E91-4DD7-AC0F-5A3E273BD3A6}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JKDefrag-Original 2 | [C++] Latest Visual-Studio 2005/2008 Project Of JKDefrag Source-Code. Fixed (Will Compile 100%) 3 | 4 | 5 |
6 | 7 | ``` 8 | JKDefrag is a disk defragmenter and optimizer for Windows 2000/2003/XP/Vista/2008 compatible with x86/x64 platforms architecture. Completely automatic and very easy to use, fast, low overhead, with several optimization strategies, and can handle floppies, USB disks, memory sticks, and anything else that looks like a disk to Windows. 9 | 10 | Included are a Windows version, a commandline version (for scheduling by the task scheduler or for use from administrator scripts), a screensaver version, a DLL library (for use from programming languages), versions for Windows X64, and the complete sources. 11 | 12 | Why use this defragger instead of the standard Windows defragger? 13 | - Much faster. 14 | - Totally automatic, extremely easy to use. 15 | - Optimized for daily use. 16 | - Disk optimization, several strategies. 17 | - Directories are moved to the beginning of the disk. 18 | - Reclaims MFT reserved space after disk-full. 19 | - Maintains free spaces for temporary files. 20 | - Can defragment very full harddisks. 21 | - Can defragment very large files. 22 | - Can defragment individual directories and files. 23 | - Can be run automatically with the Windows Scheduler. 24 | - Can be used from the commandline. 25 | - Can be used as a screen saver. 26 | - Can be run from cdrom or memory stick. 27 | - Sources available, can be customized. 28 | - Supports x86/x64 architecture. 29 | 30 | JKDefrag is an open source software by Jeroen Kessels, 31 | this is the "3.36" version, since from version 4, 32 | it was changed to "MyDefrag", which is a closed source freeware. 33 | ``` 34 | 35 | latest pre-build can be found here: hxxp://icompile.eladkarako.com/jkdefrag/ [web.archive.org](https://web.archive.org/web/20161216015541/icompile.eladkarako.com/jkdefrag) 36 | --------------------------------------------------------------------------------