{*******************************************************} { } { ThdReaction } { } { Copyright (C) 2023 kku } { } {*******************************************************} unit ThdReaction; interface uses Tocsg.Thread, System.SysUtils, System.Classes, Winapi.Windows, System.Generics.Collections, ManagerCampaign; type PRespEnt = ^TRespEnt; TRespEnt = record Kind: TCampnRespaction; sPath: String; dwTick: DWORD; nDelaySec: Integer; bIgrProc, bUseTO, bNoExp: Boolean; end; TThdReaction = class(TTgThread) protected qEnts_: TQueue; EncIgrList_, AipExtList_, AfterEncList_: TStringList; bIgrEnc_: Boolean; procedure OnEntNotify(Sender: TObject; const Item: PRespEnt; Action: TCollectionNotification); procedure SetIgrEnc(bVal: Boolean); function GetIgrEnc: Boolean; procedure Execute; override; public Constructor Create; Destructor Destroy; override; procedure AddEnt(aKind: TCampnRespaction; sPath: String; nDelaySec: Integer = 0; bTO: Boolean = true; bNoExp: Boolean = false); procedure AddEncIgr(sPath: String); procedure AddAfterEnc(sPath: String); function HasEncIgr(sPath: String; bChkDel: Boolean = false): Boolean; property IgrEnt: Boolean write SetIgrEnc; end; implementation uses Tocsg.Exception, Tocsg.Delete, Tocsg.DRM.Encrypt, Tocsg.Encrypt, GlobalDefine, Tocsg.Files, ManagerService, Tocsg.Safe, Condition, Tocsg.Path, Tocsg.Process, ManagerModel, System.DateUtils, Tocsg.Trace, superobject, CrmUtil, Define, Tocsg.AIP, Tocsg.Strings, CttSchDefine, Tocsg.Kess, Tocsg.Convert, Tocsg.Fasoo, Winapi.ActiveX, Tocsg.Hash; var _dwIgrDrmTick: DWORD = 0; { TThdReaction } Constructor TThdReaction.Create; begin Inherited Create; qEnts_ := TQueue.Create; EncIgrList_ := TStringList.Create; EncIgrList_.CaseSensitive := false; AfterEncList_ := TStringList.Create; AfterEncList_.CaseSensitive := false; AipExtList_ := TStringList.Create; AipExtList_.CaseSensitive := false; SplitString(AIP_EXTS, '|', AipExtList_); bIgrEnc_ := false; end; Destructor TThdReaction.Destroy; begin Inherited; FreeAndNil(AipExtList_); FreeAndNil(AfterEncList_); FreeAndNil(EncIgrList_); qEnts_.OnNotify := OnEntNotify; FreeAndNil(qEnts_); end; procedure TThdReaction.OnEntNotify(Sender: TObject; const Item: PRespEnt; Action: TCollectionNotification); begin if Action = cnRemoved then Dispose(Item); end; procedure TThdReaction.AddEnt(aKind: TCampnRespaction; sPath: String; nDelaySec: Integer = 0; bTO: Boolean = true; bNoExp: Boolean = false); var pEnt: PRespEnt; enum: TEnumerator; sType: String; begin try if (aKind = crtDRM) or (aKind = crtAIP) then begin if GetIgrEnc then exit; // case CUSTOMER_TYPE of // CUSTOMER_WELFNI, // CUSTOMER_WELFND : // begin // // FastDownloader.exe 감지되면 3초간 암호화 대응 정지 25_1124 08:22:04 kku // if (_dwIgrDrmTick <> 0) then // begin // if ((GetTickCount - _dwIgrDrmTick) <= 3000) then // begin // _Trace('[대응] 파일 암호화, "FastDownloader.exe" 감지, 무시, Path=%s', [sPath], 5); // exit; // end else // _dwIgrDrmTick := 0; // end; // // // 파수 복호화 파일 다운로드가 실행 되어 있으면 암호화 예외 25_0624 08:38:12 kku // if (aKind = crtDRM) and (GetProcessPidByName('FastDownloader.exe') <> 0) then // begin // _Trace('[대응] 파일 암호화, "FastDownloader.exe" 감지, 3초간 무시, Path=%s', [sPath], 5); // _dwIgrDrmTick := GetTickCount; // exit; // end; // end; // end; end; Lock; try if (nDelaySec > 0) and (qEnts_.Count > 0) then begin // 지연처리 사용 시 동일 파일이 있을 경우 앞선 작업은 무시되도록 처리 24_1206 13:17:59 kku Guard(enum, qEnts_.GetEnumerator); while enum.MoveNext do begin if CompareText(enum.Current.sPath, sPath) = 0 then enum.Current.bIgrProc := true; end; end; New(pEnt); ZeroMemory(pEnt, SizeOf(TRespEnt)); pEnt.Kind := aKind; pEnt.sPath := sPath; pEnt.dwTick := GetTickCount; pEnt.nDelaySec := nDelaySec; pEnt.bUseTO := bTO; pEnt.bNoExp := bNoExp; qEnts_.Enqueue(pEnt); finally Unlock; end; sType := 'Unknown'; case aKind of crtNone : sType := '없음'; crtDelete : sType := '삭제'; crtPerDel : sType := '완전삭제'; crtDRM : sType := '암호화'; crtAIP : sType := 'AIP암호화'; end; _Trace('대응 추가 : [%s] 유예 시간 : %d초, Path=%s', [sType, nDelaySec, sPath], 4); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. AddEnt()'); end; end; procedure TThdReaction.AddEncIgr(sPath: String); var i: Integer; begin Lock; try try i := EncIgrList_.IndexOf(sPath); if i <> - 1 then EncIgrList_.Delete(i); EncIgrList_.AddObject(sPath, TObject(Now)); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. AddEncIgr()'); end; finally Unlock; end; end; procedure TThdReaction.AddAfterEnc(sPath: String); var i: Integer; begin Lock; try try i := AfterEncList_.IndexOf(sPath); if i <> - 1 then AfterEncList_.Delete(i); AfterEncList_.AddObject(sPath, TObject(Now)); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. AddAfterEnc()'); end; finally Unlock; end; end; function TThdReaction.HasEncIgr(sPath: String; bChkDel: Boolean = false): Boolean; var i: Integer; begin Result := false; Lock; try try i := EncIgrList_.IndexOf(sPath); Result := i <> - 1; if Result and bChkDel then EncIgrList_.Delete(i); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. HasEncIgr()'); end; finally Unlock; end; end; procedure TThdReaction.SetIgrEnc(bVal: Boolean); begin Lock; try if bVal <> bIgrEnc_ then bIgrEnc_ := bVal; finally Unlock; end; end; function TThdReaction.GetIgrEnc: Boolean; begin Lock; try Result := bIgrEnc_; finally Unlock; end; end; procedure TThdReaction.Execute; var pEnt: PRespEnt; bResult, bDoAfterEnc: Boolean; i, n, nResult: Integer; dt, dtNow: TDateTime; h: HWND; sExt, sTaskDir, sEncPath, sAipLabelId: String; O: ISuperObject; LogInfo: TLogInfo; PO: TPrefModel; fas: TTgFasoo; //mgkim 20251203 파일 접근 가능 여부를 체크 하기 위함 function CheckAcessOpenReadFile(sPath: string; size: int64): Boolean; var fs: TFileStream; begin Result := false; try Guard(fs, TFileStream.Create(sPath, fmOpenRead)); Result:= True; except on E: Exception do ETgException.TraceException(E, Format('Fail .. CheckAcessOpenReadFile, size=%d', [size]), 5); end; end; function FsDecFile(sSrcPath: String; var sDecPath: String): Boolean; var bIsEnc: Boolean; begin Result := false; sDecPath := ''; try var nEncType: Integer := fas.GetFileType(sSrcPath); bIsEnc := (nEncType = 103) {FSN}; // or (nEncType = 106) {NX}; if bIsEnc then begin // 파수는 확장자 안넣어주면 암/복호화 동작안하는 버전이 있어서 넣어줌 25_0414 14:49:21 kku sDecPath := sTaskDir + Format('%d-dc_%d.%s', [GetTickCount, ConvStrToHash(sSrcPath), GetFileExt(sSrcPath)]); if fas.DoExtract(sSrcPath, sDecPath, true) then Result := true; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. FsDecFile()'); end; end; function FsEncFile(sPath: String): Boolean; var enc: TTgDrmEnc; sEncPath: String; begin Result := false; try sEncPath := sTaskDir + Format('%d-dc_%s', [GetTickCount, ExtractFileName(sPath)]); if (fas <> nil) and (fas.GetFileType(sPath) = 29) then begin // if not MoveFile_wait(sPath, sEncPath, 3) then if not CopyFile(PChar(sPath), PChar(sEncPath), false) then exit; DeleteFile(PChar(sPath)); var nResult: Integer := -1; if fas.DoPackagingFsn2(sEncPath, sPath, @nResult) then begin DeleteFile(PChar(sEncPath)); Result := true; end else begin _Trace('Fail .. FASOO DRM .. Code=%d', [nResult]); // MoveFile_wait(sEncPath, sPath, 3); if CopyFile(PChar(sEncPath), PChar(sPath), false) then DeleteFile(PChar(sEncPath)); end; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. FsEncFile()'); end; end; Label LB_DoEnc; begin try fas := nil; if UseFasooDecrypt then begin var bLoadFas: Boolean := true; case CUSTOMER_TYPE of CUSTOMER_WELFNI : SetDSD_CODE(DSD_CODE_WFNI); CUSTOMER_WELFND : SetDSD_CODE(DSD_CODE_WFND); else bLoadFas := false; end; if bLoadFas then begin var sFsDir: String := GetRunExePathDir + 'fsdinit'; if not DirectoryExists(sFsDir) then sFsDir := GetRunExePathDir + 'conf\fsdinit'; if DirectoryExists(sFsDir) then begin CoInitializeEx(nil, COINIT_APARTMENTTHREADED); fas := TTgFasoo.Create(sFsDir); end; end; end; try while not GetWorkStop and not Terminated do begin pEnt := nil; bDoAfterEnc := false; Sleep(500); // 일정 시간 뒤 자동 암호화 구현 24_0624 16:36:52 kku Lock; try if AfterEncList_.Count > 0 then begin dtNow := Now; for i := 0 downto AfterEncList_.Count - 1 do begin dt := TDateTime(AfterEncList_.Objects[i]); if SecondsBetween(dt, dtNow) >= 30 then begin bDoAfterEnc := true; New(pEnt); if IsUseEncOnlyAIP or gMgSvc.FirstAip then pEnt.Kind := crtAIP else pEnt.Kind := crtDRM; pEnt.sPath := AfterEncList_[i]; pEnt.dwTick := GetTickCount; AfterEncList_.Delete(i); break; end; end; end; finally Unlock; end; if bDoAfterEnc and (pEnt <> nil) then begin AddEncIgr(pEnt.sPath); goto LB_DoEnc; end; // 암호화 예외 타임아웃 처리 24_0624 16:36:38 kku Lock; try if EncIgrList_.Count > 0 then begin dtNow := Now; for i := 0 downto EncIgrList_.Count - 1 do begin dt := TDateTime(EncIgrList_.Objects[i]); if SecondsBetween(dt, dtNow) >= 10 then EncIgrList_.Delete(i); end; end; finally Unlock; end; Lock; try if qEnts_.Count > 0 then pEnt := qEnts_.Dequeue else pEnt := nil; finally Unlock; end; if pEnt = nil then continue; if pEnt.bIgrProc then begin Dispose(pEnt); pEnt := nil; continue; end; if (pEnt.nDelaySec > 0) and ( ((GetTickCount - pEnt.dwTick) div 1000) < pEnt.nDelaySec ) then begin Lock; try qEnts_.Enqueue(pEnt); finally Unlock; end; continue; end; LB_DoEnc : try case pEnt.Kind of crtDRM, crtAIP : _Trace('대응 : 준비, Path=%s', [pEnt.sPath], 5); end; bResult := true; // 암호화 예외 처리, 파일 변경 감시 [대응] 암호화 사용 중 // 파일 복호화 시 적용 24_0624 16:33:20 kku if not bDoAfterEnc then begin Lock; try n := EncIgrList_.IndexOf(pEnt.sPath); if n <> -1 then begin _Trace('암호화 예외 처리 : %s', [pEnt.sPath], 1); EncIgrList_.Delete(n); continue; end; finally Unlock; end; end; if FileExists(pEnt.sPath) then begin case pEnt.Kind of crtDRM, crtAIP : _Trace('대응 : 파일 확임됨, Path=%s', [pEnt.sPath], 5); end; case pEnt.Kind of crtDelete : begin bResult := DeleteFile(PChar(pEnt.sPath)); // _Trace('대응 : 파일 삭제, 결과=%s, Path=%s', [BooleanToStr(bResult, '성공', '실패'), pEnt.sPath], 5); end; crtPerDel : begin bResult := PerfectDeleteFile(pEnt.sPath, 3); // _Trace('대응 : 파일 완전 삭제, 결과=%s, Path=%s', [BooleanToStr(bResult, '성공', '실패'), pEnt.sPath], 5); end; crtDRM : begin if UseFasooEncrypt and (fas <> nil) then begin bResult := GetFileSize_path(pEnt.sPath) = 0; try sTaskDir := 'C:\ProgramData\HE\FsTask\'; if not bResult and ForceDirectories(sTaskDir) then begin case CUSTOMER_TYPE of CUSTOMER_WELFNI, CUSTOMER_WELFND : begin if fas.GetFileType(pEnt.sPath) <> 29 then begin // 암호화 되어 있는지, 기본정보등급 암호화 인지 확인 후 개인정보등급으로 변경 25_0625 16:11:48 kku var sTemp: String := fas.GetFileHeader(pEnt.sPath); if sTemp <> '' then begin var WorkList: TStringList; Guard(WorkList, TStringList.Create); SplitString(sTemp, ';', WorkList, true); if WorkList.Count > 11 then begin // 보안코드 12번째 있음 // 개인보안 등급이 아니면 다시 암호화 25_0408 10:21:32 kku if WorkList[11] <> _sSecurityLevel then begin var sDecPath: String := ''; AddEncIgr(sDecPath); if FsDecFile(pEnt.sPath, sDecPath) and FileExists(sDecPath) then begin bResult := FsEncFile(sDecPath); if bResult and FileExists(sDecPath) then begin if DeleteFile(PChar(pEnt.sPath)) then begin AddEncIgr(pEnt.sPath); // MoveFile_wait(sDecPath, pEnt.sPath); if CopyFile(PChar(sDecPath), PChar(pEnt.sPath), false) then DeleteFile(PChar(sDecPath)); end; end else DeleteFile(PChar(sDecPath)); _Trace('대응 : 파일 Fs암호화, 결과=%s, Path=%s', [BooleanToStr(bResult, '성공', '실패'), pEnt.sPath], 5); end; end; end; end; end else bResult := FsEncFile(pEnt.sPath); end; else bResult := FsEncFile(pEnt.sPath); end; if bResult then begin ZeroMemory(@LogInfo, SizeOf(LogInfo)); LogInfo.sCode := PREVENT_DRM_ENCRYPT; LogInfo.sSummary := '[FsDRM] ' + ExtractFileName(pEnt.sPath); LogInfo.sPath := pEnt.sPath; gMgSvc.SendEventLogEx(@LogInfo); end; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. Respaction(Fasoo) .. enc'); end; end else begin var downFileSize:= GetFileSize_path(pEnt.sPath); bResult := NotUseDRM or (downFileSize = 0); try sTaskDir := 'C:\ProgramData\HE\EncTask\'; if not bResult and ForceDirectories(sTaskDir) then begin //mgkim 20251203 파일 접근 가능 여부를 체크 하기 위함 //CheckSign에서 open fail에 대한 처리를 못해줘서 암호화 파일인 경우도 접근 오류가 //날경우 암호화 로직을 수행한다. if CheckAcessOpenReadFile(pEnt.sPath, downFileSize) then begin bResult := TTgEncrypt.CheckSign(pEnt.sPath, SIG_DRM); if not bResult then begin var sTgEncPath: String := GetSameFileNameInc(sTaskDir + '$Tmp' + ExtractFileName(pEnt.sPath)); PO := gMgSvc.ModePolicy; if (PO.FileMon.Kind <> fmkNone) and PO.FileMon.DeleteEvt and (gMgSvc.FileService <> nil) then begin gMgSvc.FileService.AddIgr1Path(pEnt.sPath); end; // if MoveFile_wait(pEnt.sPath, sTgEncPath, 3) then if CopyFile(PChar(pEnt.sPath), PChar(sTgEncPath), false) then begin DeleteFile(PChar(pEnt.sPath)); SaveStrToFile(sTgEncPath + '.i', pEnt.sPath, TEncoding.UTF8); var enc: TTgDrmEnc; var sDept: String := gMgSvc.DeptName; if sDept = '' then sDept := gMgSvc.PrefModel.DeptName; if not pEnt.bNoExp then AddEncIgr(pEnt.sPath); enc := TTgDrmEnc.Create(pEnt.sPath); try enc.SetHaed(PASS_DRM_HEAD, SIG_DRM, gMgSvc.EmpNo, gMgSvc.UserName, sDept, gMgSvc.PrefModel.PolicyName, CUSTOMER_TYPE); if enc.EncryptFromFile(GetMK, sTgEncPath) then begin DeleteFile(PChar(sTgEncPath)); DeleteFile(PChar(sTgEncPath + '.i')); bResult := true; end; finally FreeAndNil(enc); end; if bResult then begin ZeroMemory(@LogInfo, SizeOf(LogInfo)); LogInfo.sCode := PREVENT_DRM_ENCRYPT; LogInfo.sSummary := '[DRM] ' + ExtractFileName(pEnt.sPath); LogInfo.sPath := pEnt.sPath; gMgSvc.SendEventLogEx(@LogInfo); end else begin // MoveFile_wait(sTgEncPath, pEnt.sPath, 3); if CopyFile(PChar(sTgEncPath), PChar(pEnt.sPath), false) then DeleteFile(PChar(sTgEncPath)); DeleteFile(PChar(sTgEncPath + '.i')); TTgTrace.T('Fail .. TThdReaction.EncFile(), Path=%s', [pEnt.sPath]); end; end; _Trace('대응 : 파일 암호화, 결과=%s, Path=%s, Size=%d', [BooleanToStr(bResult, '성공', '실패'), pEnt.sPath, downFileSize], 5); end; end; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. Respaction() .. enc'); end; end; end; crtAIP : begin // KvCttSch.exe 에서 처리됨 23_1024 09:36:04 kku sTaskDir := 'C:\ProgramData\HE\AEN\'; sExt := GetFileExt(pEnt.sPath).ToUpper; bResult := (GetFileSize_path(pEnt.sPath) = 0) or ( (AipExtList_.IndexOf(sExt) = -1) or IsAipEncryted(pEnt.sPath, sTaskDir) ); try if not bResult and (IsUseEncOnlyAIP or gMgSvc.FirstAip) then begin h := gMgSvc.FindAipMdWnd; if h <> 0 then begin sAipLabelId := GetDefAipLabelId(CUSTOMER_TYPE = CUSTOMER_DEV); if CUSTOMER_TYPE = CUSTOMER_KDNVN then begin // 파일 이름에 따라 레이블 지정되도록 기능 추가 24_1125 17:10:27 kku var sFName: String := ExtractFileName(pEnt.sPath); if (sFName <> '') then begin var sLName: String := ''; if sFName[1] = '(' then sLName := GetCapsuleStr('(', ')', sFName) else if sFName[1] = '[' then sLName := GetCapsuleStr('[', ']', sFName); if sLName <> '' then begin sLName := StringReplace(sLName.ToLower, ' ', '', [rfReplaceAll]); // 공백제거 if (sLName = '일반') or (sLName = '一般') or (sLName = 'generalpurpose') or (sLName = 'ommaviyfoydalanishuchun') or (sLName = 'общедоступный') then sAipLabelId := '7c1e97b0-1625-488a-a30c-4f2188693461' else if (sLName = '대외비') or (sLName = '对外保密') or (sLName = 'confidential') or (sLName = 'ichkifoydalanish') or (sLName = 'длявнутреннего пользования') then sAipLabelId := 'e7bd3a2c-b905-4bdb-8892-e6cd8d1eb00b' else if (sLName = '기밀') or (sLName = '机密') or (sLName = 'secret') or (sLName = 'sirli') or (sLName = 'секретный') then sAipLabelId := '7d1539f9-8f3b-4cbd-b787-7ae1b30eae22' else if (sLName = '극비') or (sLName = '绝密') or (sLName = 'topsecret') or (sLName = 'judamaxfiy') or (sLName = 'строгосекретный') then sAipLabelId := 'b40a7c1a-5443-458b-9215-2abda0acc4a4'; end; end; end; if ForceDirectories(sTaskDir) and (sAipLabelId <> '') then begin sEncPath := ConvAipEncExt(GetSameFileNameInc(sTaskDir + '$Tmp' + ExtractFileName(pEnt.sPath))); PO := gMgSvc.ModePolicy; if (PO.FileMon.Kind <> fmkNone) and PO.FileMon.DeleteEvt and (gMgSvc.FileService <> nil) then begin gMgSvc.FileService.AddIgr1Path(pEnt.sPath); end; var sDecPath: String := ''; if (CUSTOMER_TYPE = CUSTOMER_KDNVN) and (KCT_IsEncrypt(pEnt.sPath) = RESULT_SUCCESS) then begin sDecPath := GetSameFileNameInc(sTaskDir + '$Tmpp' + ExtractFileName(pEnt.sPath)); var dwResult: DWORD := KCT_Decrypt(pEnt.sPath, 2, sDecPath); if dwResult <> RESULT_SUCCESS then begin _Trace('Fail .. KESS Decrypt() .. Code=%x', [dwResult]); sDecPath := ''; end; end; O := SO; O.S['src'] := BooleanToStr(sDecPath = '', pEnt.sPath, sDecPath); O.S['dst'] := sEncPath; O.S['lid'] := sAipLabelId; if gMgSvc.Email <> '' then O.S['mail'] := gMgSvc.Email; nResult := SendData(h, 5, O.AsString); // _Trace('SetAipLabel() .. Result=%d, Path=%s, EncPath=%s', [nResult, sPath, sEncPath]); // 0 : 레이블 추출, 1 : 암호화, 2 : 복호화, 3 : 암호화 확인, 4 : 레이블 확인, 5 : 레이블 설정 if (nResult = 10) and FileExists(sEncPath) then begin if sDecPath <> '' then DeleteFile(PChar(sDecPath)); if DeleteFileForce(pEnt.sPath) then begin var sCpPath: String := pEnt.sPath; if CheckMsPfileExt(sCpPath) then begin if CheckAipEncSign(sEncPath) then sCpPath := sCpPath + '.pfile'; end else sCpPath := CutFileExt(sCpPath) + '.' + GetFileExt(sEncPath); AddEncIgr(sCpPath); if CopyFile(PChar(sEncPath), PChar(sCpPath), false) then // 이거 실패 대비를 해야 할까... 23_1024 09:04:08 kku begin bResult := true; ZeroMemory(@LogInfo, SizeOf(LogInfo)); LogInfo.sCode := PREVENT_DRM_ENCRYPT; LogInfo.sSummary := '[AIP] ' + ExtractFileName(pEnt.sPath); LogInfo.sPath := pEnt.sPath; gMgSvc.SendEventLogEx(@LogInfo); // MessageBox(Handle, PChar('AIP 암호화가 완료되었습니다.'), PChar(APP_TITLE), MB_ICONINFORMATION or MB_OK); end; end; end else begin TTgTrace.T('Fail .. TThdReaction.EncFile(AIP), Path=%s, Result=%d', [pEnt.sPath, nResult]); // nResult = 2면 현재 파일사용 중, 6이면 지원하지 않는 포맷 pdf, xls등 bResult := nResult = 6; end; if FileExists(sEncPath) then DeleteFile_wait(sEncPath); if gMgSvc.PrefModel.AipEncMSec > 0 then Sleep(gMgSvc.PrefModel.AipEncMSec); _Trace('대응 : 파일 AIP암호화, 결과=%s, Path=%s', [BooleanToStr(bResult, '성공', '실패'), pEnt.sPath], 5); end else if sAipLabelId = '' then begin {$IFDEF DEBUG} ASSERT(false); {$ENDIF} end; end; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. Encrypt AIP()'); end; end; end; if not bResult then begin var nTO: Integer := 300000; // 기본 5분 if pEnt.bUseTO and (pEnt.dwTick > nTO) then nTO := pEnt.dwTick * 2; if not pEnt.bUseTO or ((GetTickCount - pEnt.dwTick) < nTO) then begin Lock; try qEnts_.Enqueue(pEnt); finally Unlock; end; end else bResult := true; end; end else bResult := true; // 파일 없음 finally if bResult then begin Dispose(pEnt); pEnt := nil; end; end; end; finally if fas <> nil then FreeAndNil(fas); end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. Execute()'); end; end; end.