{*******************************************************}
{                                                       }
{       Tocsg.Delete                                    }
{                                                       }
{       Copyright (C) 2022 sunk                         }
{                                                       }
{*******************************************************}

unit Tocsg.Delete;

interface

uses
  System.SysUtils, System.Classes, ADODB2000;

function DeleteFileSelf(sPath: String = ''; aAddList: TStringList = nil): String;
function PerfectDeleteFile(sPath: String; nOvrCnt: Integer = 1; bDetail: Boolean = false; sTmpDirName: String = ''): Boolean;

procedure ClearRecentFolder(sUName: String = '');
procedure ClearAutoJumpLists(sUName: String = ''; bIgrFav: Boolean = true);
procedure ClearUserTempFolder(bKvTmpOnly: Boolean = true);

implementation

uses
  Tocsg.Strings, Tocsg.Path, Tocsg.Safe, Winapi.Windows, Tocsg.Shell,
  Tocsg.Files;

function DeleteFileSelf(sPath: String = ''; aAddList: TStringList = nil): String;
var
  sName,
  sRName,
  sCmdSelfDel: String;
  CmdList: TStringList;
  i: Integer;
begin
  Result := '';

  if sPath = '' then
    sPath := GetRunExePath;

  sName := ExtractFileName(sPath);
  sRName := GetRandomStr(4, 8);
  sPath := ExtractFilePath(sPath);

  Guard(CmdList, TStringList.Create);
  CmdList.Add('timeout 1');
  CmdList.Add(Format('taskkill /f /pid %d', [GetCurrentProcessId]));
  CmdList.Add('timeout 1');
  CmdList.Add(Format('taskkill /f /pid %d', [GetCurrentProcessId]));
  CmdList.Add('timeout 1');
  CmdList.Add(Format('cd "%s"', [sPath]));
//  CmdList.Add(Format('ren ".\%s" "%s"', [sName, sRName]));
  CmdList.Add('timeout 1');
//  CmdList.Add(Format('del "%s"', [sRName]));
  CmdList.Add(Format('del .\"%s"', [sName]));
  CmdList.Add('timeout 1');
  if aAddList <> nil then
    for i := 0 to aAddList.Count - 1 do
    begin
      CmdList.Add(Format('del "%s"', [aAddList[i]]));
    end;

  sCmdSelfDel := Format('$d-%s.cmd', [GetRandomStr(4, 6)]);
  CmdList.Add(Format('del ".\%s"', [sCmdSelfDel]));

  try
    Result := sPath + sCmdSelfDel;
    CmdList.SaveToFile(Result);
  except
    exit;
  end;

  Sleep(500);
//  ExecuteAppAsUser(GetCurrentProcessId, 'cmd.exe', Format('/C "%s"', [sPath + sCmdSelfDel]), SW_HIDE);
  ExecutePath_hide('cmd.exe', Format('/C "%s"', [sPath + sCmdSelfDel]));
end;

function PerfectDeleteFile(sPath: String; nOvrCnt: Integer = 1; bDetail: Boolean= false; sTmpDirName: String = ''): Boolean;
const
  BUF_LEN = 65536;
var
  fs: TFileStream;
  dwWrite: DWORD;
  arrBuf: array [0 .. BUF_LEN-1] of Byte;
  i: Integer;
begin
  Result := false;
  if not FileExists(sPath) then
    exit;

  if nOvrCnt <= 0 then
    nOvrCnt := 1;

  try
    if bDetail then
    begin
      if sTmpDirName <> '' then
      begin
      // 위치 정보 변경
        var sTmpPath: String := sPath[1] + ':\' + IncludeTrailingBackslash(sTmpDirName); // ':\@TmpPd\';
        if ForceDirectories(sTmpPath) then
        begin
          sTmpPath := sTmpPath + GetRandomStr(5, 8);
          if MoveFile_wait(sPath, sTmpPath, 1) then
            sPath := sTmpPath;
        end;
      end;
    end;

  // 내용 삭제
    fs := TFileStream.Create(sPath, fmOpenReadWrite);
    try
      for i := 0 to nOvrCnt - 1 do
      begin
        ZeroMemory(@arrBuf, BUF_LEN);
        fs.Position := 0;
        while fs.Size > fs.Position do
        begin
          if BUF_LEN > (fs.Size - fs.Position) then
            dwWrite := fs.Size - fs.Position
          else
            dwWrite := BUF_LEN;

          if fs.Write(arrBuf, dwWrite) <> dwWrite then
            break;
        end;
      end;

      if bDetail then
      begin
      // 크기 초기화
        fs.Size := 0;
      end;
    finally
      fs.Free;
    end;

    if bDetail then
    begin
    // 시간 정보 초기화
      SetFileDateTime(sPath, 0, 0, 0);
    end;
  except
//    on E: Exception do
//      ETgException.TraceException(E, 'Fail .. PerfectDeleteFile()');
  end;

  Result := DeleteFileForce(sPath);
end;

procedure ClearRecentFolder(sUName: String = '');
var
  sDir: String;
  FileList: TStringList;
  i: Integer;
begin
  if sUName = '' then
  begin
    sDir := GetUserDir + 'AppData\Roaming\Microsoft\Windows\Recent\'
  end else begin
    sDir := GetWindowsDir;
    if sDir = '' then
      exit;

    sDir := sDir[1] + Format(':\Users\%s\AppData\Roaming\Microsoft\Windows\Recent\', [sUName]);
  end;

  if DirectoryExists(sDir) then
  begin
    Guard(FileList, TStringList.Create);
    ExtrFilesFromDir(sDir, FileList, false, 'lnk');
    for i := 0 to FileList.Count - 1 do
      DeleteFile(PChar(sDir + FileList[i]));
//    DeleteDirSub(sDir, false); // 이렇게 하면 즐겨 찾기까지 모두 삭제됨 23_1219 16:50:53 kku
  end;
end;

procedure ClearAutoJumpLists(sUName: String = ''; bIgrFav: Boolean = true);
var
  sDir: String;
  FileList: TStringList;
  i: Integer;
begin
  if sUName = '' then
  begin
    sDir := GetUserDir + 'AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinations\'
  end else begin
    sDir := GetWindowsDir;
    if sDir = '' then
      exit;

    sDir := sDir[1] + Format(':\Users\%s\AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinations\', [sUName]);
  end;

  if DirectoryExists(sDir) then
  begin
    Guard(FileList, TStringList.Create);
    if bIgrFav then
      FileList.Add('f01b4d95cf55d32a.automaticDestinations-ms'); // 즐겨찾기 제외
    DeleteDirSub(sDir, false, false, FileList);
  end;
end;

procedure ClearUserTempFolder(bKvTmpOnly: Boolean = true);
var
  sWinDir,
  sUsersDir: String;
  UserList: TStringList;
  i: Integer;

  procedure ClearWinTempKvOnly;
  var
    wfd: TWin32FindData;
    hSc: THandle;
    sDir, sPath, sFName: String;
  begin
    sDir := sWinDir + 'Temp\';
    sPath := sDir + '*.*';

    hSc := FindFirstFile(PChar(sPath), wfd);
    if hSc = INVALID_HANDLE_VALUE then
      exit;

    try
      Repeat
        if (String(wfd.cFileName) <> '.') and (String(wfd.cFileName) <> '..') then
          if ((wfd.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0) then
          begin
            if UpperCase(wfd.cFileName).StartsWith('KV_') then
              DeleteDir(sDir + wfd.cFileName);
          end else begin
            sFName := UpperCase(wfd.cFileName);
            if GetFileExt(sFName) = 'TMP' then
            begin
              if sFName.StartsWith('KP') or sFName.StartsWith('KV') or sFName.StartsWith('KY') then
                DeleteFile(PChar(sDir + wfd.cFileName));
            end;
          end;
      Until not FindNextFile(hSc, wfd);
    finally
      WinApi.Windows.FindClose(hSc);
    end;
  end;

begin
  sWinDir := GetWindowsDir;
  if sWinDir = '' then
    exit;

  sUsersDir := sWinDir[1] + ':\Users\';
  if not DirectoryExists(sUsersDir) then
    exit;

  Guard(UserList, TStringList.Create);
  ExtrDirFromDir(sUsersDir, UserList);
  for i := 0 to UserList.Count - 1 do
    DeleteDirSub(Format('%s\AppData\Local\Temp\', [sUsersDir + UserList[i]]), true, false, nil, true);

  if bKvTmpOnly then
    ClearWinTempKvOnly
  else
    DeleteDirSub(sWinDir + 'Temp\', true, false, nil, true); // 25_1021 13:31:14 kku
end;

end.
