unit FileHandleListUnit; interface uses Winapi.Windows, System.SysUtils, System.Generics.Collections, System.Classes, System.Character, System.StrUtils, BsoneDebug, BsoneUtil; const MIN_BUFFERSIZE = 40; HISTORY_FILE_LISTMAX = 30; type TFILE_BUFFER = record buf: array[0..MIN_BUFFERSIZE - 1] of Byte; size: DWORD; end; PFILE_BUFFER = ^TFILE_BUFFER; TFileHandle = class public path: string; drivetype: DWORD; h: THandle; hash: array[0..31] of Byte; cnt: DWORD; dwDesiredAccess: DWORD; dwShareMode: DWORD; dwCreationDisposition: DWORD; dwFlagsAndAttributes: DWORD; dwOffset: DWORD; lbuf: TList; constructor Create; destructor Destroy; override; procedure Assign(ASource: TFileHandle); end; TFileHandleList = class private public lFile: TObjectList; lHisFile: TObjectList; cs, lHcs: TRTLCriticalSection; state: boolean; HistoryFileCnt: DWORD; constructor Create; destructor Destroy; override; procedure Lock(var ACS: TRTLCriticalSection); procedure UnLock(var ACS: TRTLCriticalSection); function IsHandleOutPath(h: THandle; out sPath: string): BOOL; function FindHandle(h: THandle): TFileHandle; function ChangeHandle(h: THandle; const path: string; dwDesiredAccess: DWORD = 0; dwShareMode: DWORD = 0; dwCreationDisposition: DWORD = 0; dwFlagsAndAttributes: DWORD = 0): Boolean; function IsHandle(h: THandle; out HandleInfo: TFileHandle): BOOL; procedure Cleanup; procedure ClearHistory; function InsertHandle(h: THandle; const path: string; dwDesiredAccess, dwShareMode, dwCreationDisposition, dwFlagsAndAttributes: DWORD): BOOL; function IsEmptyBuffer(lpBuffer: PByte; size: DWORD): BOOL; function InsertBufferForHandle(h: THandle; const path: string; buf: PByte; size: DWORD): BOOL; function InsertBuffer(h: THandle; const path: string; buf: PByte; size: DWORD): BOOL; function SetHistoryFile(f: TFileHandle): BOOL; function IsHistoryFileBufferCompare(const path: string; lpBuffer: PByte; size: DWORD; out fileHandle: TFileHandle): BOOL; overload; function IsBufferCompare(const path: string; buffer: PByte; size: DWORD; out fileHandle: TFileHandle; bInterNetWriteFile: BOOL = FALSE): BOOL; overload; function DelHandle(h: THandle): BOOL; end; implementation constructor TFileHandle.Create; begin inherited Create; lbuf := TList.Create; FillChar(hash, SizeOf(hash), 0); end; destructor TFileHandle.Destroy; begin lbuf.Free; inherited Destroy; end; procedure TFileHandle.Assign(ASource: TFileHandle); begin if ASource = nil then Exit; Self.path := ASource.path; Self.drivetype := ASource.drivetype; Self.h := ASource.h; Self.hash := ASource.hash; Self.cnt := ASource.cnt; Self.dwDesiredAccess := ASource.dwDesiredAccess; Self.dwShareMode := ASource.dwShareMode; Self.dwCreationDisposition := ASource.dwCreationDisposition; Self.dwFlagsAndAttributes := ASource.dwFlagsAndAttributes; Self.dwOffset := ASource.dwOffset; end; constructor TFileHandleList.Create; begin InitializeCriticalSection(cs); InitializeCriticalSection(lHcs); lFile := TObjectList.Create(True); lHisFile := TObjectList.Create(True); HistoryFileCnt := 0; state := true; end; destructor TFileHandleList.Destroy; begin Cleanup; inherited Destroy; end; procedure TFileHandleList.Lock(var ACS: TRTLCriticalSection); begin EnterCriticalSection(ACS); end; procedure TFileHandleList.UnLock(var ACS: TRTLCriticalSection); begin LeaveCriticalSection(ACS); end; function TFileHandleList.FindHandle(h: THandle): TFileHandle; var ch: TFileHandle; begin Result := nil; if not state then Exit; if lFile.Count = 0 then Exit; for ch in lFile do begin if ch.h = h then begin Result := ch; Exit; end; end; end; function TFileHandleList.ChangeHandle(h: THandle; const path: string; dwDesiredAccess: DWORD; dwShareMode: DWORD; dwCreationDisposition: DWORD; dwFlagsAndAttributes: DWORD): Boolean; var fh: TFileHandle; begin Result := False; if not state then Exit; if lFile.Count = 0 then Exit; for fh in lFile do begin if fh.h = h then begin if path = '' then Break; if (path <> '') and (not StartsText(path, fh.path)) then begin fh.path := path; fh.dwDesiredAccess := dwDesiredAccess; fh.dwCreationDisposition := dwCreationDisposition; fh.dwFlagsAndAttributes := dwFlagsAndAttributes; fh.dwShareMode := dwShareMode; fh.lbuf.Clear; fh.drivetype := UtilGetDriveTypeEx(path); end; Result := True; Exit; end; end; end; procedure TFileHandleList.Cleanup; begin if not state then Exit; state := false; Lock(cs); FreeAndNil(lFile); UnLock(cs); Lock(lHcs); FreeAndNil(lHisFile); UnLock(lHcs); DeleteCriticalSection(cs); DeleteCriticalSection(lHcs); end; procedure TFileHandleList.ClearHistory; begin Lock(lHcs); try lHisFile.Clear; finally UnLock(lHcs); end; end; function TFileHandleList.InsertHandle(h: THandle; const path: string; dwDesiredAccess, dwShareMode, dwCreationDisposition, dwFlagsAndAttributes: DWORD): BOOL; var fh: TFileHandle; drivetype: DWORD; isType: Boolean; begin isType:= False; Result := FALSE; // DVLOG('InsertHandle: state(%d)', [DWORD(state)]); if not state then Exit; Lock(cs); try drivetype:= UtilGetDriveTypeEx(path); case drivetype of DRIVE_NO_ROOT_DIR, DRIVE_REMOTE, DRIVE_FIXED: isType:= True; end; // DVLOG('InsertHandle: drivetype(%d)', [drivetype]); if not isType then Exit; if not ChangeHandle(h, path, dwDesiredAccess, dwShareMode, dwCreationDisposition, dwFlagsAndAttributes) then begin fh := TFileHandle.Create; fh.h := h; fh.dwDesiredAccess := dwDesiredAccess; fh.dwCreationDisposition := dwCreationDisposition; fh.dwFlagsAndAttributes := dwFlagsAndAttributes; fh.dwShareMode := dwShareMode; fh.path := path; fh.drivetype := drivetype; lFile.Insert(0, fh); // DVLOG('InsertHandle: (%s)', [path]); end; finally UnLock(cs); end; Result := TRUE; end; function TFileHandleList.IsEmptyBuffer(lpBuffer: PByte; size: DWORD): BOOL; var i: DWORD; begin Result := TRUE; if lpBuffer = nil then Exit; for i := 0 to size - 1 do begin if lpBuffer[i] <> 0 then begin Result := FALSE; Break; end; end; end; function TFileHandleList.InsertBufferForHandle(h: THandle; const path: string; buf: PByte; size: DWORD): BOOL; var fh: TFileHandle; fbuf: TFILE_BUFFER; max: Integer; begin Result := FALSE; if not state then Exit; if IsEmptyBuffer(buf, 32) then Exit; // DVLOG('InsertBufferForHandle: ++',[]); Lock(cs); try fh := FindHandle(h); // DVLOG('InsertBufferForHandle: fh(%p) ++',[Pointer(fh)]); if fh = nil then begin if path = '' then Exit; fh := TFileHandle.Create; fh.h := h; fh.path := path; // DVLOG('InsertBufferForHandle: UtilGetDriveTypeEx ++', []); fh.drivetype := UtilGetDriveTypeEx(path); fh.cnt := 0; if size > 0 then begin FillChar(fbuf, SizeOf(fbuf), 0); if size > SizeOf(fbuf.buf) then max := SizeOf(fbuf.buf) else max := size; DVLOG('InsertBufferForHandle: New max(%d)', [max]); Move(buf^, fbuf.buf, max); fbuf.size := max; fh.lbuf.Add(fbuf); end; // DVLOG('InsertBufferForHandle: Insert++', []); lFile.Insert(0, fh); end else begin // DVLOG('InsertBufferForHandle: path(%s)(%s), size(%d)', [PChar(path), PChar(fh.path), size]); if (path <> '') and (not StartsText(path, fh.path)) then begin fh.path := path; end; if fh.lbuf.Count <> 0 then Exit; // fh.lbuf.Clear; if size > 0 then begin FillChar(fbuf, SizeOf(fbuf), 0); if size > SizeOf(fbuf.buf) then max := SizeOf(fbuf.buf) else max := size; DVLOG('InsertBufferForHandle: max(%d)', [max]); Move(buf^, fbuf.buf, max); fbuf.size := max; fh.lbuf.Add(fbuf); end; end; finally UnLock(cs); end; Result := TRUE; end; function TFileHandleList.InsertBuffer(h: THandle; const path: string; buf: PByte; size: DWORD): BOOL; var ch: TFileHandle; fbuf: TFILE_BUFFER; max: Integer; begin Result := FALSE; if not state then Exit; if IsEmptyBuffer(buf, 32) then Exit; Lock(cs); try ch := FindHandle(h); if ch <> nil then begin if (path <> '') and (not StartsText(path, ch.path)) then begin ch.path := path; end; ch.lbuf.Clear; if (size > 0) and (ch.lbuf.Count = 0) then begin FillChar(fbuf, SizeOf(fbuf), 0); if size > SizeOf(fbuf.buf) then max := SizeOf(fbuf.buf) else max := size; Move(buf^, fbuf.buf, max); fbuf.size := max; ch.lbuf.Insert(0, fbuf); end; end; finally UnLock(cs); end; Result := TRUE; end; function TFileHandleList.SetHistoryFile(f: TFileHandle): BOOL; begin Result := FALSE; if not state then Exit; if f = nil then Exit; Lock(lHcs); try if lHisFile.Count >= HISTORY_FILE_LISTMAX then begin lHisFile.Delete(lHisFile.Count - 1); end; lHisFile.Insert(0, f); finally UnLock(lHcs); end; Result := TRUE; end; function TFileHandleList.DelHandle(h: THandle): BOOL; var ch: TFileHandle; remain: TFileHandle; i: Integer; begin Result := FALSE; remain := nil; if not state then Exit; Lock(cs); try for i := lFile.Count - 1 downto 0 do begin ch := lFile[i]; if ch.h = h then begin if ch.cnt > 0 then begin ch.cnt := ch.cnt - 1; end else begin remain := lFile.Extract(ch); // DVLOG('DelHandle: (%p)', [Pointer(ch.h)]); Break; end; end; end; finally UnLock(cs); end; if remain = nil then Exit; // DVLOG('SetHistoryFile: (%p)', [Pointer(remain.h)]); Result := SetHistoryFile(remain); end; function TFileHandleList.IsHandle(h: THandle; out HandleInfo: TFileHandle): BOOL; var fh: TFileHandle; begin Result := FALSE; HandleInfo := nil; if not state then Exit; if lFile.Count = 0 then Exit; Lock(cs); try for fh in lFile do begin if fh.h = h then begin HandleInfo:= TFileHandle.Create; HandleInfo.Assign(fh); Result := TRUE; Break; end; end; finally UnLock(cs); end; end; function TFileHandleList.IsHandleOutPath(h: THandle; out sPath: string): BOOL; var fh: TFileHandle; begin Result := FALSE; if not state then Exit; if lFile.Count = 0 then Exit; Lock(cs); try for fh in lFile do begin if fh.h = h then begin sPath:= fh.path; Result := TRUE; Break; end; end; finally UnLock(cs); end; end; function TFileHandleList.IsHistoryFileBufferCompare(const path: string; lpBuffer: PByte; size: DWORD; out fileHandle: TFileHandle): BOOL; var fb: TFILE_BUFFER; fh: TFileHandle; i, nPos: Integer; sBuff: string; srcname, dstname: string; begin nPos := 0; Result := FALSE; //DVLOG('IsHistoryFileBufferCompare: lHisFile.Count(%d), %d', [lHisFile.Count, DWORD(state)]); if not state then Exit; if lHisFile.Count = 0 then Exit; Lock(lHcs); try for fh in lHisFile do begin if (path <> '') and (fh.path <> '') then begin srcname := ExtractFileName(path); dstname := ExtractFileName(fh.path); var diff:= SameText(srcname, dstname); DVLOG('IsHistoryFileBufferCompare: srcname(%s), dstname(%s), diff(%d)', [PChar(srcname), PChar(dstname), DWORD(diff)]); if not diff then Continue; end; for fb in fh.lbuf do begin // for i := 0 to fb.size - 1 do // sBuff := sBuff + IntToHex(fb.buf[i], 2); //DVLOG('IsHistoryFileBufferCompare: sBuff(%s)', [sBuff]); if UtilFindCode(lpBuffer, size, @fb.buf, fb.size, nPos) = nil then Continue; fileHandle := TFileHandle.Create; fileHandle.Assign(fh); Result := TRUE; Exit; end; end; finally UnLock(lHcs); end; end; function TFileHandleList.IsBufferCompare(const path: string; buffer: PByte; size: DWORD; out fileHandle: TFileHandle; bInterNetWriteFile: BOOL): BOOL; var fh: TFileHandle; fb: TFILE_BUFFER; i, nPos: Integer; srcname, dstname: string; sBuff: string; begin Result := FALSE; // DVLOG('IsBufferCompare: lFile.Count(%d), %d', [lFile.Count, DWORD(state)]); // if lFile.Count = 0 then // Exit; if not state then Exit; Lock(cs); try for fh in lFile do begin if (path <> '') and (fh.path <> '') then begin srcname := ExtractFileName(path); dstname := ExtractFileName(fh.path); var diff:= SameText(srcname, dstname); DVLOG('IsBufferCompare: srcname(%s), dstname(%s), diff(%d)', [PChar(srcname), PChar(dstname), DWORD(diff)]); if not diff then begin Continue; // fileHandle := TFileHandle.Create; // fileHandle.Assign(fh); // // ClearHistory; // Result := TRUE; // Exit; end; end; for fb in fh.lbuf do begin if UtilFindCode(buffer, size, @fb.buf, fb.size, nPos) = nil then Continue; fileHandle := TFileHandle.Create; fileHandle.Assign(fh); ClearHistory; Result := TRUE; // DVLOG('IsBufferCompare: (%s)', [fh.path]); Exit; end; end; finally UnLock(cs); end; if IsHistoryFileBufferCompare(path, buffer, size, fileHandle) then begin Result := TRUE; end; end; end.