/*

  Source for JkDefragScreenSaver.exe  --  the defragmenter screen saver.

  German Version by Markus Hörl  ||  http://jkdefrag.8qm.de or http://www.jkdefrag.de

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  For the full text of the license see the "License gpl.txt" file.



  Jeroen C. Kessels
  Internet Engineer
  http://www.kessels.com/

*/




#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <stdio.h>
#include <wchar.h>
#include <time.h>
#include <sys/timeb.h>
#include <scrnsave.h>
#include <process.h>
#include <tlhelp32.h>                  /* CreateToolhelp32Snapshot() */
#include <Powrprof.h>                  /* CallNtPowerInformation() */
#ifdef _DEBUG
#include <dbghelp.h>                   /* SetUnhandledExceptionFilter() */
#endif

#include "JkDefragLib.h"

// Only needed for pre-2008 compiler
#if _MSC_VER < 1500
#include "x64glue.h"
#endif



int Running = STOPPED;           /* If not RUNNING then stop defragging. */
int RedrawScreen = 0;            /* 0:no, 1:request, 2: busy. */

WCHAR LogFile[MAX_PATH] = L"";   /* Path to the logfile. */

HDC WindowDC = NULL;             /* Window graphics handle. */
RECT WindowSize;                 /* Window width/height. */
HANDLE DisplayMutex;             /* Mutex to make the display single-threaded. */

WCHAR MyName1[MAX_PATH] = L"";
WCHAR MyName2[MAX_PATH] = L"";
WCHAR MyShortName1[MAX_PATH] = L"";
WCHAR MyShortName2[MAX_PATH] = L"";
int IamRunning;

WCHAR Messages[6][50000];       /* The texts displayed on the screen. */
int StatusBar;                   /* 0: normal, 1: no filenames, 2: off */

ULONG64 ProgressStartTime;       /* The time at percentage zero. */
ULONG64 ProgressTime;            /* When ProgressDone/ProgressTodo were last updated. */
ULONG64 ProgressTodo;            /* Number of clusters to do. */
ULONG64 ProgressDone;            /* Number of clusters already done. */

/* Array with colors. The defragger library hands a number 0...7 to the callback
   subroutines, which will then use that number to get the actual color from this
   array. */
COLORREF Colors[8] = {
  RGB(0,0,0),           /* 0 COLOREMPTY         Empty diskspace. */
  RGB(160,160,160),     /* 1 COLORALLOCATED     Used diskspace / system files. */
  RGB(0,255,0),         /* 2 COLORUNFRAGMENTED  Unfragmented files. */
  RGB(255,0,0),         /* 3 COLORUNMOVABLE     Unmovable files. */
  RGB(255,255,0),       /* 4 COLORFRAGMENTED    Fragmented files. */
  RGB(255,255,255),     /* 5 COLORBUSY          Busy color. */
  RGB(255,150,150),     /* 6 COLORMFT           MFT reserved zones. */
  RGB(0,200,0)          /* 7 COLORSPACEHOG      Spacehogs. */
  };

/* Debug level.
   0: Fatal errors.
   1: Warning messages.
   2: General progress messages.
   3: Detailed progress messages.
   4: Detailed file information.
   5: Detailed gap-filling messages.
   6: Detailed gap-finding messages.
   */
int Debug;




/* Write a text to the logfile. The parameters are the same as for the "printf"
   functions, a Format string and a series of parameters. */
void LogMessage(WCHAR *Format, ...) {
  va_list VarArgs;
  FILE *Fout;
  int Result;
  time_t Now;
  struct tm NowTm;

  /* If there is no message then return. */
  if (Format == NULL) return;

  /* If there is no logfile then return. */
  if (*LogFile == '\0') return;

  /* Open the logfile. */
  Result = _wfopen_s(&Fout,LogFile,L"a, ccs=UTF-8");
  if ((Result != 0) || (Fout == NULL)) return;

  /* Write the string to the logfile. */
  time(&Now);
  Result = localtime_s(&NowTm,&Now);
  fwprintf_s(Fout,L"%02lu:%02lu:%02lu ",NowTm.tm_hour,NowTm.tm_min,NowTm.tm_sec);
  va_start(VarArgs,Format);
  vfwprintf_s(Fout,Format,VarArgs);
  va_end(VarArgs);
  fwprintf_s(Fout,L"\n");

  /* Close the logfile. */
  fflush(Fout);
  fclose(Fout);
  }




/* Callback: clear the screen. */
void __cdecl ClearScreen(WCHAR *Format, ...) {
  HBRUSH Brush;
  va_list VarArgs;
  FILE *Fout;
  int Result;
  time_t Now;
  struct tm NowTm;
  int i;

  /* Clear the diskmap area. */
  if (WindowDC != NULL) {
    WaitForSingleObject(DisplayMutex,1000);
    Brush = CreateSolidBrush(Colors[COLOREMPTY]);
    if (Brush != NULL) {
      FillRect(WindowDC,&WindowSize,Brush);
      DeleteObject(Brush);
      }
    ReleaseMutex(DisplayMutex);
    }

  /* If there is no message then return. */
  if (Format == NULL) return;

  /* Clear all the messages. */
  for (i = 0; i < 6; i++) *Messages[i] = '\0';

  /* Save the message in Messages 0. */
  va_start(VarArgs,Format);
  vswprintf_s(Messages[0],50000,Format,VarArgs);
  va_end(VarArgs);

  /* If there is no logfile then return. */
  if (*LogFile == '\0') return;

  /* Open the logfile. */
  Result = _wfopen_s(&Fout,LogFile,L"a, ccs=UTF-8");
  if ((Result != 0) || (Fout == NULL)) return;

  /* Write to the logfile. */
  time(&Now);
  Result = localtime_s(&NowTm,&Now);
  fwprintf_s(Fout,L"%02lu:%02lu:%02lu ",NowTm.tm_hour,NowTm.tm_min,NowTm.tm_sec);
  va_start(VarArgs,Format);
  vfwprintf_s(Fout,Format,VarArgs);
  va_end(VarArgs);
  fwprintf_s(Fout,L"\n");

  /* Close the logfile. */
  fflush(Fout);
  fclose(Fout);
  }




/* Callback: whenever an item (file, directory) is moved on disk. */
void __cdecl ShowMove(
    struct ItemStruct *Item,
    ULONG64 Clusters,
    ULONG64 FromLcn,
    ULONG64 ToLcn,
    ULONG64 FromVcn) {

  /* Save the message in Messages 3. */
  if (Clusters == 1) {
      swprintf_s(Messages[3],50000,L"Verschieben eines Blocks von %I64d nach %I64d.",FromLcn,ToLcn);
    } else {
      swprintf_s(Messages[3],50000,L"Verschieben von %I64d Blöcken von %I64d nach %I64d.",
        Clusters,FromLcn,ToLcn);
      }

  /* Save the name of the file in Messages 4. */
  if ((Item != NULL) && (Item->LongPath != NULL)) {
      swprintf_s(Messages[4],50000,L"%s",Item->LongPath);
    } else {
      *Messages[4] = '\0';
      }

  /* If debug mode then write a message to the logfile. */
  if (Debug < 3) return;
  if (FromVcn > 0) {
      if (Clusters == 1) {
          LogMessage(L"%s\n  Verschieben eines Blocks von %I64d nach %I64d, VCN=%I64d.",
            Item->LongPath,FromLcn,ToLcn,FromVcn);
        } else {
          LogMessage(L"%s\n  Verschieben von %I64d Blöcken von %I64d nach %I64d, VCN=%I64d.",
            Item->LongPath,Clusters,FromLcn,ToLcn,FromVcn);
          }
    } else {
      if (Clusters == 1) {
          LogMessage(L"%s\n  Verschieben eines Blocks von %I64d nach %I64d.",
            Item->LongPath,FromLcn,ToLcn);
        } else {
          LogMessage(L"%s\n  Verschieben von %I64d Blöcken von %I64d nach %I64d.",
            Item->LongPath,Clusters,FromLcn,ToLcn);
          }
      }
  }




/* Callback: for every file during analysis.
   This subroutine is called one last time with Item=NULL when analysis has
   finished. */
void __cdecl ShowAnalyze(struct DefragDataStruct *Data, struct ItemStruct *Item) {

  if ((Data != NULL) && (Data->CountAllFiles != 0)) {
      swprintf_s(Messages[3],50000,L"Datei %I64d, Verzeichnis %I64d, Block %I64d",
        Data->CountAllFiles,Data->CountDirectories,Data->CountAllClusters);
    } else {
      swprintf_s(Messages[3],50000,L"Anwenden der Ausschluss- und SpaceHogs-Masken...");
      }

  /* Save the name of the file in Messages 4. */
  if ((Item != NULL) && (Item->LongPath != NULL)) {
      swprintf_s(Messages[4],50000,L"%s",Item->LongPath);
    } else {
      *Messages[4] = '\0';
      }
  }




/* Callback: show a debug message. */
void __cdecl ShowDebug(int Level, struct ItemStruct *Item, WCHAR *Format, ...) {
  va_list VarArgs;
  FILE *Fout;
  int Result;
  time_t Now;
  struct tm NowTm;

  if (Debug < Level) return;

  /* Save the name of the file in Messages 4. */
  if ((Item != NULL) && (Item->LongPath != NULL)) {
    swprintf_s(Messages[4],50000,L"%s",Item->LongPath);
    }

  /* If there is no message then return. */
  if (Format == NULL) return;

  /* Save the debug message in Messages 5. */
  va_start(VarArgs,Format);
  vswprintf_s(Messages[5],50000,Format,VarArgs);
  va_end(VarArgs);

  /* If there is no logfile then return. */
  if (*LogFile == '\0') return;

  /* Open the logfile. */
  Result = _wfopen_s(&Fout,LogFile,L"a, ccs=UTF-8");
  if ((Result != 0) || (Fout == NULL)) return;

  /* Write to the logfile. */
  time(&Now);
  Result = localtime_s(&NowTm,&Now);
  fwprintf_s(Fout,L"%02lu:%02lu:%02lu ",NowTm.tm_hour,NowTm.tm_min,NowTm.tm_sec);
  if ((Item != NULL) && (Item->LongPath != NULL)) fwprintf_s(Fout,L"%s\n  ",Item->LongPath);
  va_start(VarArgs,Format);
  vfwprintf_s(Fout,Format,VarArgs);
  va_end(VarArgs);
  fwprintf_s(Fout,L"\n");

  /* Close the logfile. */
  fflush(Fout);
  fclose(Fout);
  }




/* Callback: paint a cluster on the screen in a color. */
void __cdecl DrawCluster(
    struct DefragDataStruct *Data,
    ULONG64 ClusterStart,
    ULONG64 ClusterEnd,
    int Color) {
  struct __timeb64 Now;
  ULONG64 Width;
  ULONG64 Height;
  ULONG64 TopHeight;
  ULONG64 x1;
  ULONG64 y1;
  ULONG64 x2;
  ULONG64 y2;

  /* Save the PhaseTodo and PhaseDone counters for later use by the progress
     counter. */
  if (Data->PhaseTodo != 0) {
    _ftime64_s(&Now);
    ProgressTime = Now.time * 1000 + Now.millitm;
    ProgressDone = Data->PhaseDone;
    ProgressTodo = Data->PhaseTodo;
    }

  /* Sanity check. */
  if (Data->TotalClusters == 0) return;
  if (WindowDC == NULL) return;
  if (ClusterStart == ClusterEnd) return;

  /* Reserve some whitespace at the top for progress messages. */
  TopHeight = 33;
  if (Debug > 1) TopHeight = 48;

  /* Setup variables. */
  if (WindowSize.right <= 0) return;
  Width = WindowSize.right;
  if (WindowSize.bottom <= TopHeight) return;
  Height = WindowSize.bottom - TopHeight;
  x1 = (ClusterStart * Width * Height / Data->TotalClusters ) % Width;
  y1 = (ClusterStart * Width * Height / Data->TotalClusters ) / Width;
  x2 = (ClusterEnd * Width * Height / Data->TotalClusters ) % Width;
  y2 = (ClusterEnd * Width * Height / Data->TotalClusters ) / Width;

  /* Draw the clusters on the virtual screen. */
  WaitForSingleObject(DisplayMutex,1000);
  SelectObject(WindowDC,GetStockObject(DC_PEN));
  SetDCPenColor(WindowDC,Colors[Color]);
  while (y1 < y2) {
    if (MoveToEx(WindowDC,(int)x1,(int)(WindowSize.bottom - y1 - 1),NULL) != 0) {
      LineTo(WindowDC,WindowSize.right,(int)(WindowSize.bottom - y1 - 1));
      }
    x1 = 0;
    y1 = y1 + 1;
    }
  if (MoveToEx(WindowDC,(int)x1,(int)(WindowSize.bottom - y1 - 1),NULL) != 0) {
    LineTo(WindowDC,(int)x2,(int)(WindowSize.bottom - y2 - 1));
    }
  ReleaseMutex(DisplayMutex);
  }




/* Callback: just before the defragger starts a new Phase, and when it finishes. */
void __cdecl ShowStatus(struct DefragDataStruct *Data) {
  struct ItemStruct *Item;
  int Fragments;
  ULONG64 TotalFragments;
  ULONG64 TotalBytes;
  ULONG64 TotalClusters;
  struct ItemStruct *LargestItems[25];
  int LastLargest;
  struct __timeb64 Now;
  int i;
  int j;

  /* Reset the progress counter. */
  _ftime64_s(&Now);
  ProgressStartTime = Now.time * 1000 + Now.millitm;
  ProgressTime = ProgressStartTime;
  ProgressDone = 0;
  ProgressTodo = 0;

  /* Reset all the messages. */
  for (i = 0; i < 6; i++) *Messages[i] = '\0';

  /* Update Message 0 and 1. */
  if (Data != NULL) {
    swprintf_s(Messages[0],50000,L"Lw %s",Data->Disk.MountPoint);
    switch(Data->Phase) {
      case 1: wcscpy_s(Messages[1],50000,L"Schritt 1: Analysieren"); break;
      case 2: wcscpy_s(Messages[1],50000,L"Schritt 2: Defragmentieren"); break;
      case 3: wcscpy_s(Messages[1],50000,L"Schritt 3: Zwangsfüllung"); break;
      case 4: swprintf_s(Messages[1],50000,L"Zone %u: Sortieren",Data->Zone + 1); break;
      case 5: swprintf_s(Messages[1],50000,L"Zone %u: Optimieren",Data->Zone + 1); break;
      case 6: wcscpy_s(Messages[1],50000,L"Schritt 3: Nach oben schieben"); break;
      case 7:
        wcscpy_s(Messages[1],50000,L"Beendet.");
        swprintf_s(Messages[4],50000,L"Protokolldatei: %s",LogFile);
        break;
      case 8: wcscpy_s(Messages[1],50000,L"Schritt 3: Neuverteilung"); break;
      }
    LogMessage(Messages[1]);
    }

  /* Write some statistics to the logfile. */
  if ((Data != NULL) && (Data->Phase == 7)) {
    LogMessage(L"- Gesamte Festplattengröße: %I64d Bytes (%.04f GB), %I64d Blöcke",
      Data->BytesPerCluster * Data->TotalClusters,
      (double)(Data->BytesPerCluster * Data->TotalClusters) / (1024 * 1024 * 1024),
      Data->TotalClusters);
    LogMessage(L"- Bytes je Block: %I64d Bytes",Data->BytesPerCluster);

    LogMessage(L"- Anzahl der Dateien: %I64d",Data->CountAllFiles);
    LogMessage(L"- Anzahl der Verzeichnisse: %I64d",Data->CountDirectories);
    LogMessage(L"- Gesamtgröße der analysierten Einheiten: %I64d Bytes (%.04f GB), %I64d Blöcke",
      Data->CountAllClusters * Data->BytesPerCluster,
      (double)(Data->CountAllClusters * Data->BytesPerCluster) / (1024 * 1024 * 1024),
      Data->CountAllClusters);
    if (Data->CountAllFiles + Data->CountDirectories > 0) {
        LogMessage(L"- Anzahl der fragmentierten Einheiten: %I64d (%.04f%% aller Einheiten)",
          Data->CountFragmentedItems,
          (double)(Data->CountFragmentedItems * 100) / (Data->CountAllFiles + Data->CountDirectories));
      } else {
        LogMessage(L"- Anzahl der fragmentierten Einheiten: %I64d",Data->CountFragmentedItems);
        }
    if ((Data->CountAllClusters > 0) && (Data->TotalClusters > 0)) {
        LogMessage(L"- Gesamtgröße der fragmentierten Einheiten: %I64d Bytes, %I64d Blöcke, (%.04f%% aller Einheiten bzw. %.04f%% der Festplatte)",
          Data->CountFragmentedClusters * Data->BytesPerCluster,
          Data->CountFragmentedClusters,
          (double)(Data->CountFragmentedClusters * 100) / Data->CountAllClusters,
          (double)(Data->CountFragmentedClusters * 100) / Data->TotalClusters);
      } else {
        LogMessage(L"- Gesamtgröße der fragmentierten Einheiten: %I64d Bytes, %I64d Blöcke",
          Data->CountFragmentedClusters * Data->BytesPerCluster,
          Data->CountFragmentedClusters);
        }

    if (Data->TotalClusters > 0) {
        LogMessage(L"- Freier Festplattenplatz: %I64d Bytes, %I64d Blöcke (%.04f%% der Festplatte)",
          Data->CountFreeClusters * Data->BytesPerCluster,
          Data->CountFreeClusters,
          (double)(Data->CountFreeClusters * 100) / Data->TotalClusters);
      } else {
        LogMessage(L"- Freier Festplattenplatz: %I64d Bytes, %I64d Blöcke",
          Data->CountFreeClusters * Data->BytesPerCluster,
          Data->CountFreeClusters);
        }
    LogMessage(L"- Anzahl der Lücken: %I64d",Data->CountGaps);
    if (Data->CountGaps > 0) {
        LogMessage(L"- Anzahl der kleinen Lücken: %I64d (%.04f%% aller Lücken)",
          Data->CountGapsLess16,
          (double)(Data->CountGapsLess16 * 100) / Data->CountGaps);
      } else {
        LogMessage(L"- Anzahl der kleinen Lücken: %I64d",
          Data->CountGapsLess16);
        }
    if (Data->CountFreeClusters > 0) {
        LogMessage(L"- Größe der kleinen Lücken: %I64d Bytes, %I64d Blöcke (%.04f%% des freien Festplattenplatzes)",
          Data->CountClustersLess16 * Data->BytesPerCluster,
          Data->CountClustersLess16,
          (double)(Data->CountClustersLess16 * 100) / Data->CountFreeClusters);
      } else {
        LogMessage(L"- Größe der kleinen Lücken: %I64d Bytes, %I64d Blöcke",
          Data->CountClustersLess16 * Data->BytesPerCluster,
          Data->CountClustersLess16);
        }
    if (Data->CountGaps > 0) {
        LogMessage(L"- Anzahl der großen Lücken: %I64d (%.04f%% aller Lücken)",
          Data->CountGaps - Data->CountGapsLess16,
          (double)((Data->CountGaps - Data->CountGapsLess16) * 100) / Data->CountGaps);
      } else {
        LogMessage(L"- Anzahl der großen Lücken: %I64d",
          Data->CountGaps - Data->CountGapsLess16);
        }
    if (Data->CountFreeClusters > 0) {
        LogMessage(L"- Größe der großen Lücken: %I64d Bytes, %I64d Blöcke (%.04f%% des freien Festplattenplatzes)",
          (Data->CountFreeClusters - Data->CountClustersLess16) * Data->BytesPerCluster,
          Data->CountFreeClusters - Data->CountClustersLess16,
          (double)((Data->CountFreeClusters - Data->CountClustersLess16) * 100) / Data->CountFreeClusters);
      } else {
        LogMessage(L"- Größe der großen Lücken: %I64d Bytes, %I64d Blöcke",
          (Data->CountFreeClusters - Data->CountClustersLess16) * Data->BytesPerCluster,
          Data->CountFreeClusters - Data->CountClustersLess16);
        }
    if (Data->CountGaps > 0) {
      LogMessage(L"- Durchschnittliche Lückengröße: %.04f Blöcke",
        (double)(Data->CountFreeClusters) / Data->CountGaps);
      }
    if (Data->CountFreeClusters > 0) {
        LogMessage(L"- Größte Lücke: %I64d Bytes, %I64d Blöcke (%.04f%% des freien Festplattenplatzes)",
          Data->BiggestGap * Data->BytesPerCluster,
          Data->BiggestGap,
          (double)(Data->BiggestGap * 100) / Data->CountFreeClusters);
      } else {
        LogMessage(L"- Größte Lücke: %I64d Bytes, %I64d Blöcke",
          Data->BiggestGap * Data->BytesPerCluster,
          Data->BiggestGap);
        }
    if (Data->TotalClusters > 0) {
        LogMessage(L"- Durchschnittliche Ende-Anfang Entfernung: %.0f Blöcke (%.4f%% der Datenträgergröße)",
          Data->AverageDistance,100.0 * Data->AverageDistance / Data->TotalClusters);
      } else {
        LogMessage(L"- Durchschnittliche Ende-Anfang Entfernung: %.0f Blöcke",Data->AverageDistance);
        }

    for (Item = TreeSmallest(Data->ItemTree); Item != NULL; Item = TreeNext(Item)) {
      if (Item->Unmovable != YES) continue;
      if (Item->Exclude == YES) continue;
      if ((Item->Directory == YES) && (Data->CannotMoveDirs > 20)) continue;
      break;
      }
    if (Item != NULL) {
      LogMessage(L"Diese Teile können nicht verschoben werden:");
      LogMessage(L"  Fragmente       Bytes    Blöcke Dateiname mit Pfad");
      TotalFragments = 0;
      TotalBytes = 0;
      TotalClusters = 0;
      for (Item = TreeSmallest(Data->ItemTree); Item != NULL; Item = TreeNext(Item)) {
        if (Item->Unmovable != YES) continue;
        if (Item->Exclude == YES) continue;
        if ((Item->Directory == YES) && (Data->CannotMoveDirs > 20)) continue;
        if ((Item->LongFilename != NULL) &&
            ((_wcsicmp(Item->LongFilename,L"$BadClus") == 0) ||
             (_wcsicmp(Item->LongFilename,L"$BadClus:$Bad:$DATA") == 0))) continue;
        Fragments = FragmentCount(Item);
        if (Item->LongPath == NULL) {
            LogMessage(L"  %9lu %11I64u %9I64u [bei Block %I64u]",Fragments,Item->Bytes,Item->Clusters,
              GetItemLcn(Item));
          } else {
            LogMessage(L"  %9lu %11I64u %9I64u %s",Fragments,Item->Bytes,Item->Clusters,Item->LongPath);
            }
        TotalFragments = TotalFragments + Fragments;
        TotalBytes = TotalBytes + Item->Bytes;
        TotalClusters = TotalClusters + Item->Clusters;
        }
      LogMessage(L"  --------- ----------- --------- ---------");
      LogMessage(L"  %9I64u %11I64u %9I64u Insgesamt",TotalFragments,TotalBytes,TotalClusters);
      }

    for (Item = TreeSmallest(Data->ItemTree); Item != NULL; Item = TreeNext(Item)) {
      if (Item->Exclude == YES) continue;
      if ((Item->Directory == YES) && (Data->CannotMoveDirs > 20)) continue;
      Fragments = FragmentCount(Item);
      if (Fragments <= 1) continue;
      break;
      }
    if (Item != NULL) {
      LogMessage(L"Diese Teile sind noch fragmentiert:");
      LogMessage(L"  Fragmente       Bytes    Blöcke Dateiname mit Pfad");
      TotalFragments = 0;
      TotalBytes = 0;
      TotalClusters = 0;
      for (Item = TreeSmallest(Data->ItemTree); Item != NULL; Item = TreeNext(Item)) {
        if (Item->Exclude == YES) continue;
        if ((Item->Directory == YES) && (Data->CannotMoveDirs > 20)) continue;
        Fragments = FragmentCount(Item);
        if (Fragments <= 1) continue;
        if (Item->LongPath == NULL) {
            LogMessage(L"  %9lu %11I64u %9I64u [bei Block %I64u]",Fragments,Item->Bytes,Item->Clusters,
              GetItemLcn(Item));
          } else {
            LogMessage(L"  %9lu %11I64u %9I64u %s",Fragments,Item->Bytes,Item->Clusters,Item->LongPath);
            }
        TotalFragments = TotalFragments + Fragments;
        TotalBytes = TotalBytes + Item->Bytes;
        TotalClusters = TotalClusters + Item->Clusters;
        }
      LogMessage(L"  --------- ----------- --------- ---------");
      LogMessage(L"  %9I64u %11I64u %9I64u Insgesamt",TotalFragments,TotalBytes,TotalClusters);
      }

    LastLargest = 0;
    for (Item = TreeSmallest(Data->ItemTree); Item != NULL; Item = TreeNext(Item)) {
      if ((Item->LongFilename != NULL) &&
          ((_wcsicmp(Item->LongFilename,L"$BadClus") == 0) ||
           (_wcsicmp(Item->LongFilename,L"$BadClus:$Bad:$DATA") == 0))) {
        continue;
        }
      for (i = LastLargest - 1; i >= 0; i--) {
        if (Item->Clusters < LargestItems[i]->Clusters) break;
        if ((Item->Clusters == LargestItems[i]->Clusters) &&
            (Item->Bytes < LargestItems[i]->Bytes)) break;
        if ((Item->Clusters == LargestItems[i]->Clusters) &&
            (Item->Bytes == LargestItems[i]->Bytes) &&
            (Item->LongPath != NULL) &&
            (LargestItems[i]->LongPath != NULL) &&
            (_wcsicmp(Item->LongPath,LargestItems[i]->LongPath) > 0)) break;
        }
      if (i < 24) {
        if (LastLargest < 25) LastLargest++;
        for (j = LastLargest - 1; j > i + 1; j--) {
          LargestItems[j] = LargestItems[j-1];
          }
        LargestItems[i + 1] = Item;
        }
      }
    if (LastLargest > 0) {
      LogMessage(L"Die 25 größten Dateien des Laufwerks:");
      LogMessage(L"  Fragmente       Bytes    Blöcke Dateiname mit Pfad");
      for (i = 0; i < LastLargest; i++) {
        if (LargestItems[i]->LongPath == NULL) {
            LogMessage(L"  %9u %11I64u %9I64u [bei Block %I64u]",FragmentCount(LargestItems[i]),
              LargestItems[i]->Bytes,LargestItems[i]->Clusters,GetItemLcn(LargestItems[i]));
          } else {
            LogMessage(L"  %9u %11I64u %9I64u %s",FragmentCount(LargestItems[i]),
              LargestItems[i]->Bytes,LargestItems[i]->Clusters,LargestItems[i]->LongPath);
            }
        }
      }
    }
  }





#ifdef _DEBUG
/* Write a crash report to the log.
   To test the crash handler add something like this:
     char *p1;
     p1 = 0;
     *p1 = 0;
   */
LONG __stdcall CrashReport(EXCEPTION_POINTERS *ExceptionInfo) {
  IMAGEHLP_LINE64 SourceLine;
  DWORD LineDisplacement;
  STACKFRAME64 StackFrame;
  DWORD ImageType;
  BOOL Result;
  int FrameNumber;
  char s1[BUFSIZ];
  WCHAR s2[BUFSIZ];

  /* Exit if we're running inside a debugger. */
//  if (IsDebuggerPresent() == TRUE) return(EXCEPTION_EXECUTE_HANDLER);

  LogMessage(L"Programmabsturz!");
  LogMessage(L"  in Befehlszeile: %s",GetCommandLineW());

  /* Show the type of exception. */
  switch(ExceptionInfo->ExceptionRecord->ExceptionCode) {
    case EXCEPTION_ACCESS_VIOLATION         : strcpy_s(s1,BUFSIZ,"ACCESS_VIOLATION (der Speicher kann nicht gelesen oder beschrieben werden)"); break;
    case EXCEPTION_DATATYPE_MISALIGNMENT    : strcpy_s(s1,BUFSIZ,"DATATYPE_MISALIGNMENT (ein Ausrichtungsfehler eines Datentyps wurde in einer Lade- oder Speicheranweisung festgestellt)"); break;
    case EXCEPTION_BREAKPOINT               : strcpy_s(s1,BUFSIZ,"BREAKPOINT"); break;
    case EXCEPTION_SINGLE_STEP              : strcpy_s(s1,BUFSIZ,"SINGLE_STEP"); break;
    case EXCEPTION_ARRAY_BOUNDS_EXCEEDED    : strcpy_s(s1,BUFSIZ,"ARRAY_BOUNDS_EXCEEDED"); break;
    case EXCEPTION_FLT_DENORMAL_OPERAND     : strcpy_s(s1,BUFSIZ,"FLT_DENORMAL_OPERAND"); break;
    case EXCEPTION_FLT_DIVIDE_BY_ZERO       : strcpy_s(s1,BUFSIZ,"FLT_DIVIDE_BY_ZERO"); break;
    case EXCEPTION_FLT_INEXACT_RESULT       : strcpy_s(s1,BUFSIZ,"FLT_INEXACT_RESULT"); break;
    case EXCEPTION_FLT_INVALID_OPERATION    : strcpy_s(s1,BUFSIZ,"FLT_INVALID_OPERATION"); break;
    case EXCEPTION_FLT_OVERFLOW             : strcpy_s(s1,BUFSIZ,"FLT_OVERFLOW"); break;
    case EXCEPTION_FLT_STACK_CHECK          : strcpy_s(s1,BUFSIZ,"FLT_STACK_CHECK"); break;
    case EXCEPTION_FLT_UNDERFLOW            : strcpy_s(s1,BUFSIZ,"FLT_UNDERFLOW"); break;
    case EXCEPTION_INT_DIVIDE_BY_ZERO       : strcpy_s(s1,BUFSIZ,"INT_DIVIDE_BY_ZERO"); break;
    case EXCEPTION_INT_OVERFLOW             : strcpy_s(s1,BUFSIZ,"INT_OVERFLOW"); break;
    case EXCEPTION_PRIV_INSTRUCTION         : strcpy_s(s1,BUFSIZ,"PRIV_INSTRUCTION"); break;
    case EXCEPTION_IN_PAGE_ERROR            : strcpy_s(s1,BUFSIZ,"IN_PAGE_ERROR"); break;
    case EXCEPTION_ILLEGAL_INSTRUCTION      : strcpy_s(s1,BUFSIZ,"ILLEGAL_INSTRUCTION"); break;
    case EXCEPTION_NONCONTINUABLE_EXCEPTION : strcpy_s(s1,BUFSIZ,"NONCONTINUABLE_EXCEPTION"); break;
    case EXCEPTION_STACK_OVERFLOW           : strcpy_s(s1,BUFSIZ,"STACK_OVERFLOW"); break;
    case EXCEPTION_INVALID_DISPOSITION      : strcpy_s(s1,BUFSIZ,"INVALID_DISPOSITION"); break;
    case EXCEPTION_GUARD_PAGE               : strcpy_s(s1,BUFSIZ,"GUARD_PAGE"); break;
    case EXCEPTION_INVALID_HANDLE           : strcpy_s(s1,BUFSIZ,"INVALID_HANDLE"); break;
    case CONTROL_C_EXIT                     : strcpy_s(s1,BUFSIZ,"STATUS_CONTROL_C_EXIT"); break;
    case DBG_TERMINATE_THREAD               : strcpy_s(s1,BUFSIZ,"DBG_TERMINATE_THREAD (Debugger terminated thread)"); break;
    case DBG_TERMINATE_PROCESS              : strcpy_s(s1,BUFSIZ,"DBG_TERMINATE_PROCESS (Debugger terminated process)"); break;
    case DBG_CONTROL_C                      : strcpy_s(s1,BUFSIZ,"DBG_CONTROL_C (Debugger got control C)"); break;
    case DBG_CONTROL_BREAK                  : strcpy_s(s1,BUFSIZ,"DBG_CONTROL_BREAK (Debugger received control break)"); break;
    case DBG_COMMAND_EXCEPTION              : strcpy_s(s1,BUFSIZ,"DBG_COMMAND_EXCEPTION (Debugger command communication exception)"); break;
    default                                 : strcpy_s(s1,BUFSIZ,"(unknown exception)");
    }
  LogMessage(L"  Ausnahmefehler: %S",s1);

  /* Try to show the linenumber of the sourcefile. */
  SymSetOptions(SymGetOptions() || SYMOPT_LOAD_LINES);
  Result = SymInitialize(GetCurrentProcess(),NULL,TRUE);
  if (Result == FALSE) {
    SystemErrorStr(GetLastError(),s2,BUFSIZ);
    LogMessage(L"  Initialisieren von SymInitialize() ist fehlgeschlagen: %s",s2);
    return(EXCEPTION_EXECUTE_HANDLER);
    }
  ZeroMemory(&StackFrame,sizeof(StackFrame));
#ifdef _M_IX86
  ImageType = IMAGE_FILE_MACHINE_I386;
  StackFrame.AddrPC.Offset = ExceptionInfo->ContextRecord->Eip;
  StackFrame.AddrPC.Mode = AddrModeFlat;
  StackFrame.AddrFrame.Offset = ExceptionInfo->ContextRecord->Ebp;
  StackFrame.AddrFrame.Mode = AddrModeFlat;
  StackFrame.AddrStack.Offset = ExceptionInfo->ContextRecord->Esp;
  StackFrame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
  ImageType = IMAGE_FILE_MACHINE_AMD64;
  StackFrame.AddrPC.Offset = ExceptionInfo->ContextRecord->Rip;
  StackFrame.AddrPC.Mode = AddrModeFlat;
  StackFrame.AddrFrame.Offset = ExceptionInfo->ContextRecord->Rsp;
  StackFrame.AddrFrame.Mode = AddrModeFlat;
  StackFrame.AddrStack.Offset = ExceptionInfo->ContextRecord->Rsp;
  StackFrame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
  ImageType = IMAGE_FILE_MACHINE_IA64;
  StackFrame.AddrPC.Offset = ExceptionInfo->ContextRecord->StIIP;
  StackFrame.AddrPC.Mode = AddrModeFlat;
  StackFrame.AddrFrame.Offset = ExceptionInfo->ContextRecord->IntSp;
  StackFrame.AddrFrame.Mode = AddrModeFlat;
  StackFrame.AddrBStore.Offset = ExceptionInfo->ContextRecord->RsBSP;
  StackFrame.AddrBStore.Mode = AddrModeFlat;
  StackFrame.AddrStack.Offset = ExceptionInfo->ContextRecord->IntSp;
  StackFrame.AddrStack.Mode = AddrModeFlat;
#endif
  for (FrameNumber = 1; ; FrameNumber++) {
    Result = StackWalk64(ImageType,GetCurrentProcess(),GetCurrentThread(),&StackFrame,
      ExceptionInfo->ContextRecord,NULL,SymFunctionTableAccess64,SymGetModuleBase64,NULL);
    if (Result == FALSE) break;
    if (StackFrame.AddrPC.Offset == StackFrame.AddrReturn.Offset) break;
    if (StackFrame.AddrPC.Offset != 0) {
      LineDisplacement = 0;
      ZeroMemory(&SourceLine,sizeof(SourceLine));
      SourceLine.SizeOfStruct = sizeof(SourceLine);
      Result = SymGetLineFromAddr64(GetCurrentProcess(),StackFrame.AddrPC.Offset,
        &LineDisplacement,&SourceLine);
      if (Result == TRUE) {
          LogMessage(L"  %i. Bei Zeile %d in '%S'",FrameNumber,SourceLine.LineNumber,SourceLine.FileName);
        } else {
          LogMessage(L"  %i. Bei Zeile (unbekannt) in (unbekannt)",FrameNumber);
/*
          SystemErrorStr(GetLastError(),s2,BUFSIZ);
          LogMessage(L"  Error executing SymGetLineFromAddr64(): %s",s2);
*/
          }
      }
    }

  /* Possible return values:
       EXCEPTION_CONTINUE_SEARCH    = popup a window about the error, user has to click.
       EXCEPTION_CONTINUE_EXECUTION = infinite loop
       EXCEPTION_EXECUTE_HANDLER    = stop program, do not run debugger
     */
  return(EXCEPTION_EXECUTE_HANDLER);
  }
#endif






/* If the defragger is not yet running then return NO. If it's already
   running or if there was an error getting the processlist then return
   YES. */
int AlreadyRunning(void) {
  HANDLE Snapshot;
  PROCESSENTRY32 pe32;
  DWORD MyPid;
  char MyName[MAX_PATH];
  WCHAR s1[BUFSIZ];
  WCHAR s2[BUFSIZ];

  /* Get a process-snapshot from the kernel. */
  Snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
  if (Snapshot == INVALID_HANDLE_VALUE) {
    SystemErrorStr(GetLastError(),s1,BUFSIZ);
    swprintf_s(s2,BUFSIZ,L"Keine Ablaufmomentaufnahme möglich: %s",s1);
    ShowDebug(0,NULL,s2);
    return(YES);
    }
  pe32.dwSize = sizeof(PROCESSENTRY32);

  /* Get my own executable name. */
  MyPid = GetCurrentProcessId();
  *MyName = '\0';
  if (Process32First(Snapshot,&pe32) != FALSE) do {
    if (MyPid == pe32.th32ProcessID) {
      strcpy_s(MyName,MAX_PATH,pe32.szExeFile);
      break;
      }
    } while (Process32Next(Snapshot,&pe32));
  if (*MyName == '\0') {
    /* "Cannot find my own name in the process list: %s" */
    swprintf_s(s1,BUFSIZ,L"Der eigene Name kann nicht in der Ablaufliste gefunden werden: %s",MyName);
    ShowDebug(0,NULL,s1);
    return(YES);
    }

  /* Search for any other process with the same executable name as
     myself. If found then return YES. */
  Process32First(Snapshot,&pe32);
  do {
    if (MyPid == pe32.th32ProcessID) continue;          /* Ignore myself. */
    if ((_stricmp(pe32.szExeFile,MyName) == 0) ||
        (_stricmp(pe32.szExeFile,"jkdefrag.exe") == 0) ||
        (_stricmp(pe32.szExeFile,"jkdefragscreensaver.exe") == 0) ||
        (_stricmp(pe32.szExeFile,"jkdefragcmd.exe") == 0)) {
      CloseHandle(Snapshot);
      swprintf_s(s1,BUFSIZ,L"Läuft bereits: %S",pe32.szExeFile);
      ShowDebug(0,NULL,s1);
      return(YES);
      }
    } while (Process32Next(Snapshot,&pe32));

  /* Return NO, not yet running. */
  CloseHandle(Snapshot);
  return(NO);
  }




/* Read a JkDefrag setting from the registry. For example:
      GetValueFromRegistry(L"AlternateScreenSaver",Alternate);
   */
void GetValueFromRegistry(WCHAR *Name, WCHAR *Output) {
  HKEY Key;
  DWORD KeyDisposition;
  int Result;
  DWORD Length;
  WCHAR s1[BUFSIZ];
  WCHAR s2[BUFSIZ];

  *Output = 0;
  Result = RegCreateKeyExW(HKEY_CURRENT_USER,L"Control Panel\\Screen Saver.JkDefrag",0,
    NULL,REG_OPTION_NON_VOLATILE,KEY_READ,NULL,&Key,&KeyDisposition);
  if (Result == ERROR_SUCCESS) {
    Length = sizeof(WCHAR) * BUFSIZ;
    Result = RegQueryValueExW(Key,Name,NULL,NULL,(BYTE *)Output,&Length);
    RegCloseKey(Key);
    }
  if (Result != ERROR_SUCCESS) {
    *Output = 0;
    SystemErrorStr(GetLastError(),s1,BUFSIZ);
    swprintf_s(s2,BUFSIZ,L"Getting key \"Control Panel\\Screen Saver.JkDefrag\\%s\": %s",
      Name,s1);
    ShowDebug(3,NULL,s2);
    return;
    }

  swprintf_s(s1,BUFSIZ,L"Read \"Control Panel\\Screen Saver.JkDefrag\\%s\" = \"%s\"",
    Name,Output);
  ShowDebug(3,NULL,s1);
  }




/* Write a value to the registry. */
void SaveValueInRegistry(WCHAR *Name, WCHAR *Value) {
  HKEY Key;
  DWORD KeyDisposition;
  int Result;
  DWORD Length;
  WCHAR s1[BUFSIZ];
  WCHAR s2[BUFSIZ];

  Result = RegCreateKeyExW(HKEY_CURRENT_USER,L"Control Panel\\Screen Saver.JkDefrag",0,
    NULL,REG_OPTION_NON_VOLATILE,KEY_WRITE,NULL,&Key,&KeyDisposition);
  if (Result == ERROR_SUCCESS) {
    Length = (DWORD)(sizeof(WCHAR) * (wcslen(Value) + 1));
    Result = RegSetValueExW(Key,Name,0,REG_SZ,(BYTE *)Value,Length);
    RegCloseKey(Key);
    }
  if (Result != ERROR_SUCCESS) {
    SystemErrorStr(GetLastError(),s1,BUFSIZ);
    swprintf_s(s2,BUFSIZ,L"Writing key \"Control Panel\\Screen Saver.JkDefrag\\%s\": %s",
      Name,s1);
    ShowDebug(3,NULL,s2);
    }
  ShowDebug(3,NULL,L"Write \"Control Panel\\Screen Saver.JkDefrag\\%s\" = \"%s\"",
    Name,Value);
  }




/* The main thread that performs all the work. Interpret the commandline
   parameters and call the defragger library. */
DWORD WINAPI DefragThread(LPVOID) {
  struct __timeb64 Time;
  LONG64 NowL;
  LONG64 LastRun;
  LONG64 SecondsToWait;
  WCHAR CommandLine[BUFSIZ];
  WCHAR Alternate[BUFSIZ];
  int OptimizeMode;                /* 1...4 */
  int Speed;                       /* 0...100 */
  double FreeSpace;                /* 0...100 */
  WCHAR **Excludes;
  WCHAR **SpaceHogs;
  int DoAllVolumes;
  LPWSTR *argv;
  int argc;
  time_t Now;
  struct tm NowTm;
  OSVERSIONINFO OsVersion;
  WCHAR w1[BUFSIZ];
  int i;

  /* Fetch the alternate screensaver setting from the registry. */
  GetValueFromRegistry(L"AlternateScreenSaver",Alternate);
  if ((*Alternate != '\0') &&
      ((_wcsicmp(Alternate,MyName1) == 0) ||
       (_wcsicmp(Alternate,MyName2) == 0) ||
       (_wcsicmp(Alternate,MyShortName1) == 0) ||
       (_wcsicmp(Alternate,MyShortName2) == 0))) *Alternate = '\0';

  /* Set the IamRunning flag to RUNNING. */
  IamRunning = RUNNING;

  /* Fetch the StatusBar setting. */
  GetValueFromRegistry(L"StatusBar",w1);
  StatusBar = 0;
  if (_wcsicmp(w1,L"No status bar") == 0) StatusBar = 2;
  if (_wcsicmp(w1,L"Top line only (no filenames)") == 0) StatusBar = 1;
  if (_wcsicmp(w1,L"Full status bar") == 0) StatusBar = 0;

  /* If the LastRun was less than HoursToWait ago then exit this thread. */
  LastRun = 0;
  GetValueFromRegistry(L"LastRun",w1);
  if (*w1 != '\0') LastRun = _wtoi64(w1);
  SecondsToWait = 4 * 3600;
  GetValueFromRegistry(L"HoursToWait",w1);
  if (*w1 != '\0') SecondsToWait = _wtoi64(w1) * 3600;
  _ftime64_s(&Time);
  NowL = Time.time;
  if (LastRun > NowL) {
    /* The LastRun is impossible, resetting it to zero. */
    SaveValueInRegistry(L"LastRun",L"0");
    LastRun = 0;
    }
  if (NowL - LastRun < SecondsToWait) {
    LogMessage(L"Der letzte Lauf ist %I64u Sekunden her, das sind weniger als %I64u Sekunden (%I64u Stunden), somit wird nicht gestartet.",
      NowL - LastRun,SecondsToWait,SecondsToWait / 3600);
    if (*Alternate != '\0') {
      LogMessage(L"Starte den alternativen Bildschirmschoner: %s",Alternate);
      exit(1);
      }
    LogMessage(L"Es ist kein alternativer Bildschirmschoner eingestellt, somit wird ein schwarzer Bildschirm angezeigt.");
    IamRunning = STOPPED;
    Running = STOPPED;
    return(0);
    }

  /* Setup the defaults. */
  OptimizeMode = 2;
  Speed = 100;
  FreeSpace = 1;
  Excludes = NULL;
  SpaceHogs = NULL;

  /* Fetch the commandline. */
  GetValueFromRegistry(L"CommandLine",CommandLine);
  argv = CommandLineToArgvW(CommandLine,&argc);

  /* Scan the commandline arguments for "-l" and setup the logfile. */
  if ((*CommandLine != 0) && (argc > 0)) {
    for (i = 0; i < argc; i++) {
      if (wcscmp(argv[i],L"-l") == 0) {
        i++;
        if (i >= argc) continue;
        swprintf_s(LogFile,MAX_PATH,L"%s",argv[i]);
        continue;
        }
      if ((wcsncmp(argv[i],L"-l",2) == 0) && (wcslen(argv[i]) >= 3)) {
        swprintf_s(LogFile,MAX_PATH,L"%s",&argv[i][2]);
        continue;
        }
      }
    }
  if (*LogFile != '\0') _wunlink(LogFile);          /* Delete old logfile. */
  LogMessage(VERSIONTEXT);
  time(&Now);
  localtime_s(&NowTm,&Now);
  LogMessage(L"Date: %04lu/%02lu/%02lu",1900 + NowTm.tm_year,1 + NowTm.tm_mon,NowTm.tm_mday);

  ZeroMemory(&OsVersion,sizeof(OSVERSIONINFO));
  OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  if (GetVersionEx(&OsVersion) != 0) {
    LogMessage(L"Windows-Version: v%lu.%lu Build %lu %S",OsVersion.dwMajorVersion,
      OsVersion.dwMinorVersion,OsVersion.dwBuildNumber,OsVersion.szCSDVersion);
    }

  if (LastRun > 0) {
      LogMessage(L"Der letzte Lauf ist %I64u Sekunden her, das ist mehr als %I64u Sekunden (%I64u Stunden) her.",
        NowL - LastRun,SecondsToWait,SecondsToWait / 3600);
    } else {
      LogMessage(L"Die letzter Lauf Eingabe ist leer, die Einstellung ist %I64u Sekunden (%I64u Stunden).",
        SecondsToWait,SecondsToWait / 3600);
      }

  /* Scan the commandline again for all the other arguments. */
  if ((*CommandLine != 0) && (argc > 0)) {
    for (i = 0; i < argc; i++) {

      if (wcscmp(argv[i],L"-a") == 0) {
        i++;
        if (i >= argc) {
          ShowDebug(0,NULL,L"Fehler: Es wurden keine Zahl nach der \"-a\" Kommandozeileneingabe angegeben.");
          continue;
          }
        OptimizeMode = _wtol(argv[i]);
        if ((OptimizeMode < 1) || (OptimizeMode > 11)) {
          ShowDebug(0,NULL,L"Fehler: Die Zahl nach der \"-a\" Kommandozeileneingabe ist ungültig.");
          OptimizeMode = 3;
          }
        OptimizeMode = OptimizeMode - 1;
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-a' wurde akzeptiert. Der Optimierungsmodus ist %u.",OptimizeMode+1);
        continue;
        }
      if (wcsncmp(argv[i],L"-a",2) == 0) {
        OptimizeMode = _wtol(&argv[i][2]);
        if ((OptimizeMode < 1) || (OptimizeMode > 11)) {
          ShowDebug(0,NULL,L"Fehler: Die Zahl nach der \"-a\" Kommandozeilen-Option ist ungültig.");
          OptimizeMode = 3;
          }
        OptimizeMode = OptimizeMode - 1;
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-a' wurde akzeptiert. Der Optimierungsmodus ist %u.",OptimizeMode+1);
        continue;
        }

      if (wcscmp(argv[i],L"-s") == 0) {
        i++;
        if (i >= argc) {
          ShowDebug(0,NULL,L"Fehler: Es wurden keine Zahl nach der \"-s\" Kommandozeileneingabe angegeben.");
          continue;
          }
        Speed = _wtol(argv[i]);
        if ((Speed < 1) || (Speed > 100)) {
          ShowDebug(0,NULL,L"Fehler: Die Zahl nach der \"-s\" Kommandozeileneingabe ist ungültig.");
          Speed = 100;
          }
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-s' wurde akzeptiert. Die Ablaufgeschwindigkeit beträgt %u%%.",Speed);
        continue;
        }
      if ((wcsncmp(argv[i],L"-s",2) == 0) && (wcslen(argv[i]) >= 3)) {
        Speed = _wtol(&argv[i][2]);
        if ((Speed < 1) || (Speed > 100)) {
          ShowDebug(0,NULL,L"Fehler: Die Zahl nach der \"-s\" Kommandozeileneingabe ist ungültig.");
          Speed = 100;
          }
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-s' wurde akzeptiert. Die Ablaufgeschwindigkeit beträgt %u%%.",Speed);
        continue;
        }

      if (wcscmp(argv[i],L"-f") == 0) {
        i++;
        if (i >= argc) {
          ShowDebug(0,NULL,L"Fehler: Es wurden keine Zahl nach der \"-f\" Kommandozeileneingabe angegeben.");
          continue;
          }
        FreeSpace = _wtof(argv[i]);
        if ((FreeSpace < 0) || (FreeSpace > 100)) {
          ShowDebug(0,NULL,L"Fehler: Die Zahl nach der \"-f\" Kommandozeileneingabe ist ungültig.");
          FreeSpace = 1;
          }
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-f' wurde akzeptiert. Der freie Speicher beträgt %0.1f%%.",FreeSpace);
        continue;
        }
      if ((wcsncmp(argv[i],L"-f",2) == 0) && (wcslen(argv[i]) >= 3)) {
        FreeSpace = _wtof(&argv[i][2]);
        if ((FreeSpace < 0) || (FreeSpace > 100)) {
          ShowDebug(0,NULL,L"Fehler: Die Zahl nach der \"-f\" Kommandozeileneingabe ist ungültig.");
          FreeSpace = 1;
          }
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-f' wurde akzeptiert. Der freie Speicher beträgt %0.1f%%.",FreeSpace);
        continue;
        }

      if (wcscmp(argv[i],L"-d") == 0) {
        i++;
        if (i >= argc) {
          ShowDebug(0,NULL,L"Fehler: Es wurden keine Zahl nach der \"-d\" Kommandozeileneingabe angegeben.");
          continue;
          }
        Debug = _wtol(argv[i]);
        if ((Debug < 0) || (Debug > 6)) {
          ShowDebug(0,NULL,L"Fehler: Die Zahl nach der \"-d\" Kommandozeileneingabe ist ungültig.");
          Debug = 1;
          }
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-d' wurde akzeptiert. Die Stufe der Fehlermeldungen beträgt %u.",Debug);
        continue;
        }
      if ((wcsncmp(argv[i],L"-d",2) == 0) && (wcslen(argv[i]) == 3) &&
          (argv[i][2] >= '0') && (argv[i][2] <= '6')) {
        Debug = _wtol(&argv[i][2]);
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-d' wurde akzeptiert. Die Stufe der Fehlermeldungen beträgt %u.",Debug);
        continue;
        }

      if (wcscmp(argv[i],L"-l") == 0) {
        i++;
        if (i >= argc) {
          ShowDebug(0,NULL,L"Fehler: Es wurde kein Dateiname nach der \"-l\" Kommandozeileneingabe angegeben.");
          continue;
          }
        if (*LogFile != '\0') {
			ShowDebug(0,NULL,L"Kommandozeileneingabe '-l' wurde akzeptiert. Die Protokolldatei heißt: %s",LogFile);
          } else {
            ShowDebug(0,NULL,L"Kommandozeileneingabe '-l' wurde akzeptiert. Die Protokolldatei ist deaktiviert.");
            }
        continue;
        }
      if ((wcsncmp(argv[i],L"-l",2) == 0) && (wcslen(argv[i]) >= 3)) {
        if (*LogFile != '\0') {
			ShowDebug(0,NULL,L"Kommandozeileneingabe '-l' wurde akzeptiert. Die Protokolldatei heißt: %s",LogFile);
          } else {
            ShowDebug(0,NULL,L"Kommandozeileneingabe '-l' wurde akzeptiert. Die Protokolldatei ist deaktiviert.");
            }
        continue;
        }

      if (wcscmp(argv[i],L"-e") == 0) {
        i++;
        if (i >= argc) {
          ShowDebug(0,NULL,L"Fehler: Es wurde kein Pfad für die Maske nach der \"-e\" Kommandozeileneingabe angegeben.");
          continue;
          }
        Excludes = AddArrayString(Excludes,argv[i]);
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-e' wurde akzeptiert. Es wurde '%s' zur Ausschlussmaske hinzugefügt.",argv[i]);
        continue;
        }
      if ((wcsncmp(argv[i],L"-e",2) == 0) && (wcslen(argv[i]) >= 3)) {
        Excludes = AddArrayString(Excludes,&argv[i][2]);
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-e' wurde akzeptiert. Es wurde '%s' zur Ausschlussmaske hinzugefügt.",&argv[i][2]);
        continue;
        }

      if (wcscmp(argv[i],L"-u") == 0) {
        i++;
        if (i >= argc) {
          ShowDebug(0,NULL,L"Fehler: Es wurde kein Pfad für die Maske nach der \"-u\" Kommandozeileneingabe angegeben.");
          continue;
          }
        SpaceHogs = AddArrayString(SpaceHogs,argv[i]);
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-u' wurde akzeptiert. Es wurde '%s' zu den Spacehogs hinzugefügt.",argv[i]);
        continue;
        }
      if ((wcsncmp(argv[i],L"-u",2) == 0) && (wcslen(argv[i]) >= 3)) {
        SpaceHogs = AddArrayString(SpaceHogs,&argv[i][2]);
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-u' wurde akzeptiert. Es wurde '%s' zu den Spacehogs hinzugefügt.",&argv[i][2]);
        continue;
        }

      if (argv[i][0] == '-') {
        ShowDebug(0,NULL,L"Fehler: Kommandozeileneingabe wird nicht erkannt: %s",argv[i]);
        }
      }
    }

  /* Defragment all the paths that are specified on the commandline one by
     one. */
  DoAllVolumes = YES;
  if ((*CommandLine != 0) && (argc > 0)) {
    for (i = 0; i < argc; i++) {
      if (IamRunning != RUNNING) break;
      if ((wcscmp(argv[i],L"-a") == 0) ||
          (wcscmp(argv[i],L"-e") == 0) ||
          (wcscmp(argv[i],L"-u") == 0) ||
          (wcscmp(argv[i],L"-s") == 0) ||
          (wcscmp(argv[i],L"-f") == 0) ||
          (wcscmp(argv[i],L"-d") == 0) ||
          (wcscmp(argv[i],L"-l") == 0)) {
        i++;
        continue;
        }
      if (*argv[i] == '-') continue;
      if (*argv[i] == '\0') continue;
      RunJkDefrag(argv[i],OptimizeMode,Speed,FreeSpace,Excludes,SpaceHogs,&Running,
        &RedrawScreen,ShowStatus,ShowMove,ShowAnalyze,ShowDebug,DrawCluster,
        ClearScreen,NULL);
      DoAllVolumes = NO;
      }
    }

  /* If no paths are specified on the commandline then defrag all fixed
     harddisks. */
  if ((DoAllVolumes == YES) && (IamRunning == RUNNING)) {
    RunJkDefrag(NULL,OptimizeMode,Speed,FreeSpace,Excludes,SpaceHogs,&Running,
      &RedrawScreen,ShowStatus,ShowMove,ShowAnalyze,ShowDebug,DrawCluster,
      ClearScreen,NULL);
    }

  /* If the main message thread has requested the defragger library to stop
     (because the mouse has moved) then simply exit this thread. */
  if (IamRunning != RUNNING) {
    IamRunning = STOPPED;
    return(0);
    }

  /* Save the current time in the LastRun variable in the registry, so that the
     defragger will not run again before the HoursToWait are up. */
  time(&NowL);
  swprintf_s(w1,BUFSIZ,L"%lu",NowL);
  SaveValueInRegistry(L"LastRun",w1);

  /* If an alternate screensaver is selected then exit the program with return
     code 1. This will tell the wrapper to start the alternate screensaver. */
  if (*Alternate != '\0') {
    LogMessage(L"Starte den alternativen Bildschirmschoner: %s",Alternate);
    exit(1);
    }

  LogMessage(L"Es ist kein alternativer Bildschirmschoner eingestellt, somit wird ein schwarzer Bildschirm angezeigt.");
  IamRunning = STOPPED;
  return(0);
  }





/* Paint the screen (refresh). */
void PaintScreen(HWND WindowHandle) {
  HBRUSH Brush;
  RECT NewWindowSize;                        /* Graphics width/height. */
  HFONT Font;
  HFONT OldFont;
  struct __timeb64 Now;
  ULONG64 Time;
  RECT R;
  double Elapsed1;
  double Elapsed2;
  double Done;

  /* Reset the system idle timer (power saver). */
  SetThreadExecutionState(ES_SYSTEM_REQUIRED);

  /* Calculate the progress counter. */
  if ((ProgressTodo > 0) && (ProgressTime > ProgressStartTime + 1000)) {
    _ftime64_s(&Now);
    Time = Now.time * 1000 + Now.millitm;
    Elapsed1 = (double)(Time - ProgressStartTime);
    Elapsed2 = (double)(ProgressTime - ProgressStartTime);
    Done = ((double)ProgressDone / (double)ProgressTodo) * (Elapsed1 / Elapsed2);
    if (Done > 1) Done = 1;
    swprintf_s(Messages[2],50000,L"%.2f%%",100 * Done);
    }

  /* Grab the display mutex, to make sure that we are the only thread changing the window. */
  WaitForSingleObject(DisplayMutex,1000);

  /* If the screen has changed size then get a new handle to the window and ask the
     defragger to redraw the screen. */
  GetClientRect(WindowHandle,&NewWindowSize);
  if ((WindowDC == NULL) ||
      (WindowSize.right != NewWindowSize.right) ||
      (WindowSize.bottom != NewWindowSize.bottom)) {
    if (WindowDC != NULL) ReleaseDC(WindowHandle,WindowDC);
    WindowDC = GetDC(WindowHandle);
    if (WindowDC == NULL) {
      ReleaseMutex(DisplayMutex);
      return;
      }
    GetClientRect(WindowHandle,&WindowSize);
    RedrawScreen = 1;         /* Ask defragger to completely redraw the screen. */
    }

  /* If finished then show a black screen, otherwize show the current
     messages. */
  if (Running == STOPPED) {
      R.left = 0;
      R.top = 0;
      R.right = WindowSize.right;
      R.bottom = WindowSize.bottom;
      FillRect(WindowDC,&R,(HBRUSH)GetStockObject(BLACK_BRUSH));
    } else {
      /* Clear the text area. */
      R.left = 0;
      R.top = 0;
      R.right = WindowSize.right;
      R.bottom = 33;
      if (StatusBar == 1) R.bottom = 17;
      if (StatusBar == 2) R.bottom = 0;
      if (Debug > 1) R.bottom = R.bottom + 16;
      SetBkMode(WindowDC,OPAQUE);
      if (R.bottom > 0) {
        Brush = CreateSolidBrush(Colors[COLORBUSY]);
        if (Brush != NULL) {
          FillRect(WindowDC,&R,Brush);
          DeleteObject(Brush);
          }
        }

      /* Use a modern font. */
      Font = CreateFont(-9, 0, 0, 0, 0, 0, 0, 0, 0, OUT_TT_PRECIS, 0, 0, 0, TEXT("MS Shell Dlg"));
      OldFont = (HFONT) SelectObject(WindowDC,Font);
      if (OldFont != Font) DeleteObject(OldFont);

      /* Show all the messages in the textarea. */
      SetBkColor(WindowDC,Colors[COLORBUSY]);
      SetTextColor(WindowDC,Colors[COLOREMPTY]);
      if ((StatusBar == 0) || (StatusBar == 1)) {
        TextOutW(WindowDC,2,0,Messages[0],(int)wcslen(Messages[0]));
        TextOutW(WindowDC,40,0,Messages[1],(int)wcslen(Messages[1]));
        TextOutW(WindowDC,220,0,Messages[2],(int)wcslen(Messages[2]));
        TextOutW(WindowDC,290,0,Messages[3],(int)wcslen(Messages[3]));
        }
      if (StatusBar == 0) {
        TextOutW(WindowDC,2,17,Messages[4],(int)wcslen(Messages[4]));
        }
      if (Debug > 1) {
        if (StatusBar == 0) {
          TextOutW(WindowDC,2,33,Messages[5],(int)wcslen(Messages[5]));
          }
        if (StatusBar == 1) {
          TextOutW(WindowDC,2,17,Messages[5],(int)wcslen(Messages[5]));
          }
        if (StatusBar == 2) {
          TextOutW(WindowDC,2,0,Messages[5],(int)wcslen(Messages[5]));
          }
        }
      }

  /* Release the display mutex. */
  ReleaseMutex(DisplayMutex);
  }




/* Message handler. */
LRESULT WINAPI ScreenSaverProc(
    HWND WindowHandle,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam) {
  SYSTEM_BATTERY_STATE BatteryState;
  int WaitCount;
  DWORD Result;
  int i;
  WCHAR *p1;
  WCHAR *p2;

#ifdef _DEBUG
  /* Setup crash report handler. */
  SetUnhandledExceptionFilter(CrashReport);
#endif

  switch (Message) {
    case WM_CREATE:

      /* Setup the defaults. */
      Debug = 1;
      StatusBar = 0;
      ProgressStartTime = 0;
      ProgressTime = 0;
      ProgressDone = 0;
      for (i = 0; i < 6; i++) *Messages[i] = '\0';
      WindowDC = GetDC(WindowHandle);
      GetClientRect(WindowHandle,&WindowSize);
      DisplayMutex = CreateMutex(NULL,FALSE,NULL);
      GetModuleFileNameW(NULL,MyName1,MAX_PATH);
      GetShortPathNameW(MyName1,MyShortName1,MAX_PATH);
      GetLongPathNameW(MyShortName1,MyName1,MAX_PATH);
      wcscpy_s(MyName2,MAX_PATH,MyName1);
      p2 = stristrW(MyName2,L".exe");
      if (p2 != NULL) {
        *p2 = '\0';
        wcscat_s(MyName2,MAX_PATH,L".scr");
        }
      wcscpy_s(MyShortName2,MAX_PATH,MyShortName1);
      p2 = stristrW(MyShortName2,L".exe");
      if (p2 != NULL) {
        *p2 = '\0';
        wcscat_s(MyShortName2,MAX_PATH,L".scr");
        }

      /* Determine default path to logfile. */
      swprintf_s(LogFile,MAX_PATH,L"%s",MyName1);
      p1 = stristrW(LogFile,L".exe");
      if (p1 == NULL) p1 = stristrW(LogFile,L".scr");
      if (p1 != NULL) {
          *p1 = '\0';
          wcscat_s(LogFile,MAX_PATH,L".log");
          _wunlink(LogFile);
        } else {
          *LogFile = '\0';
          }

      /* If another instance is already running then exit. */
      if (AlreadyRunning() == YES) exit(0);

      /* Exit if we are running on battery power. */
      Result = CallNtPowerInformation(SystemBatteryState,NULL,0,&BatteryState,
        sizeof(SYSTEM_BATTERY_STATE));
      if ((Result == 0) &&
          (BatteryState.BatteryPresent == TRUE) &&
          (BatteryState.AcOnLine == FALSE)) {
        ShowDebug(0,NULL,L"Um die Batterie zu schonen wird das Programm jetzt beendet.");
        exit(1);
        }

      /* Initialize the screen. */
      ClearScreen(L"Es wird gestartet, bitte warten...");
      PaintScreen(WindowHandle);

      /* Start up the defragmentation and timer threads. */
      if (CreateThread(NULL,0,DefragThread,NULL,0,NULL) == NULL) exit(0);
      SetTimer(WindowHandle,1,300,NULL);

      break;

    case WM_ERASEBKGND:
      break;

    case WM_TIMER:                      /* Refresh the screen every 0.3 seconds. */
      PaintScreen(WindowHandle);
      break;

    case WM_DESTROY:                                      /* Stop the defragger. */

      /* Ask & wait for the DefragThread to finish. */
      if (IamRunning == RUNNING) {
        IamRunning = STOPPING;
        StopJkDefrag(&Running,-1);
        for (WaitCount = 0; WaitCount < 100; WaitCount++) {
          if (IamRunning == STOPPED) break;
          Sleep(100);
          }
        }

      break;

    }

  return(DefScreenSaverProc(WindowHandle,Message,wParam,lParam));
  }




/* Subfunction for WalkScreenSavers(), see below. */
void WalkScreenSavers2(HWND Handle, int Mode, WCHAR *Root, WCHAR *SystemRoot,
    WCHAR *SelectedPath, WCHAR *SelectedName) {
  HANDLE FindHandle;
  WIN32_FIND_DATAW FindFileData;
  WCHAR Name[BUFSIZ];
  WCHAR Path[MAX_PATH];
  HMODULE ModuleHandle;
  int Result;
  WCHAR s1[BUFSIZ];
  WCHAR s2[BUFSIZ];
  WCHAR *p1;

  swprintf_s(Path,MAX_PATH,L"%s\\%s*.scr",SystemRoot,Root);
  FindHandle = FindFirstFileW(Path,&FindFileData);
  if (FindHandle == INVALID_HANDLE_VALUE) return;
  do {
    if (wcscmp(FindFileData.cFileName,L".") == 0) continue;
    if (wcscmp(FindFileData.cFileName,L"..") == 0) continue;

    /* If the screensaver is myself then ignore. We don't want an infinite
       loop. */
    swprintf_s(Path,MAX_PATH,L"%s\\%s%s",SystemRoot,Root,FindFileData.cFileName);
    if ((_wcsicmp(Path,MyName1) == 0) ||
        (_wcsicmp(Path,MyName2) == 0) ||
        (_wcsicmp(Path,MyShortName1) == 0) ||
        (_wcsicmp(Path,MyShortName2) == 0)) continue;

    /* Load the screensaver file. */
    ModuleHandle = LoadLibraryW(Path);
    if (ModuleHandle == NULL) {
      SystemErrorStr(GetLastError(),s1,BUFSIZ);
      swprintf_s(s2,BUFSIZ,L"Die ausführbare Datei des Bildschirmschoners kann nicht geladen werden \"%s\": %s",Path,s1);
      ShowDebug(0,NULL,s2);
      FreeLibrary(ModuleHandle);
      continue;
      }

    /* The descriptive name of the screensaver is the resource string with
       number 1. If not found then use the filename. */
    Result = LoadStringW(ModuleHandle,1,Name,BUFSIZ);
    if (Result == 0) {
      swprintf_s(Name,BUFSIZ,L"%s",FindFileData.cFileName);
      p1 = stristrW(Name,L".exe");
      if (p1 != NULL) *p1 = 0;
      p1 = stristrW(Name,L".scr");
      if (p1 != NULL) *p1 = 0;
      }

    /* Free the screensaver file. */
    FreeLibrary(ModuleHandle);

    /* Mode=1: Add the name of the screensaver to the pull-down box. */
    if (Mode == 1) {
      SendMessageW(Handle,CB_ADDSTRING,NULL,(LPARAM)Name);
      if (_wcsicmp(Path,SelectedPath) == 0) {
        SendMessageW(Handle,CB_SELECTSTRING,-1,(LPARAM)Name);
        }
      }

    /* Mode=2: Save the full path to the screensaver in our AlternateScreenSaver
       registry key, for later reference. */
    if ((Mode == 2) && (_wcsicmp(Name,SelectedName) == 0)) {
      SaveValueInRegistry(L"AlternateScreenSaver",Path);
      }

    } while (FindNextFileW(FindHandle,&FindFileData) != 0);
  FindClose(FindHandle);
  }





/* The pull-down list of screensavers in the configuration window shows the
   names of all the screensavers. These are not available just like that, to
   find them all we have to walk through all the "*.scr" files in the
   "%SystemRoot%/system32" directory, load every file, and read the first
   string in it's resources. This subroutine does that, and performs two
   functions:
   Mode=1   Save the names of all the screensavers in the drop-down list box.
   Mode=2   Save the path of the currently selected screensaver (which is a
            name in the drop-down list) in the AlternateScreenSaver registry
            key, so we can execute the screensaver when defragging finishes.
   */
void WalkScreenSavers(HWND WindowHandle, int Mode) {
  HWND Handle;
  WCHAR SystemRoot[MAX_PATH];
  WCHAR SelectedPath[BUFSIZ];
  WCHAR SelectedName[BUFSIZ];
  WCHAR Name[BUFSIZ];
  WCHAR Path[BUFSIZ];
  HKEY Key;
  DWORD KeyDisposition;
  int Result;
  DWORD Length;
  WCHAR s1[BUFSIZ];
  WCHAR s2[BUFSIZ];

  /* Get handle to the drop-down list in the configuration window. */
  Handle = GetDlgItem(WindowHandle,1005);

  /* Mode=1: Fetch the current AlternateScreenSaver from the registry. */
  if (Mode == 1) {
    GetValueFromRegistry(L"AlternateScreenSaver",SelectedPath);
    }

  /* Mode=2: Fetch the currently selected screensaver from the pull-down list. */
  if (Mode == 2) {
    SendMessageW(Handle,WM_GETTEXT,BUFSIZ,(LPARAM)SelectedName);
    }

  /* Get the value of the %SystemRoot% from the registry. */
  *SystemRoot = 0;
  Result = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
    0,NULL,REG_OPTION_NON_VOLATILE,KEY_READ,NULL,&Key,&KeyDisposition);
  if (Result == ERROR_SUCCESS) {
    Length = sizeof(WCHAR) * MAX_PATH;
    Result = RegQueryValueExW(Key,L"SystemRoot",NULL,NULL,(BYTE *)SystemRoot,&Length);
    RegCloseKey(Key);
    }
  if (Result != ERROR_SUCCESS) {
    SystemErrorStr(GetLastError(),s1,BUFSIZ);
    swprintf_s(s2,BUFSIZ,L"Getting key \"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\SystemRoot\": %s",s1);
    ShowDebug(3,NULL,s2);
    return;
    }

  /* Walk through all the "%SystemRoot%/system32/*.scr" files. */
  WalkScreenSavers2(Handle,Mode,L"system32\\",SystemRoot,SelectedPath,SelectedName);
  WalkScreenSavers2(Handle,Mode,L"",SystemRoot,SelectedPath,SelectedName);

  /* Handle the [black screen] item in the pull-down box. */
  wcscpy_s(Name,BUFSIZ,L"[black screen]");
  *Path = 0;
  if (Mode == 1) {
    SendMessageW(Handle,CB_INSERTSTRING,0,(LPARAM)Name);
    if (*SelectedPath == 0) SendMessageW(Handle,CB_SELECTSTRING,-1,(LPARAM)Name);
    }
  if ((Mode == 2) && (_wcsicmp(Name,SelectedName) == 0)) {
    SaveValueInRegistry(L"AlternateScreenSaver",Path);
    }
  }




/* Handle messages from the configuration dialog, which is defined in the RC
   file. */
BOOL WINAPI ScreenSaverConfigureDialog(
    HWND WindowHandle, UINT message, WPARAM wParam, LPARAM lParam) {
  HWND Handle;
  int TextLength;
  WCHAR w1[BUFSIZ];
  WCHAR *p1;
  WCHAR *p2;

#ifdef _DEBUG
  /* Setup crash report handler. */
  SetUnhandledExceptionFilter(CrashReport);
#endif

  switch(message) {
    case WM_INITDIALOG:

      GetModuleFileNameW(NULL,MyName1,MAX_PATH);
      GetShortPathNameW(MyName1,MyShortName1,MAX_PATH);
      GetLongPathNameW(MyShortName1,MyName1,MAX_PATH);
      wcscpy_s(MyName2,MAX_PATH,MyName1);
      p2 = stristrW(MyName2,L".exe");
      if (p2 != NULL) {
        *p2 = '\0';
        wcscat_s(MyName2,MAX_PATH,L".scr");
        }
      wcscpy_s(MyShortName2,MAX_PATH,MyShortName1);
      p2 = stristrW(MyShortName2,L".exe");
      if (p2 != NULL) {
        *p2 = '\0';
        wcscat_s(MyShortName2,MAX_PATH,L".scr");
        }

      /* Determine default path to logfile. */
      swprintf_s(LogFile,MAX_PATH,L"%s",MyName1);
      p1 = stristrW(LogFile,L".exe");
      if (p1 == NULL) p1 = stristrW(LogFile,L".scr");
      if (p1 != NULL) {
          *p1 = '\0';
          wcscat_s(LogFile,BUFSIZ,L".log");
        } else {
          *LogFile = '\0';
          }

      /* Fill the commandline editbox with the value currently stored in the
         registry. */
      GetValueFromRegistry(L"CommandLine",w1);
      Handle = GetDlgItem(WindowHandle,1003);
      SendMessageW(Handle,WM_SETTEXT,NULL,(LPARAM)w1);

      /* Fill the screensavers combobox with a list of all the screensavers. */
      WalkScreenSavers(WindowHandle,1);

      /* Setup and select the HoursToWait. */
      Handle = GetDlgItem(WindowHandle,1008);
      SendMessageW(Handle,CB_INSERTSTRING,NULL,(LPARAM)L"24");
      SendMessageW(Handle,CB_INSERTSTRING,NULL,(LPARAM)L"18");
      SendMessageW(Handle,CB_INSERTSTRING,NULL,(LPARAM)L"12");
      SendMessageW(Handle,CB_INSERTSTRING,NULL,(LPARAM)L"6");
      SendMessageW(Handle,CB_INSERTSTRING,NULL,(LPARAM)L"5");
      SendMessageW(Handle,CB_INSERTSTRING,NULL,(LPARAM)L"4");
      SendMessageW(Handle,CB_INSERTSTRING,NULL,(LPARAM)L"3");
      SendMessageW(Handle,CB_INSERTSTRING,NULL,(LPARAM)L"2");
      SendMessageW(Handle,CB_INSERTSTRING,NULL,(LPARAM)L"1");
      SendMessageW(Handle,CB_INSERTSTRING,NULL,(LPARAM)L"0");
      GetValueFromRegistry(L"HoursToWait",w1);
      if (*w1 != '\0') {
          SendMessageW(Handle,CB_SELECTSTRING,-1,(LPARAM)w1);
        } else {
          SendMessageW(Handle,CB_SELECTSTRING,-1,(LPARAM)L"4");
          }

      /* Setup and select the Statusbar. */
      Handle = GetDlgItem(WindowHandle,1011);
      SendMessageW(Handle,CB_INSERTSTRING,NULL,(LPARAM)L"No status bar");
      SendMessageW(Handle,CB_INSERTSTRING,NULL,(LPARAM)L"Top line only (no filenames)");
      SendMessageW(Handle,CB_INSERTSTRING,NULL,(LPARAM)L"Full status bar");
      GetValueFromRegistry(L"StatusBar",w1);
      if (*w1 != '\0') {
          SendMessageW(Handle,CB_SELECTSTRING,-1,(LPARAM)w1);
        } else {
          SendMessageW(Handle,CB_SELECTSTRING,-1,(LPARAM)L"Full status bar");
          }

      /* Reset the LastRun setting in the registry, so the next time the
         screensaver starts it will defragment. */
      SaveValueInRegistry(L"LastRun",L"0");

      return TRUE;
      break;


    case WM_COMMAND:

      /* Commandline editbox: save the commandline in the registry. */
      if (LOWORD(wParam) == 1003) {
        Handle = GetDlgItem(WindowHandle,1003);
        TextLength = GetWindowTextLength(Handle) + 1;
        p1 = (WCHAR *)malloc(sizeof(WCHAR) * TextLength);
        if (p1 != NULL) {
          SendMessageW(Handle,WM_GETTEXT,TextLength,(LPARAM)p1);
          SaveValueInRegistry(L"CommandLine",p1);
          free(p1);
          }
        return(TRUE);
        }

      /* Screensaver Combobox: save the selected screensaver in the registry. */
      if (LOWORD(wParam) == 1005) {
        WalkScreenSavers(WindowHandle,2);
        return(TRUE);
        }

      /* Settings button clicked: call the settings window of the screensaver. */
      if (LOWORD(wParam) == 1006) {
        GetValueFromRegistry(L"AlternateScreenSaver",w1);
        if (*w1 != '\0') _wspawnlp(P_NOWAIT,w1,w1,L"/c",NULL);
        return(TRUE);
        }

      /* Hour clicked: save the selected number of hours in the registry. */
      if (LOWORD(wParam) == 1008) {
        Handle = GetDlgItem(WindowHandle,1008);
        SendMessageW(Handle,WM_GETTEXT,BUFSIZ,(LPARAM)w1);
        SaveValueInRegistry(L"HoursToWait",w1);
        return(TRUE);
        }

      /* Status bar clicked: save the selected setting in the registry. */
      if (LOWORD(wParam) == 1011) {
        Handle = GetDlgItem(WindowHandle,1011);
        SendMessageW(Handle,WM_GETTEXT,BUFSIZ,(LPARAM)w1);
        SaveValueInRegistry(L"StatusBar",w1);
        return(TRUE);
        }

      /* OK button clicked: close the window. */
      if ((LOWORD(wParam) == 1012) || (LOWORD(wParam) == WM_DESTROY)) {
        EndDialog(WindowHandle,0);
        return(TRUE);
        }

      break;

    }

  return(FALSE);
  }




BOOL WINAPI RegisterDialogClasses(HANDLE hInst) {
  return(TRUE);
  }
/*
  German Version 3.36 by Markus Hörl  ||  http://jkdefrag.8qm.de or http://www.jkdefrag.de
*/