{*******************************************************} { } { ThdPrintWork } { } { Copyright (C) 2025 kku } { } {*******************************************************} unit ThdPrintWork; interface uses Tocsg.Thread, Tocsg.Printer, System.SysUtils, System.Classes, System.Generics.Collections, Winapi.Windows, Tocsg.Fasoo; type PPrtHookJobEnt = ^TPrtHookJobEnt; TPrtHookJobEnt = record dtReg: TDateTime; sPName, sPrtName, sDocName, sPrtDocId: String; ullPid: ULONGLONG; end; TPrtHookJobEntList = TList; TThdPrintWork = class(TTgThread) protected JobList_: TList; fas_: TTgFasoo; bIsWorking_: Boolean; CurJob_: TPrtJobInfo; PrintAppList_, IgrPrtSpoolAnal_: TStringList; bBackupSpl_: Boolean; sWorkDir_: String; PrtHookJobEntList_: TPrtHookJobEntList; // 후킹방식으로 프린트 워터마크 출력 시 예외 되었는지 체크 bPrtWaterExp_: Boolean; procedure OnPrtHe(Sender: TObject; const Item: PPrtHookJobEnt; Action: TCollectionNotification); procedure Execute; override; procedure ProcessPrintJob(aJob: TPrtJobInfo); procedure AddIgrPrtSpool(sPrtName: String); function IsSpoolAnal(sPrtName: String): Boolean; function GetHookJob(sPrtName, sDocName: String; bFindDel: Boolean = false): PPrtHookJobEnt; procedure RefineHookJob; public WordPrtPage: Integer; Constructor Crate; Destructor Destroy; override; procedure AddJob(aJob: TPrtJobInfo); function HasJob(aJob: TPrtJobInfo): Boolean; procedure AddHookJob(sPName, sPrtName, sDocName, sPrtDocId: String; ullPid: ULONGLONG); property IsWorking: Boolean read bIsWorking_; property PrtWaterExp: Boolean write bPrtWaterExp_; end; implementation uses Tocsg.Exception, Tocsg.Files, Tocsg.Path, Winapi.ActiveX, Tocsg.Safe, Tocsg.Strings, Tocsg.Shell, Tocsg.Trace, superobject, ManagerService, GlobalDefine, IdHTTP, IdSSLOpenSSL, IdMultipartFormData, Tocsg.DateTime, System.IniFiles, ManagerModel, ManagerPattern, Tocsg.Convert, Condition, Winapi.WinSpool, ManagerPrint, Tocsg.Process, ThdWebUrl, Tocsg.WndUtil, Tocsg.Registry, CrmUtil, ProcessSoftcampDRM, Tocsg.Encrypt, System.DateUtils, Tocsg.DRM.Encrypt, Tocsg.PCRE, ManagerRule, CttSchDefine, ThdExecuteEndNoti, ManagerCampaign, Tocsg.Valid, AppCtrlDefine, Vcl.Printers, Vcl.Graphics, Vcl.Imaging.pngimage, DefineHelper, ManagerHook; { TThdPrintWork } Constructor TThdPrintWork.Crate; var sPath: String; ini: TIniFile; begin Inherited Create; bPrtWaterExp_ := false; JobList_ := TList.Create; bIsWorking_ := false; bBackupSpl_ := false; WordPrtPage := 0; sWorkDir_ := 'C:\ProgramData\HE\'; PrtHookJobEntList_ := TPrtHookJobEntList.Create;; PrintAppList_ := TStringList.Create; PrintAppList_.CaseSensitive := false; sPath := GetProgramFilesDir + DIR_TG + INI_FORCEHE; var sPrtApps: String; case CUSTOMER_TYPE of CUSTOMER_GEC, CUSTOMER_HDENG : sPrtApps := RPINT_SUPPORT_APPS_HEC; else sPrtApps := PRINT_SUPPORT_APPS; end; if FileExists(sPath) then begin try Guard(ini, TIniFile.Create(sPath)); sPrtApps := sPrtApps + '|' + ini.ReadString('Force', 'PrtWaterApp', ''); bBackupSpl_ := ini.ReadBool('Force', 'BackupSpool', false); except // .. end; end; SplitString(sPrtApps, '|', PrintAppList_, false, true); RefinePrintHookAppList(PrintAppList_); IgrPrtSpoolAnal_ := TStringList.Create; IgrPrtSpoolAnal_.CaseSensitive := false; sPath := GetRunExePathDir + INI_HE; if FileExists(sPath) then begin try Guard(ini, TIniFile.Create(sPath)); SplitString(ini.ReadString('Print', 'IgrSpool', ''), '|', IgrPrtSpoolAnal_); except // .. end; end; end; Destructor TThdPrintWork.Destroy; begin if fas_ <> nil then FreeAndNil(fas_); FreeAndNil(IgrPrtSpoolAnal_); FreeAndNil(PrintAppList_); FreeAndNil(JobList_); PrtHookJobEntList_.OnNotify := OnPrtHe; FreeAndNil(PrtHookJobEntList_); Inherited; end; procedure TThdPrintWork.OnPrtHe(Sender: TObject; const Item: PPrtHookJobEnt; Action: TCollectionNotification); begin if Action = cnRemoved then Dispose(Item); end; procedure TThdPrintWork.AddJob(aJob: TPrtJobInfo); begin Lock; try JobList_.Add(aJob); finally Unlock; end; end; function TThdPrintWork.HasJob(aJob: TPrtJobInfo): Boolean; begin Result := false; try Lock; try if CurJob_ = aJob then Exit(true); if JobList_.IndexOf(aJob) <> -1 then Exit(true); finally Unlock; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. HasJob()'); end; end; procedure TThdPrintWork.AddHookJob(sPName, sPrtName, sDocName, sPrtDocId: String; ullPid: ULONGLONG); var pEnt: PPrtHookJobEnt; begin try New(pEnt); ZeroMemory(pEnt, SizeOf(TPrtHookJobEnt)); pEnt.dtReg := Now; pEnt.sPName := sPName; pEnt.sPrtName := sPrtName; pEnt.sDocName := sDocName; pEnt.sPrtDocId := sPrtDocId; pEnt.ullPid := ullPid; Lock; try PrtHookJobEntList_.Add(pEnt); finally Unlock; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. AddHookJob()'); end; end; function TThdPrintWork.GetHookJob(sPrtName, sDocName: String; bFindDel: Boolean = false): PPrtHookJobEnt; var i: Integer; pEnt: PPrtHookJobEnt; begin Result := nil; try Lock; try for i := 0 to PrtHookJobEntList_.Count - 1 do begin pEnt := PrtHookJobEntList_[i]; if (CompareText(sPrtName, pEnt.sPrtName) = 0) and (CompareText(sDocName, pEnt.sDocName) = 0) then begin Result := pEnt; if bFindDel then PrtHookJobEntList_.Delete(i); exit; end; end; finally Unlock; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. GetHookJob()'); end; end; procedure TThdPrintWork.RefineHookJob; var i: Integer; dtNow: TDateTime; begin try dtNow := Now; Lock; try for i := PrtHookJobEntList_.Count - 1 downto 0 do begin if MinutesBetween(PrtHookJobEntList_[i].dtReg, dtNow) > 60 then PrtHookJobEntList_.Delete(i); end; finally Unlock; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. RefineHookJob()'); end; end; procedure TThdPrintWork.AddIgrPrtSpool(sPrtName: String); begin try if IgrPrtSpoolAnal_.IndexOf(sPrtName) = -1 then begin IgrPrtSpoolAnal_.Add(sPrtName); var i: Integer; var sIgrPrts: String := ''; for i := 0 to IgrPrtSpoolAnal_.Count - 1 do SumString(sIgrPrts, IgrPrtSpoolAnal_[i], '|'); var sPath: String := GetRunExePathDir + INI_HE; var ini: TIniFile; Guard(ini, TIniFile.Create(sPath)); ini.WriteString('Print', 'IgrSpool', sIgrPrts); end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. AddIgrPrtSpool()'); end; end; function TThdPrintWork.IsSpoolAnal(sPrtName: String): Boolean; begin Result := true; exit; Result := IgrPrtSpoolAnal_.IndexOf(sPrtName) = -1; end; procedure TThdPrintWork.ProcessPrintJob(aJob: TPrtJobInfo); var PO, PPO: TPrefModel; PP: TPrintPolicy; pPhJob: PPrtHookJobEnt; sPrtName, sDocName, sPort, sPrtDocId, sChk, sData, sPName, sPPath, sDocPath, sPrtIp, sExtrOcrTxt, sThumbIds, sExtrOcr180Txt, sExtrTxt, sSpoolPath, sEmfDir, sBlockReson: String; ini: TIniFile; ExcpList: TStringList; i, nTotalPages: Integer; bBlock, bIsDrm, bCfActive, bWaterMark, bIgrApproval, bApprovalPost: Boolean; PrtInfo: TPrtJobDevInfo; OVio, OVioTemp, OEnt: ISuperObject; PatternEntList: TPatternEntList; bIsHook, bIsSplAnal, bSpoolEnd: Boolean; procedure SetProcessName(sName: String); begin if sPName <> sName then begin sPName := sName; bIsHook := IsPrintWaterHook; if bIsHook and not IsHD then begin if PrintAppList_.IndexOf(sPName) = -1 then bIsHook := false; end; bIsSplAnal := not bIsHook; if bIsSplAnal then bIsSplAnal := IsSpoolAnal(sPrtName); end; end; function FindPrintingFile(sDocName: String; bIgrRB: Boolean = false): String; var sDir, sChk, sUserDir: String; DList: TStringList; FList: TModFileList; i, n: Integer; // Plf: TParserLinkFile; begin Result := ''; sDir := GetWindowsDir; if sDir = '' then exit; CoInitialize(nil); try Guard(DList, TStringList.Create); Guard(FList, TModFileList.Create(TModeFileComparer.Create)); sUserDir := UpperCase(sDir[1]) + ':\Users\'; ExtrDirFromDir(sUserDir, DList); for i := 0 to DList.Count - 1 do begin sDir := sUserDir + DList[i] + '\AppData\Roaming\Microsoft\Windows\Recent\'; ExtrModFilesFromDir(sDir, FList); end; if FList.Count > 0 then begin FList.Sort; if bIgrRB then sDocName := StrsReplace(sDocName, ['(', ')', '[', ']'], '0'); for i := 0 to FList.Count - 1 do begin if bIgrRB then sChk := StrsReplace(CutFileExt(FList[i].sFName), ['(', ')', '[', ']'], '0') else sChk := CutFileExt(FList[i].sFName); // 최근문서 파일처리 보완 24_0722 14:54:30 kku if (sChk.Length > 0) and (sChk[sChk.Length] = ')') then begin n := LastIndexOf(' (', sChk); if n > 0 then Delete(sChk, n, sChk.Length - n + 1); end; // TTgTrace.T('Check RecentFileName = "%s"', [sChk], 3); if Pos(sChk, sDocName) > 0 then begin Result := GetTargetExeFromLink(FList[i].sDir + FList[i].sFName); if Result = '' then continue; // 간혹 사용자 폴더가 이상하게(?) 표시되는 경우가 있다.. 보정 24_0514 10:09:24 kku if (Result <> '') and (Pos('windows\system32\config\systemprofile', Result.ToLower) > 0) then begin TTgTrace.T('Replace path = "%s"', [Result], 3); sDir := StringReplace(FList[i].sDir, '\AppData\Roaming\Microsoft\Windows\Recent\', '', [rfReplaceAll, rfIgnoreCase]); sDir := ExtractFileName(sDir); Result := StringReplace(Result, 'windows\system32\config\systemprofile', Format('Users\%s', [sDir]), [rfReplaceAll, rfIgnoreCase]); TTgTrace.T('Replace Path done = "%s"', [Result], 3); end; TTgTrace.T('Found Path = "%s", CheckName = "%s", RecentName = "%s"', [Result, sChk, FList[i].sFName], 3); if DirectoryExists(Result) or not FileExists(Result) then continue; // Guard(Plf, TParserLinkFile.Create); // if Plf.LoadFromFile(sDir + FList[i].sName) then // begin // Result := GetLfiValueFromCaption(Plf.LfiEntList, 'Base Path'); // if not FileExists(Result) then // Result := ''; // end; exit; end; end; // PDF의 경우... 문서 이름에 파일 이름이 안들어 가는 경우가 있다... // PDF 파일 최근파일 내역을 찾을 수 없다면 맨 첫번째 있는 PDF 파일을 넘겨준다.. 24_0418 15:10:27 kku if Pos('.PDF', sDocName.ToUpper) > 0 then begin for i := 0 to FList.Count - 1 do begin sChk := CutFileExt(FList[i].sFName); if GetFileExt(sChk).ToUpper = 'PDF' then begin Result := sChk; exit; end; end; end; end; finally CoUninitialize; end; end; procedure SendReqPrint(sExtrTxt, sFName, sPName: String); var O: ISuperObject; sMsg: String; begin sExtrTxt := StringReplace(sExtrTxt, #13#10, ' ', [rfReplaceAll]); if nTotalPages = 0 then nTotalPages := 1; sMsg := Format('PRINTER:%s|TEXT:%s|FILENAME:%s|TYPE:6|EVTTIME:%s|PAGE:%d|APP:%s|DESC:None|SCANRESULT:-1', [sPrtName, sExtrTxt, sFName, FormatDateTime('yyyy-mm-dd hh:nn:ss', Now), nTotalPages, sPName]); // DirectSendEventLog(URI_USER_ACTION, REQUEST_PRINT_EXCEPT, sMsg); gMgSvc.SendEventLog(URI_USER_ACTION, REQUEST_PRINT_EXCEPT, sMsg); end; function DirectSendPtrFile(sPath: String): Boolean; var HTTP: TIdHTTP; SSL: TIdSSLIOHandlerSocketOpenSSL; ss: TStringStream; sBoundary, sDestFileUrl: String; Params: TIdMultiPartFormDataStream; msResp: TMemoryStream; begin Result := false; if not FileExists(sPath) then exit; try sDestFileUrl := gMgSvc.DestServerUrl; sDestFileUrl := StringReplace(gMgSvc.DestServerUrl, 'agentLogRequest.do', 'agentPrintExcept.do', [rfReplaceAll]); sDestFileUrl := StringReplace(sDestFileUrl, 'agentLogRequests.do', 'agentPrintExcept.do', [rfReplaceAll]); sBoundary := Format('%X', [GetLocalIncUtcMin * 6000]); Guard(SSL, TIdSSLIOHandlerSocketOpenSSL.Create(nil)); SSL.SSLOptions.Method := sslvSSLv23; SSL.SSLOptions.SSLVersions := [sslvTLSv1_2, sslvTLSv1_1, sslvTLSv1]; Guard(HTTP, TIdHTTP.Create(nil)); HTTP.IOHandler := SSL; with HTTP do begin HandleRedirects := true; Request.Clear; Request.UserAgent := 'Mozilla/5.0'; Request.ContentType := 'application/xml'; Request.AcceptCharSet := 'UTF-8'; // Request.Connection := 'Keep-Alive'; // Request.CustomHeaders.Values['Keep-Alive'] := 'timeout=300, max=100'; Request.Connection := 'close'; HTTPOptions := HTTPOptions - [hoKeepOrigProtocol]; HTTPOptions := HTTP.HTTPOptions + [hoForceEncodeParams]; ConnectTimeout := 2000; ReadTimeout := 2000; Request.ContentType := Format('multipart/form-data; boundary=%s; charset=utf-8', [sBoundary]); end; Guard(msResp, TMemoryStream.Create); Guard(Params, TIdMultiPartFormDataStream.Create); with Params.AddFile(ExtractFileName(sPath), sPath, Format('application/%s', [GetFileExt(sPath)])) do begin ContentTransfer := ''; HeaderEncoding := '8'; //8bit HeaderCharSet := 'utf-8'; Charset := 'utf-8'; end; Params.Position := 0; HTTP.Post(sDestFileUrl, Params, msResp); Result := HTTP.ResponseCode = 200; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. DirectSendPtrFile()'); end; end; function GetSpoolCopyPath(sSubDirName: String = 'Task'): String; var sHookPName, sRecentSool, sSpoolWorkDir: String; i: Integer; begin Result := ''; try // if CUSTOMER_TYPE = CUSTOMER_CJOV_GLOBAL then // sSpoolWorkDir := GetRunExePathDir + 'Task\' + sSubDirName + '\' + // IncludeTrailingPathDelimiter(GetValidFileName(sPrtName, '#')) // else sSpoolWorkDir := sWorkDir_ + sSubDirName + '\' + IncludeTrailingPathDelimiter(GetValidFileName(sPrtName, '#') + Format('-%d', [GetTickCount64])); DeleteDir(sSpoolWorkDir); if ForceDirectories(sSpoolWorkDir) then begin _Trace('프린터 출력 감지 .. Spool 수집 시작. Printer=%s, Doc=%s', [sPrtName, sDocName], 2); while (aJob <> nil) and aJob.IsSpooling2 do begin if i = 4000 then // 200초간 기다려준다 23_0525 08:03:11 kku break; Inc(i); Sleep(50); end; // 스풀링 끝나고 정지가 풀리는 프린터가 있다? // 의심되서 추가 25_0618 08:45:08 kku aJob.PausePrtJob; sRecentSool := GetLastSpoolPath(GetSystemDir + 'spool\PRINTERS\'); _Trace('프린터 출력 감지 .. Spool 확인. Path=%s', [sRecentSool], 2); {$IF false} sRecentSool := 'C:\Users\kku\Desktop\이전 바탕화면\spl2pdf_cmd\PPT그라데이션_20250911093157.spl'; if FileExists(sRecentSool) and ForceDirectories('C:\ProgramData\HE\Test_Prt\') then begin Result := Format('C:\ProgramData\HE\Test_Prt\%s.spl', [FormatDateTime('yyyymmddhhnnss', Now)]); CopyFile(PChar(sRecentSool), PChar(Result), false); end; {$ELSE} if FileExists(sRecentSool) then begin Result := sSpoolWorkDir + ExtractFileName(sRecentSool); CopyFile(PChar(sRecentSool), PChar(Result), false); if bBackupSpl_ and ForceDirectories('C:\ProgramData\HE\') then CopyFile(PChar(sRecentSool), PChar(Format('C:\ProgramData\HE\%s.spl', [FormatDateTime('yyyymmddhhnnss', Now)])), false); // Sleep(500); // if not FileExists(Result) or (GetFileSize_path(Result) = 0) then // begin // // 스플 경로가 System 권한으로 접근하기 어려운 경우 현재 사용자 권한으로 다시 시도 24_1212 13:17:40 kku // var sExe: String := GetRunExePathDir + DIR_CONF + EXE_HLP; // if FileExists(sExe) then // begin // var O: ISuperObject := SO; // O.I['Cmd'] := HPCMD_COPY_FILE; // O.S['Src'] := sRecentSool; // O.S['Dest'] := Result; // SaveJsonObjToFile(O, GetRunExePathDir + DIR_CONF + DAT_PARAM); // {$IFDEF DEBUG} // ExecutePath(sExe); // {$ELSE} // ExecuteAppAsUser('explorer.exe', sExe, '', SW_SHOWNORMAL); // {$ENDIF} // end; // end; if not FileExists(Result) then begin Result := ''; _Trace('프린터 출력 감지 .. Spool 수집 실패. Printer=%s, Doc=%s', [sPrtName, sDocName], 2); end else begin _Trace('프린터 출력 감지 .. Spool 수집 성공. Printer=%s, Doc=%s, SrcSize=%d, DestSize=%d', [sPrtName, sDocName, GetFileSize_path(sRecentSool), GetFileSize_path(Result)], 2); end; end else TTgTrace.T('Not found spool file..'); {$IFEND} // 스풀링 끝나면 정확한 정보를 위해 한번더 가져오기 시도 aJob.GetJobDevInfo(PrtInfo); bSpoolEnd := true; // 스풀 PJL 포맷이면 정확한 부수 정보 가져오기 if FileExists(Result) and IsPJL(Result) then begin if not IsPJLAndLanguagePLW(sSpoolPath) then begin var bCollate: Boolean; var nCopies: Integer := GetQtyFromPJL(sSpoolPath, bCollate); if (nCopies > 1) and (PrtInfo.dwCopyCount = 1) then begin _Trace('PJL 포맷에서 부수정보 다름 확인 .. PJL=%d, Job=%d', [nCopies, PrtInfo.dwCopyCount], 2); PrtInfo.dwTotalPage := PrtInfo.dwTotalPage div nCopies; PrtInfo.dwCopyCount := nCopies; end; end; end; if pPhJob = nil then begin pPhJob := GetHookJob(sPrtName, sDocName, true); if pPhJob <> nil then begin bIsHook := true; bIsSplAnal := not bIsHook; sPPath := GetProcessPathByPid(pPhJob.ullPid); if sPPath <> '' then begin sPName := ExtractFileName(sPPath); sPPath := ExtractFilePath(sPPath); end else sPName := pPhJob.sPName; sPrtDocId := pPhJob.sPrtDocId; _Trace('프린트 Hook 프로세스 확인(ex) : %s', [sPName], 1); end; // else begin // sHookPName := GetRegValueAsString(HKEY_USERS, gMgSvc.RecentUserSid + '\Software\eCrmHomeEdition', 'PrtPName'); // if sHookPName = '' then // sHookPName := GetRegValueAsString(HKEY_USERS, gMgSvc.RecentUserSid + '\Software\WOW6432Node\eCrmHomeEdition', 'PrtPName'); // // if sHookPName <> '' then // begin // _Trace('프린트 Hook 프로세스 확인 : %s', [sHookPName], 1); // if sHookPName <> sPName then // begin // _Trace('프린트 Hook 프로세스와 인식된 프로세스가 다름. 조치 시도, Cur=%s, New=%s', [sPName, sHookPName], 1); // SetProcessName(sHookPName); // end; // end else begin // bIsHook := false; // bIsSplAnal := not bIsHook; // if bIsSplAnal then // bIsSplAnal := IsSpoolAnal(sPrtName); // _Trace('프린트 Hook 프로세스 아님 : %s', [sPName], 1); // end; // end; end; // DelRegValue(HKEY_USERS, gMgSvc.RecentUserSid + '\Software\eCrmHomeEdition', 'PrtPName'); // DelRegValue(HKEY_USERS, gMgSvc.RecentUserSid + '\Software\WOW6432Node\eCrmHomeEdition', 'PrtPName'); end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. GetSpoolCopyPath()'); end; end; function GetEmfCopyDir(sSubDirName: String = 'PrtColi'): String; var sRecentSool, sSpoolWorkDir: String; i: Integer; begin Result := ''; try while (aJob <> nil) and aJob.IsSpooling2 do begin if i = 4000 then // 200초간 기다려준다 23_0525 08:03:11 kku break; Inc(i); Sleep(50); end; // Sleep(500); if DirectoryExists(sWorkDir_ + 'PrtCol\') then begin if DirectoryExists(sWorkDir_ + sSubDirName) then DeleteDir(sWorkDir_ + sSubDirName); if MoveFile_wait(sWorkDir_ + 'PrtCol\', sWorkDir_ + sSubDirName, 3, true) then Result := IncludeTrailingPathDelimiter(sWorkDir_ + sSubDirName); end else if DirectoryExists(sWorkDir_ + sSubDirName) then Result := IncludeTrailingPathDelimiter(sWorkDir_ + sSubDirName); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. GetSpoolCopyPath()'); end; end; function GetPrintLog(sCode: String): ISuperObject; var O, OSub: ISuperObject; sCompId: String; sMsg: String; dt: TDateTime; begin Result := nil; try if bIsHook then bWaterMark := (PP.PrintWater <> pwNone) and not bPrtWaterExp_; sMsg := Format('Printer : %s, Document : %s', [sPrtName, sDocName]); PO := gMgSvc.ModePolicy; PPO := gMgSvc.PrefModel; dt := Now; O := SO; if sCode = LOGCODE_EVENT_PRINTER then O.S['TYP_MSG'] := '@(!)_IC_M' else O.S['TYP_MSG'] := '@(!)_IC_P'; O.S['KEY_AGENTID'] := gMgSvc.AgentId; O.S['KEY_EMPNO'] := gMgSvc.EmpNo; O.S['KEY_HOSTNAME'] := gMgSvc.UserName; O.S['KEY_SUBMITTIME'] := FormatDateTime('yyyy-mm-dd hh:nn:ss', dt); O.S['key_submitTime'] := FormatDateTime('yyyy-MM-dd"T"hh:nn:ss.zzz', dt) + gMgSvc.UtcOffset; O.S['KEY_LOGCODE'] := sCode; O.S['DETECTION_DATE'] := O.S['KEY_SUBMITTIME']; // 이벤트 발생 시각. REQUEST의 경우, 예외 신청할 이벤트의 발생 시각 O.S['detectionDate'] := O.S['key_submitTime']; // 이벤트 발생 시각. REQUEST의 경우, 예외 신청할 이벤트의 발생 시각 O.S['KEY_SUMMARY'] := sMsg; // O.S['PARENT_LA_ID'] // 상위 이벤트 O.S['POLICY_ID'] := PO.PolicyId; // O.S['POLICY_KEY'] // 정책 위반한 경우 (PREVENT, MONITOR), 위반한 정책의 정책 키 값, 수집인 경우 (DEPLOY), 수집 요청 ID if PO.Print.bCollectOutput then begin if PPO.TextLimit > 0 then begin if Length(sExtrTxt) > PPO.TextLimit then SetLength(sExtrTxt, PPO.TextLimit); if Length(sExtrOcrTxt) > PPO.TextLimit then SetLength(sExtrOcrTxt, PPO.TextLimit); end; O.S['MESSAGE_BODY'] := sExtrTxt; // 정책 위반한 경우 (PREVENT, MONITOR), 원문 (본문) 컨텐츠, 수집인 경우 (DEPLOY), 수집 결과 내용, 원문 수집 정책이 비활성화인경우 수집하지 않음 O.S['OCR_BODY'] := sExtrOcrTxt; // 이미지 OCR 추출된 컨텐츠, 원문 수집 정책이 비활성화인 경우 수집하지 않음 end; if OVio <> nil then O.O['RULE_VIOLATED'] := OVio; // 정책 위반 규칙과 위반 개수. (LIST) if FileExists(sDocPath) then begin if PO.Print.bCollectFile then begin sCompId := gMgSvc.MakeComponentId(ExtractFileName(sDocPath)); // SendFile(sCompId, 'printLogCollect.do', sDocPath); // 원본 파일 수집은 이걸로 하면 안됨 24_0122 16:52:00 kku gMgSvc.SendFileNor(false, sCompId, 'quarantineLogCollect.do', sDocPath, PO.PrtMinMB, PO.PrtMaxMB); O.S['COMPONENT_ID'] := sCompId; // 파일 고유값. 에이전트에서 임의 생성. (이벤트간 동일 파일 판단은 서버에서 하겠습니다.) end; O.S['COMPONENT_FILENAME'] := ExtractFileName(sDocPath); // 파일명 O.S['COMPONENT_PATH'] := ExtractFilePath(sDocPath);// 파일 절대 경로 var dtCreate, dtModify, dtAccess: TDateTime; GetFileDateTime_Local(sDocPath, dtCreate, dtModify, dtAccess); var dwAttr: DWORD := GetFileAttributes(PChar(sDocPath)); var OTemp: ISuperObject := SO; OTemp.S['FILESIZE'] := IntToStr(GetFileSize_path(sDocPath)); OTemp.S['LASTMODIFIED'] := FormatDateTime('yyyy-mm-dd hh:nn:ss', dtModify); if dwAttr <> DWORD(-1) then OTemp.S['READONLY'] := BooleanToStr((dwAttr and FILE_ATTRIBUTE_READONLY) <> 0, 'true', 'false'); if dwAttr <> DWORD(-1) then OTemp.S['HIDDEN'] := BooleanToStr((dwAttr and FILE_ATTRIBUTE_HIDDEN) <> 0, 'true', 'false'); // OTemp.S['AUTHOR'] // {만든 이} // OTemp.S['LASTUSER'] // {마지막으로 저장한 사람} // OTemp.S['FILETYPE'] // {컨텐츠 타입} // OTemp.S['FILECLS'] // {컨텐츠 형식} // OTemp.S['PASSWORD'] // {암호적용여부} // OTemp.S['ENCRYPTED'] := BooleanToStr(pInfo.bDrm, 'true', 'false'); // OTemp.S['CORRUPT'] // {깨진파일/정상파일여부} O.O['COMPONENT_METADATA'] := OTemp; O.S['COMPONENT_LASTACCESS'] := FormatDateTime('yyyy-mm-dd hh:nn:ss', dtAccess); end; O.S['COMPONENT_THUMBNAIL_ID'] := sThumbIds; // 이미지 미리보기 혹은 이미지가 있는경우, 이미지 파일의 파일 고유값. (출력물의 경우 첫 3장 이미지, 캡쳐 이미지 등), 복수개 허용, ";" 구분 // O.S['SOURCE_IP'] // 이벤트 발생 시점의 에이전트 IP. 단, 외부에서 수신하는 이벤트의 경우, 발송지의 IP가 되고, DESTINATION_URL이 에이전트 IP가 된다 // O.S['SOURCE_PORT'] // 송신지 사용 포트 // O.S['EMAIL_SENDER'] // 메일 발신자 // O.S['DESTINATION_URL'] // 수신지 URL (혹은 IP) O.S['DESTINATION_PORT'] := sPrtIp; // 수신지 포트 // O.S['EMAIL_RECIPIENT'] // 메일 수신자 ; 로 구분 // O.S['EMAIL_SUBJECT'] // 메일 제목 O.S['APPLICATION_NAME'] := sPName; // 사용 APP 이름 O.S['APPLICATION_PATH'] := sPPath;// 사용 APP 절대 경로 (실행 파일 exe의 절대 경로) O.S['PRINTER_JOBNAME'] := sDocName; OSub := SO; OSub.S['PAGEINFO'] := Format('%d/%d', [PrtInfo.dwTotalPage, PrtInfo.dwTotalPage]); OSub.S['COLOR'] := BooleanToStr(PrtInfo.bColor, 'true', 'false'); OSub.S['WARTERMARK'] := BooleanToStr(bWaterMark, 'true', 'false'); OSub.S['PAPERINFO'] := PrtInfo.sPaperInfo; O.O['PRINTER_METADATA'] := OSub; // '{"PAGEINFO": "2/2", "COLOR": "true", "WARTERMARK": "false"}'; if bWaterMark then O.S['RESPONSE_INFO'] := 'WATERMARK'; O.S['REMOVABLE_NAME'] := sPrtName; // USB등과 같은 매체 혹은 프린터 등과 같은 외부 장치 이름 // O.S['REMOVABLE_DEVICEID'] := aExptInfo.sSerial; // USB등과 같은 매체 혹은 프린터 등과 같은 외부 장치 Device ID // O.S['DEVICE_TYPE'] := IntToStr(Integer(aExptInfo.ReqDevType)); // 의미 없어서 제외 23_0822 12:40:55 kku // O.S['REMOVABLE_CLASSID'] // USB등과 같은 매체 혹은 프린터 등과 같은 외부 장치 Class ID // O.S['RESPONSE_INFO'] // 적용된 대응 정보 (DELETE, DRM, POPUP, LOG, EXCEPT 등) // O.S['RESPONSE_RESULT'] // 적용된 대응 적용 결과 (true / false / pending / decline) // O.S['REQUEST_COMMENT'] := sComment; // 사용자가 기입하는 요청 코멘트 // O.S['REQUEST_DUEDATE'] := sDueDate; // 사용자가 기입하는 예외 기간 (언제 까지. 최종일) // O.S['REQUEST_TARGET'] := IntToStr(nTarget); // 사용자가 기입하는 예외 대상 (0 - 해당 pc, 1 - 해당 사용자, 2 - 해당 부서, 3-전체) O.S['actionGroupId'] := sPrtDocId; // 문서번호 O.I['printCopyCnt'] := PrtInfo.dwCopyCount; // 부수 O.I['printPageCnt'] := PrtInfo.dwTotalPage; // 장수 Result := O; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. SendEventLog()'); end; end; procedure SendPrintLog(sCode: String); var O: ISuperObject; begin try O := GetPrintLog(sCode); if O = nil then begin _Trace('Fail .. SendPrintLog() .. 1', 3); exit; end; _Trace('SendPrintLog > Code=%s, Msg=%s', [sCode, O.S['KEY_SUMMARY']], 3); // JSON 포맷으로 잘 변환되서 보내도록 보완 24_0716 10:32:32 kku gMgSvc.EventLog.Push(O.AsJSon); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. SendEventLog()'); end; end; procedure CancelJob; begin if (aJob <> nil) and not aJob.WorkEnd then begin if not bSpoolEnd then aJob.GetJobDevInfo(PrtInfo); CurJob_ := nil; aJob.WorkEnd := true; // SetPrtJobFromHelperApp(aJob, JOB_CONTROL_CANCEL); // SetPrtJobFromHelperApp(aJob, JOB_CONTROL_DELETE); aJob.SetPrtJob(JOB_CONTROL_CANCEL); // Sleep(1000); // aJob.SetPrtJob(JOB_CONTROL_DELETE); // Job을 삭제하면 aJob가 해제 되서 아래처럼 처리함 25_0522 16:33:40 kku aJob := nil; end; end; function CollectPrintInfo(sDocPath, sText, sOcrText, sVioText: String; sThumbIds: String = ''): Boolean; var sConv: String; begin Result := false; if bIgrApproval then exit; if not IsApproveSupport then exit; // 결재자가 할당 되어 있나? if not gMgSvc.UseApproval then exit; // 결재요청 정책이 있나? if not PO.PrintApproval then exit; // 후킹방식은 지원하지 않음 // if IsPrintWaterHook then // exit; try // 한번 멈추고 진행하도록 변경 25_1203 kku // if PO.PrintApprovalPost then // begin // var PrtEnt: TPrtEnt; // ZeroMemory(@PrtEnt, SizeOf(PrtEnt)); //// PrtEnt.dtReg := Now; //// PrtEnt.sId := sId; // PrtEnt.sText := sText; // PrtEnt.sOcrText := sOcrText; // PrtEnt.sVioText := sVioText; // PrtEnt.sThumbIds := sThumbIds; // PrtEnt.sPName := sPName; // PrtEnt.sFPath := sDocPath; // // PrtEnt.WInfo.sPtrName := PrtInfo.sPtrName; // PrtEnt.WInfo.sDocName := PrtInfo.sDocName; // PrtEnt.WInfo.DevMode := TTgRtti.SetTypeToStr(PrtInfo.DevMode); // PrtEnt.WInfo.dwCopy := PrtInfo.dwCopyCount; // PrtEnt.WInfo.dwTotalPage := PrtInfo.dwTotalPage; // PrtEnt.WInfo.bPaperV := PrtInfo.bPaperV; // PrtEnt.WInfo.bColor := PrtInfo.bColor; // PrtEnt.WInfo.sPaperInfo := PrtInfo.sPaperInfo; // PrtEnt.WInfo.sPrtIp := PrinterDriverToIP(PrtInfo.sDrvName); // PrtEnt.WInfo.sPdfPath := sPort; // PrtEnt.WInfo.bUseWM := true; // var sUrl: String := gMgSvc.SendApproval(13, @PrtEnt); // // if sUrl = '' then // begin // TTgTrace.T('Fail .. CollectPrintInfo(PrintApprovalPost)'); // exit; // end else begin // var LogInfo: TLogInfo; // ZeroMemory(@LogInfo, SizeOf(LogInfo)); // LogInfo.sCode := REQUEST_APPROVAL; // LogInfo.sDevName := PrtEnt.WInfo.sPtrName; // LogInfo.sPath := PrtEnt.WInfo.sDocName; // LogInfo.sActionId := PrtEnt.WInfo.sPrtDocId; // LogInfo.sSummary := 'Post-Approval Request : Print'; // gMgSvc.SendEventLogEx(@LogInfo, false); // // var sHlpExe: String := GetRunExePathDir + DIR_CONF + EXE_HLP; // if FileExists(sHlpExe) then // begin // var O: ISuperObject := SO; // O.I['RcvWnd'] := Handle; // O.I['Cmd'] := HPCMD_EXECUTE_FILE; // O.S['Path'] := gMgSvc.DestIPort + sUrl; // SaveJsonObjToFile(O, GetRunExePathDir + DIR_CONF + DAT_PARAM); // {$IFDEF DEBUG} // ExecutePath(sHlpExe); // {$ELSE} // ExecuteAppAsUser('explorer.exe', sHlpExe, '', SW_SHOWNORMAL); // {$ENDIF} // end else // ExecutePath(gMgSvc.DestIPort + sUrl); // // bBlock := false; // 차단 헤제 // bApprovalPost := true; // end; // end else begin sConv := GetRunExePathDir + DIR_CONF + EXE_SPL; sEmfDir := GetEmfCopyDir; if (DirectoryExists(sEmfDir) or FileExists(sConv)) and not IsHD then begin if (sSpoolPath = '') or not FileExists(sSpoolPath) then begin sSpoolPath := GetSpoolCopyPath; _Trace('CollectPrintInfo() .. GetSpoolCopyPath() .. Path=%s', [sSpoolPath]); end; if FileExists(sSpoolPath) then begin // 워드는 부수, 한부씩인쇄 값을 가져올 수 없어서 아래처럼 처리 if CompareText('winword.exe', sPName) = 0 then PrtInfo.DevMode.dmCollate := 1; if IsPJL(sSpoolPath) then begin _Trace('CollectPrintInfo() .. PJL 포맷 확인됨', 1); if IsPJLAndLanguagePLW(sSpoolPath) then begin _Trace('CollectPrintInfo() .. PJL 포맷, PLW 스플 확인됨. 결재 처리 불가.', 1); exit; end; var bCollate: Boolean; var nCopies: Integer := GetQtyFromPJL(sSpoolPath, bCollate); if nCopies > 0 then begin PrtInfo.dwCopyCount := nCopies; PrtInfo.DevMode.dmCopies := nCopies; if bCollate then PrtInfo.DevMode.dmCollate := 1; _Trace('CollectPrintInfo() .. PJL 포맷 부수 정보 : %d', [nCopies], 1); end; end; if aJob <> nil then CancelJob; if PrtInfo.sPtrName = '' then PrtInfo.sPtrName := sPrtName; if PrtInfo.sDocName = '' then PrtInfo.sDocName := sDocName; var pEnt: PPrtEnt := gMgSvc.MgPrint.AddPrintInfo(PrtInfo, sPName, sDocPath, sSpoolPath, sEmfDir, sPort, sText, sOcrText, sVioText, sThumbIds, PO.PrintApprovalPost); Result := pEnt <> nil; if Result and (gMgSvc.RcvHwnd <> 0) then begin SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 3, 0); pEnt.WInfo.sPrtDocId := sPrtDocId; PostMessage(gMgSvc.RcvHwnd, WM_REQUEST_APPROVAL, 0, NativeInt(pEnt)); end; end; end; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. OnPtrJobNotify() > CollectPrintInfo()'); end; end; // function CheckBlockException(var sReason: String): Boolean; function CheckBlockException: Boolean; var i: Integer; sReason: String; // 차단 이유를 기록하기 위함이었는데 이제 사용하지 않음.. 일단 정리 전에 놔둠 begin Result := false; if PP.sPrinterExcepts <> '' then begin // 프린터 예외 확인 Guard(ExcpList, TStringList.Create); SplitString(PP.sPrinterExcepts, '|', ExcpList); sChk := sPrtName.ToUpper; for i := 0 to ExcpList.Count - 1 do begin if Pos(ExcpList[i], sChk) > 0 then begin Result := true; sReason := 'PTR'; _Trace('프린터 출력 감지 .. 프린터 종류 예외. Printer=%s, Doc=%s', [sPrtName, sDocName], 2); exit; end; end; end; if PO.PrinterIpExcept <> '' then begin // 프린터 IP 예외 추가 24_1030 13:24:10 kku var sChkPrtIp: String := StringReplace(sPrtIp, '_', '.', [rfReplaceAll]); var StrList: TStringList; Guard(StrList, TStringList.Create); SplitString(PO.PrinterIpExcept, '|', StrList); for i := 0 to StrList.Count - 1 do begin if Pos(StrList[i], sChkPrtIp) > 0 then begin Result := true; sReason := 'PTR_IP'; _Trace('프린터 출력 감지 .. 프린터 IP 예외. Printer=%s, IP=%s, Doc=%s', [sPrtName, sChkPrtIp, sDocName], 2); exit; end; end; end; if (PP.sPrintUrlExcepts <> '') and (gMgSvc.ThdWebUrl <> nil) then begin // 브라우저 URL 확인 Guard(ExcpList, TStringList.Create); SplitString(PP.sPrintUrlExcepts, '|', ExcpList); if (sPName <> '') and (Pos(LowerCase(sPName), BROWSER_LIST) > 0) then // 브라우저인지 확인 begin var sUrl: String := ''; try sUrl := gMgSvc.ThdWebUrl.LastUrl; except // .. end; if sUrl <> '' then begin sChk := UpperCase(sUrl); for i := 0 to ExcpList.Count - 1 do begin if Pos(ExcpList[i], sChk) > 0 then begin Result := true; sReason := 'URL|' + sUrl; _Trace('프린터 출력 감지 .. URL 예외. Printer=%s, URO=%s, Doc=%s', [sPrtName, sUrl, sDocName], 2); exit; end; end; end; end; end; if PP.sPrintAppExcepts <> '' then begin // App 확인 Guard(ExcpList, TStringList.Create); SplitString(PP.sPrintAppExcepts, '|', ExcpList); if sPName <> '' then begin sChk := UpperCase(sPName); for i := 0 to ExcpList.Count - 1 do begin if Pos(ExcpList[i], sChk) > 0 then begin Result := true; sReason := 'APP|' + sPName; _Trace('프린터 출력 감지 .. APP 예외. Printer=%s, PName=%s, Doc=%s', [sPrtName, sPName, sDocName], 2); exit; end; end; end; end; if PP.sPrintDocExcepts <> '' then begin // 문서명 확인 Guard(ExcpList, TStringList.Create); SplitString(PP.sPrintDocExcepts, '|', ExcpList); sChk := UpperCase(sDocName); for i := 0 to ExcpList.Count - 1 do begin if Pos(ExcpList[i], sChk) > 0 then begin Result := true; sReason := 'APP|' + sPName; _Trace('프린터 출력 감지 .. 문서이름 예외. Printer=%s, Doc=%s', [sPrtName, sDocName], 2); break; end; end; end; end; function ProcessContentFilter_AfterExit(sTargetTxt: String; bIsOcrTxt: Boolean): Boolean; var i, nHits, nTotalHits, nOrCnt, nAndCnt, nHighCnt: Integer; sFound, sResult, sSchTxt: String; begin Result := false; if OVio <> nil then begin // 1. 문서, 2. OCR 두번 시도될 수 있는데, 문서에서 이미 검출된게 있다면 넘어간다 exit; end; sFound := ''; sResult := ''; nOrCnt := 0; nAndCnt := 0; nHighCnt := 0; sSchTxt := ''; nTotalHits := 0; OVioTemp := TSuperObject.Create(stArray); for i := 0 to PatternEntList.Count - 1 do begin sSchTxt := PatternEntList[i].GetSearchText; nHits := TTgPcre.GetMatchValues(sTargetTxt, sSchTxt, sFound); if nHits > 0 then begin if gMgSvc.IsNewApi then begin if nHits < PatternEntList[i].IfCount then continue; if sSchTxt.StartsWith('(? 0 then begin var bFoundOk: Boolean := true; if gMgSvc.IsNewApi then begin if (nHighCnt = 0) and (PatternEntList.AndCount > 0) then begin // AND 갯수가 다르다면 X if nAndCnt <> PatternEntList.AndCount then bFoundOk := false; // OR가 조건으로 있는데 검출된 OR가 없다면 X if (PatternEntList.AndCount <> PatternEntList.Count) and (nOrCnt = 0) then bFoundOk := false; end; end; if bFoundOk then OVio := OVioTemp else sResult := ''; end; // if bIsOcrTxt and (nTotalHits = 0) then // begin // // 이미지 180도 돌려서 한번더 처리.. 시연을 위해 추가됨, 나중에 비활성 필요 // sFound := ''; // sResult := ''; // sExtrOcr180Txt := ExtrTextFromPrintImgFiles(sExportPath, 180); // // // 이거 사용할거면 위 처리처럼 바꿔야함 24_0214 14:02:37 kku // for i := 0 to PrintPatternEnts_.Count - 1 do // begin // nHits := TTgPcre.GetMatchValues(sExtrOcr180Txt, PrintPatternEnts_[i].GetSearchText, sFound); // if nHits > 0 then // begin // Inc(nTotalHits, nHits); // if gMgSvc.IsNewApi then // SumString(sResult, Format('%s(%d)', [gMgSvc.MgRule.GetRuleNameFromId(PrintPatternEnts_[i].Name), nHits]), ',') // else // SumString(sResult, Format('%s(%d)', [CttCodeToStr(PrintPatternEnts_[i].Name), nHits]), ','); // // OEnt := SO; // OEnt.S['RULE_ID'] := PrintPatternEnts_[i].Name; // OEnt.S['CNT'] := IntToStr(nHits); // OEnt.S['TEXT'] := RemoveOverlapWords(sFound); // // if OVio = nil then // OVio := TSuperObject.Create(stArray); // OVio.AsArray.Add(OEnt); // end; // end; // // if nTotalHits > 0 then // sTargetTxt := sExtrOcr180Txt; // end; if OVio <> nil then begin bBlock := (PP.PrintKind = pkBlock) and (PP.ContentFilter.nHitLimit <= nTotalHits) and not bApprovalPost; if bBlock then begin if CheckBlockException then begin // 예외 대상인건 검출되도 차단 안함 25_1106 13:40:07 kku _Trace('프린터 출력 감지 .. 컨텐츠 감지. 차단 예외됨. Content=%s, Printer=%s, Doc=%s, OCR=%s', [sResult, sPrtName, sDocName, BooleanToStr(bIsOcrTxt, 'Y', 'N')], 2); bBlock := false; exit; end; if CollectPrintInfo(sDocPath, sExtrTxt, sExtrOcrTxt, OVio.AsJSon, sThumbIds) then begin SendPrintLog(LOGCODE_PREVENT_PRINTER); if bIsOcrTxt then _Trace('프린터 출력 감지 .. OCR 컨텐츠 감지, 출력 승인 요청. Content=%s, Printer=%s, Doc=%s', [sResult, sPrtName, sDocName], 2) else _Trace('프린터 출력 감지 .. 컨텐츠 감지, 출력 승인 요청. Content=%s, Printer=%s, Doc=%s', [sResult, sPrtName, sDocName], 2); Result := true; exit; end; sBlockReson := 'CTT|' + sResult; if bIsOcrTxt then _Trace('프린터 출력 감지 .. OCR 컨텐츠 감지 차단. Content=%s, Printer=%s, Doc=%s', [sResult, sPrtName, sDocName], 2) else _Trace('프린터 출력 감지 .. 컨텐츠 감지 차단. Content=%s, Printer=%s, Doc=%s', [sResult, sPrtName, sDocName], 2); end; end; end; Label LB_DoPrint; begin try if (aJob = nil) or aJob.WorkEnd then exit; if (aJob.Document <> '') and aJob.Document.Contains(' *BSOne') then begin // SetPrtJobFromHelperApp(aJob, JOB_CONTROL_RESUME); aJob.ResumePrtJob(true); exit; end; try if aJob.IsCustomPause then begin RefineHookJob; pPhJob := nil; ForceDirectories(sWorkDir_); bIsWorking_ := true; bApprovalPost := false; CurJob_ := aJob; sPrtDocId := ''; sPName := ''; bSpoolEnd := false; sPPath := GetProcessPathFromWndHandle(aJob.Wnd); if sPPath <> '' then begin SetProcessName(ExtractFileName(sPPath)); sPPath := ExtractFilePath(sPPath); end else SetProcessName(GetProcessNameFromWndHandle(GetForegroundWindow)); aJob.GetJobDevInfo(PrtInfo); sPrtName := aJob.PrinterName; if sPrtName = '' then sPrtName := PrtInfo.sPtrName; sDocName := aJob.Document; if sDocName = '' then sDocName := PrtInfo.sDocName; sPrtIp := PrinterDriverToIP(PrtInfo.sDrvName); if (sDocName <> '') and sDocName.Contains(' *BSOne') then begin // SetPrtJobFromHelperApp(aJob, JOB_CONTROL_RESUME); aJob.ResumePrtJob(true); exit; end; sPort := aJob.Port; nTotalPages := aJob.TotalPages; PO := gMgSvc.ModePolicy; PPO := gMgSvc.PrefModel; PP := PO.Print; bCfActive := PP.ContentFilter.bActive; OVio := nil; if gMgSvc.IsPrtWaterExcept then PP.PrintWater := pwNone; sData := PPO.PrtNameH; // 워터마크 예외 체크 if PP.PrintWater <> pwNone then begin var StrList: TStringList; Guard(StrList, TStringList.Create); if PO.PrtWtExpUrl <> '' then begin SplitString(UpperCase(PO.PrtWtExpUrl), '|', StrList); if (sPName <> '') and (Pos(LowerCase(sPName), BROWSER_LIST) > 0) and (gMgSvc.ThdWebUrl <> nil) then begin try sChk := UpperCase(gMgSvc.ThdWebUrl.LastUrl); except // .. end; for i := 0 to StrList.Count - 1 do if Pos(StrList[i], sChk) > 0 then begin PP.PrintWater := pwNone; break; end; end; end; if (PP.PrintWater <> pwNone) and (PO.PrtWtExpDocName <> '') then begin SplitString(UpperCase(PO.PrtWtExpDocName), '|', StrList); sChk := UpperCase(PrtInfo.sDocName); for i := 0 to StrList.Count - 1 do if Pos(StrList[i], sChk) > 0 then begin PP.PrintWater := pwNone; break; end; end; if (PP.PrintWater <> pwNone) and (PO.PrtWtExpPrtName <> '') then begin SplitString(UpperCase(PO.PrtWtExpPrtName), '|', StrList); sChk := UpperCase(PrtInfo.sPtrName); for i := 0 to StrList.Count - 1 do if Pos(StrList[i], sChk) > 0 then begin PP.PrintWater := pwNone; break; end; end; if (PP.PrintWater <> pwNone) and (PO.PrtWtExpProcName <> '') then begin SplitString(UpperCase(PO.PrtWtExpProcName), '|', StrList); sChk := UpperCase(sPName); for i := 0 to StrList.Count - 1 do if Pos(StrList[i], sChk) > 0 then begin PP.PrintWater := pwNone; break; end; end; if gMgSvc.MgPwe.CountHash > 0 then begin _Trace('프린트 워터마크 예외 경로 확인...', 2); sDocPath := sDocName; if not FileExists(sDocPath) then begin sDocPath := FindPrintingFile(sDocPath); _Trace('프린트 워터마크 예외 경로 확인 : FindPrintingFile() .. Path="%s"', [sDocPath], 2); end; if (sDocPath = '') and (CompareText('notepad.exe', sPName) = 0) then begin // Windows 11 메모장에서 문서 이름이 현재 파일이 아닌 단순 "메모장"으로 뜨게된다. 23_0322 10:57:54 kku sDocPath := GetWindowCaption(GetForegroundWindow); sDocPath := FindPrintingFile(sDocPath, CompareText(sPName, 'excel.exe') = 0); _Trace('프린트 워터마크 예외 경로 확인 : FindPrintingFile() 22.. Path="%s"', [sDocPath], 2); end; if FileExists(sDocPath) then begin _Trace('프린트 워터마크 예외 확인 .. Path="%s"', [sDocPath], 2); if gMgSvc.MgPwe.HasFileHash(sDocPath) then begin _Trace('프린트 워터마크 예외 확인됨 .. Path="%s"', [sDocPath], 2); PP.PrintWater := pwNone; end; end; end; end; SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 1, 0); sEmfDir := ''; sSpoolPath := ''; if ( (PP.PrintWater <> pwNone) or PP.bCollectOutput or PO.IsPrtCollectThum ) then begin // if PP.PrintWater <> pwNone then // CancelJob; // CJ에서 프린트 정지 하면 스플이 너무 빨리 사라지는 현상이 있어서 미리 구해놓음.. 24_1212 16:11:51 kku // _Trace('ttttttttt, Path=%s', [GetSpoolCopyPath('WTask')], 9); sSpoolPath := GetSpoolCopyPath('WTask'); _Trace('GetSpoolCopyPath(1) .. Path=%s', [sSpoolPath]); if FileExists(sSpoolPath) and not IsPrintWaterHook and (PP.PrintWater <> pwNone) then  CancelJob; sEmfDir := GetEmfCopyDir; end; // 후킹 처리로 생성된 값이 있는지 먼저 확인 25_1103 10:37:28 kku sData := gMgSvc.RecentUserSid + '\Software\eCrmHomeEdition'; if sPrtDocId = '' then begin sPrtDocId := GetRegValueAsString(HKEY_USERS, sData, 'PrtDocId'); if sPrtDocId = '' then begin sData := gMgSvc.RecentUserSid + '\Software\WOW6432Node\eCrmHomeEdition'; sPrtDocId := GetRegValueAsString(HKEY_USERS, sData, 'PrtDocId'); end; end; // 이거 유지할지 고민... 25_1210 18:35:40 kku DelRegValue(HKEY_USERS, gMgSvc.RecentUserSid + '\Software\eCrmHomeEdition', 'PrtDocId'); DelRegValue(HKEY_USERS, gMgSvc.RecentUserSid + '\Software\WOW6432Node\eCrmHomeEdition', 'PrtDocId'); if sPrtDocId = '' then sPrtDocId := StrsReplace(TGUID.NewGuid.ToString, ['{', '}'], ''); try gMgSvc.PrtMaskingStr := ''; sDocPath := ''; sExtrOcrTxt := ''; sExtrOcr180Txt := ''; sThumbIds := ''; bWaterMark := false; bIsDrm := false; bIgrApproval := false; _Trace('프린터 출력 감지 .. Printer=%s, Doc=%s', [sPrtName, sDocName], 2); bBlock := (PP.PrintKind = pkBlock) and not bCfActive; sBlockReson := ''; // 결재요청 예외 체크 if IsApproveSupport and gMgSvc.UseApproval and PO.PrintApproval then begin var StrList: TStringList; Guard(StrList, TStringList.Create); if PO.PrtApvExpUrl <> '' then begin SplitString(UpperCase(PO.PrtApvExpUrl), '|', StrList); if (sPName <> '') and (Pos(LowerCase(sPName), BROWSER_LIST) > 0) and (gMgSvc.ThdWebUrl <> nil) then begin try sChk := UpperCase(gMgSvc.ThdWebUrl.LastUrl); except // .. end; for i := 0 to StrList.Count - 1 do if Pos(StrList[i], sChk) > 0 then begin bIgrApproval := true; break; end; end; end; if not bIgrApproval and (PO.PrtApvExpDocName <> '') then begin SplitString(UpperCase(PO.PrtApvExpDocName), '|', StrList); sChk := UpperCase(PrtInfo.sDocName); for i := 0 to StrList.Count - 1 do if Pos(StrList[i], sChk) > 0 then begin bIgrApproval := true; break; end; end; if not bIgrApproval and (PO.PrtApvExpPrtName <> '') then begin SplitString(UpperCase(PO.PrtApvExpPrtName), '|', StrList); sChk := UpperCase(PrtInfo.sPtrName); for i := 0 to StrList.Count - 1 do if Pos(StrList[i], sChk) > 0 then begin bIgrApproval := true; break; end; end; if not bIgrApproval and (PO.PrtApvExpProcName <> '') then begin SplitString(UpperCase(PO.PrtApvExpProcName), '|', StrList); sChk := UpperCase(sPName); for i := 0 to StrList.Count - 1 do if Pos(StrList[i], sChk) > 0 then begin bIgrApproval := true; break; end; end; end; Guard(PatternEntList, TPatternEntList.Create); if bCfActive then begin if gMgSvc.PrintPatterns.Contains('scanoption') then gMgSvc.SetPatternList(gMgSvc.PrintPatterns, PatternEntList) else gMgSvc.SetRuleToPtrnList(gMgSvc.PrintPatterns, PatternEntList); end; sDocPath := sDocName; if not FileExists(sDocPath) then sDocPath := FindPrintingFile(sDocPath); var bForceSpoolWater: Boolean := false; // not IsPrintWaterHookForce and (CUSTOMER_TYPE = CUSTOMER_CJONS) and (Pos('CANON', UpperCase(PrtInfo.sPtrName)) = 0); // if (bForceSpoolWater or not bIsHook) and (CUSTOMER_TYPE = CUSTOMER_GEC) and (PP.PrintWater <> pwNone) then if CUSTOMER_TYPE = CUSTOMER_GEC then begin // sDocPath := 'D:\temp3\abc_e.docx'; //FindPrintingFile(sDocPath); // 기본 대외비 처리 24_0513 16:28:34 kku var sLabelName: String := 'None'; gMgSvc.RecentLabel := '대외비(Restricted)'; {$IFDEF DEBUG} // 출력 시 AIP 원문 수집 테스트 // sDocPath := 'D:\temp3\123_label.docx'; {$ENDIF} if FileExists(sDocPath) then begin // 오피스 파일은 열려 있는 상태에서 레이블 확인이 불가능하다. 복사해서 처리 24_0513 16:43:49 kku var sTaskDir: String := sWorkDir_ + 'HEC\'; if ForceDirectories(sTaskDir) then begin var sDest: String := sTaskDir + ExtractFileName(sDocPath); if CopyFileAfOpenCheck(sDocPath, sDest) then begin var O: ISuperObject := SO; O.S['src'] := sDest; O.S['dst'] := sDest + '.tmp'; O.S['regval'] := 'LabelA'; // O.S['ssid'] := RecentUserSid; // 비활성화 해서 HKEY_LOCAL_MACHINE 여기서 만들어지게 DelRegValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\eCrmHomeEdition', 'LabelA'); if SendData(gMgSvc.FindAipMdWnd, 4, O.AsString) = 10 then begin // sLabelName := GetRegValueAsString(HKEY_USERS, RecentUserSid + 'SOFTWARE\eCrmHomeEdition', 'ALabel'); sLabelName := GetRegValueAsString(HKEY_LOCAL_MACHINE, 'SOFTWARE\eCrmHomeEdition', 'LabelA'); if sLabelName <> '' then DelRegValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\eCrmHomeEdition', 'LabelA'); if (sLabelName = '') or (Pos('anyuser', sLabelName.ToLower) > 0) then begin PP.PrintWater := pwNone; gMgSvc.RecentLabel := ''; end else begin gMgSvc.RecentLabel := sLabelName; bIsDrm := true; end; end; DeleteFile(PChar(sDest)); end; end; end else _Trace('Fail .. FindPrintingFile() .000. Path="%s"', [sDocPath]); _Trace('프린트 워터마크 레이블 확인 : FindPrintingFile() .. Path="%s", Label=%s', [sDocPath, gMgSvc.RecentLabel], 2); end else if fas_ <> nil then begin if FileExists(sDocPath) then begin var sTaskDir: String := sWorkDir_ + 'HEC\'; if ForceDirectories(sTaskDir) then begin var sDest: String := sTaskDir + ExtractFileName(sDocPath); if CopyFileAfOpenCheck(sDocPath, sDest) then begin var nEncType: Integer := fas_.GetFileType(sDest); bIsDrm := (nEncType = 103) {FSN}; // or (nEncType = 106) {NX}; if bIsDrm then _Trace('Fasoo 암호화 확인 : Path="%s"', [sDocPath], 2); DeleteFile(PChar(sDest)); end; end; end else _Trace('Fail .. FindPrintingFile() .111222. Path="%s"', [sDocPath]); end else if UseSoftcampDecrypt then begin if FileExists(sDocPath) then begin // 오피스 파일은 열려 있는 상태에서 레이블 확인이 불가능하다. 복사해서 처리 24_0513 16:43:49 kku var sTaskDir: String := sWorkDir_ + 'HEC\'; if ForceDirectories(sTaskDir) then begin var sDest: String := sTaskDir + ExtractFileName(sDocPath); if CopyFileAfOpenCheck(sDocPath, sDest) then begin // bIsDrm := DS_IsEncrypted(sDest); bIsDrm := DSCSIsEncryptedFile(sDest) = 1; if bIsDrm then _Trace('소프트캠프 암호화 확인 : Path="%s"', [sDocPath], 2); DeleteFile(PChar(sDest)); end; end; end else _Trace('Fail .. FindPrintingFile() .111. Path="%s"', [sDocPath]); end; if not bIsDrm then begin var sTaskDir: String := sWorkDir_ + 'HEC\'; var sDest: String := sTaskDir + ExtractFileName(sDocPath); if CopyFileAfOpenCheck(sDocPath, sDest) then begin bIsDrm := TTgEncrypt.CheckSign(sDest, SIG_DRM); if bIsDrm then _Trace('BSOne 암호화 확인 : Path="%s"', [sDocPath], 2); DeleteFile(PChar(sDest)); end; end; // if bBlock then begin if not bBlock and PP.bDateBlock then begin var dtNow := Now; if ( (CompareDateTime(PP.dtBlockB, dtNow) = -1) and (CompareDateTime(PP.dtBlockE, dtNow) = 1) ) then begin bBlock := true; sBlockReson := 'DATE'; _Trace('프린터 출력 감지 .. 기간 차단. Printer=%s, Doc=%s', [sPrtName, sDocName], 2); end; end; if not bBlock then begin if ShowTestFun and ( gMgSvc.SharePcPrintBlock or gMgSvc.WSDPortPrintBlock or gMgSvc.TcpIpPrintBlock or gMgSvc.PrintSavingBlock ) then begin // _Trace('출력 제어 체크 ..', 5); // var sHpExe: String := GetRunExePathDir + DIR_CONF + EXE_HP; // var sPrtInfoPath: String := sWorkDir_ + '#pi.dat'; // if FileExists(sHpExe) then // begin // // "공유PC" 프린터 정보를 가져오기 위해서는 이렇게 처리해야함 25_1127 14:19:52 kku // _Trace('출력 제어 체크 .. 사용자 권한으로 프린터 정보 요청 ..', 5); // var O: ISuperObject := SO; // var sParam: String := GetRunExePathDir + DIR_CONF + '#ppi.dat'; // O.I['Cmd'] := HPCDM_PRINT_INFO_LIST; // O.S['P'] := sPrtInfoPath; // SaveJsonObjToFile(O, sParam); // // {$IFDEF DEBUG} // ExecuteAppWaitUntilTerminate(sHpExe, Format('-p "%s"', [sParam]), SW_HIDE); // {$ELSE} // var PInfo: TProcessInformation := ExecuteAppAsUser('explorer.exe', sHpExe, Format('-p "%s"', [sParam]), SW_HIDE); //// Sleep(1000); // if PInfo.dwProcessId <> 0 then // begin // var dwExecuteTick: DWORD := GetTickCount; // while true do // begin // if WaitForSingleObject(PInfo.hProcess, 50) <> WAIT_TIMEOUT then // break; // // if (GetTickCount - dwExecuteTick) > 3000 then // begin // TerminateProcess(PInfo.hProcess, 999); // exit; // end; // end; // end; // {$ENDIF} // DeleteFile(PChar(sParam)); // // if FileExists(sPrtInfoPath) then // _Trace('출력 제어 체크 .. 사용자 권한으로 프린터 정보 요청 .. OK', 5); // end; var PrintersInfo: TPrintersInfo; var pPrtInfo: PPrinterInfo; Guard(PrintersInfo, TPrintersInfo.Create); // if FileExists(sPrtInfoPath) then // begin // PrintersInfo.LoadFromFile(sPrtInfoPath); // DeleteFile(PChar(sPrtInfoPath)); // end else PrintersInfo.RefreshList; pPrtInfo := PrintersInfo.GetPrtInfoByPrtName(sPrtName); if pPrtInfo <> nil then begin if gMgSvc.SharePcPrintBlock then begin if pPrtInfo.PortType = PTShared then begin bBlock := true; sBlockReson := 'SHARE'; _Trace('프린터 출력 감지 .. 공유PC를 통한 출력 차단. Printer=%s, Doc=%s', [sPrtName, sDocName], 2); end; end; if gMgSvc.WSDPortPrintBlock then begin if pPrtInfo.PortType = PTWsd then begin bBlock := true; sBlockReson := 'WSD'; _Trace('프린터 출력 감지 .. WSD Port로 연결된 프린터 차단. Printer=%s, Doc=%s', [sPrtName, sDocName], 2); end; end; if gMgSvc.TcpIpPrintBlock then begin if pPrtInfo.PortType <> PTTcpIp then begin bBlock := true; sBlockReson := 'NoTCPIP'; _Trace('프린터 출력 감지 .. TCP/IP로 설정이 안된 프린터 차단. Printer=%s, Doc=%s', [sPrtName, sDocName], 2); end; end; if gMgSvc.PrintSavingBlock then begin if pPrtInfo.bIsPowerSaveMode then begin bBlock := true; sBlockReson := 'PSAVING'; _Trace('프린터 출력 감지 .. 절전모드 상태 프린터 차단. Printer=%s, Doc=%s', [sPrtName, sDocName], 2); end; end; end; end; end; if bBlock then bBlock := not CheckBlockException; // if PP.PrintKind = pkBlock then // bCfActive := bCfActive and bBlock; // 차단 정책 ON일때 예외일 경우 컨텐츠 필터도 하지 않음 25_0513 17:57:38 kku if ( bCfActive and (PatternEntList.Count > 0) ) or PP.bCollectOutput then begin sExtrTxt := ''; sDocPath := sDocName; _Trace('프린터 출력 감지 .. 원본 파일에서 컨텐츠 추출 .. Path=%s, Printer=%s, Doc=%s', [sDocPath, sPrtName, sDocName], 2); if not FileExists(sDocPath) then begin sDocPath := FindPrintingFile(sDocPath, CompareText(sPName, 'excel.exe') = 0); _Trace('FindPrintingFile() .. Path="%s"', [sDocPath], 2); end; if (sDocPath = '') and (CompareText('notepad.exe', sPName) = 0) then begin // Windows 11 메모장에서 문서 이름이 현재 파일이 아닌 단순 "메모장"으로 뜨게된다. 23_0322 10:57:54 kku sDocPath := GetWindowCaption(GetForegroundWindow); sDocPath := FindPrintingFile(sDocPath); if sDocPath = '' then begin // 윈 11에서 추가 처리 sDocPath := StrsReplace(sDocName, [' - 메모장', ' - Notepad'], ''); sDocPath := FindPrintingFile(sDocPath); end; _Trace('FindPrintingFile() 22.. Path="%s"', [sDocPath], 2); end; _Trace('원본 파일에서 컨텐츠 추출 .. 1, Path = "%s"', [sDocPath], 4); if FileExists(sDocPath) then begin _Trace('원본 파일에서 컨텐츠 추출 .. 2', 4); // todo : DRM 체크. 아래는 사용중인 파일로 오류남. 보완 필요 24_0514 11:06:05 kku // if not bIsDrm then // bIsDrm := TTgEncrypt.CheckSign(sDocPath, SIG_DRM); var sTaskPath: String := sWorkDir_ + 'HEC\'; if ForceDirectories(sTaskPath) then begin sTaskPath := sTaskPath + ExtractFileName(sDocPath); try if bIsDrm then begin {$IFDEF DEBUG} // 출력 시 AIP 원문 수집 테스트 // sDocPath := 'D:\temp3\123_label.docx'; {$ENDIF} try case CUSTOMER_TYPE of CUSTOMER_GEC : begin // AIP if CopyFileAfOpenCheck(sDocPath, sTaskPath) then begin var sTaskDecPath: String := ExtractFilePath(sTaskPath) + 'd_' + ExtractFileName(sTaskPath); var O: ISuperObject := SO; O.S['src'] := sTaskPath; O.S['dst'] := sTaskDecPath; if SendData(gMgSvc.FindAipMdWnd, 2, O.AsString) = 10 then begin sExtrTxt := ExtrTextFromFile(sTaskDecPath, true); _Trace('원본 파일에서 컨텐츠 추출 .. 3 .. 성공 (AIP 복호화), Length = %d', [Length(sExtrTxt)], 4); end; if FileExists(sTaskDecPath) then DeleteFile(PChar(sTaskDecPath)); end; end; else begin if UseSoftcampDecrypt then begin var sTaskDecPath: String := ExtractFilePath(sTaskPath) + 'd_' + ExtractFileName(sTaskPath); if CopyFileAfOpenCheck(sDocPath, sTaskPath) then begin if DSCSForceDecryptFile(sTaskPath, sTaskDecPath) = 1 then begin sExtrTxt := ExtrTextFromFile(sTaskDecPath, true); _Trace('원본 파일에서 컨텐츠 추출 .. 3 .. 성공 (SC 복호화), Length = %d', [Length(sExtrTxt)], 4); end else _Trace('원본 파일에서 컨텐츠 추출 .. 3 .. 실패 (SC 복호화), Length = %d', [Length(sExtrTxt)], 4); end; if FileExists(sTaskDecPath) then DeleteFile(PChar(sTaskDecPath)); end else if fas_ <> nil then begin var sTaskDecPath: String := ExtractFilePath(sTaskPath) + 'd_' + ExtractFileName(sTaskPath); if CopyFileAfOpenCheck(sDocPath, sTaskPath) then begin if fas_.DoExtract(sTaskPath, sTaskDecPath) then begin sExtrTxt := ExtrTextFromFile(sTaskDecPath, true); _Trace('원본 파일에서 컨텐츠 추출 .. 3 .. 성공 (Fasoo 복호화), Length = %d', [Length(sExtrTxt)], 4); end else _Trace('원본 파일에서 컨텐츠 추출 .. 3 .. 실패 (Fasoo 복호화), Length = %d', [Length(sExtrTxt)], 4); end; if FileExists(sTaskDecPath) then DeleteFile(PChar(sTaskDecPath)); end else begin var dec: TTgDrmDec; Guard(dec, TTgDrmDec.Create(sTaskPath)); if dec.DecryptToFile(GetMK, sDocPath) then begin sExtrTxt := ExtrTextFromFile(sTaskPath, true); _Trace('원본 파일에서 컨텐츠 추출 .. 3 .. 성공 (BS 복호화), Length = %d', [Length(sExtrTxt)], 4); end; end; end; end; except on E: Exception do ETgException.TraceException(Self, E, Format('프린터 출력 감지 .. 원본파일 복호화 실패.. Path=%s', [sDocPath])); end; end else begin // 오피스 파일은 열려 있으면 제대로 처리 안될 가능성 있음 24_0718 09:44:06 kku if CopyFileAfOpenCheck(sDocPath, sTaskPath) then begin sExtrTxt := ExtrTextFromFile(sTaskPath, true); _Trace('원본 파일에서 컨텐츠 추출 .. 3 .. 성공, Length = %d', [Length(sExtrTxt)], 4); end; end; finally if FileExists(sTaskPath) then DeleteFile(PChar(sTaskPath)); end; end; if sExtrTxt <> '' then _Trace('프린터 출력 감지 .. 원본 파일에서 컨텐츠 추출 성공 .. ' + 'Path=%s, Printer=%s, Doc=%s', [sDocPath, sPrtName, sDocName], 2); end; end; if not bBlock and bCfActive and (PatternEntList.Count > 0) then begin // 출력 대상 파일에서 컨텐츠 필터 시작 ===================================== // 출력 정보 전송 23_0223 13:38:21 kku // if FileExists(sDocPath) then // sChk := ExtractFileName(sDocPath) // else // sChk := sDocName; // SendReqPrint(sExtrTxt, sChk, sPName); // DirectSendPtrFile(sDocPath); // 출력 대상 파일에서 컨텐츠 필터 끝 ======================================= // 개인정보 확인 if sExtrTxt <> '' then begin if ProcessContentFilter_AfterExit(sExtrTxt, false) then exit; end else _Trace('프린터 출력 감지 .. 원본 파일에서 컨텐츠 추출 실패 .. Path=%s, Printer=%s, Doc=%s', [sDocPath, sPrtName, sDocName], 2); end; end; SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0); // todo : 출력물 수집 // sSpoolPath := ''; // if (sBlockReson = '') and (PP.bCollectOutput and (PP.PrintWater = pwNone)) then // 프린터 워터마크 처리 전 전송하도록 기능 수정 23_0913 13:05:37 kku if PO.IsPrtCollectThum then // 프린터 워터마크 처리 전 전송하도록 기능 수정 23_0913 13:05:37 kku begin sEmfDir := GetEmfCopyDir; if DirectoryExists(sEmfDir) then begin SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRINTWATER_PROGRESS, 0, 0); SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0); // emf를 png로 변환 후 텍스트 추출 if PO.Print.bCollectOutput then begin var bDoExtr: Boolean := true; var ExtList: TStringList := gMgSvc.PrefModel.PrtOcrTxtExtList; if ExtList.Count > 0 then begin if FileExists(sDocPath) then begin var sExt: String := GetFileExt(sDocPath).ToUpper; if ExtList.IndexOf(sExt) = - 1 then bDoExtr := false; end else if ExtList.IndexOf('*NF') = -1 then bDoExtr := false; end; if bDoExtr then begin _Trace('출력 이미지에서 텍스트 추출(E) ..', 3); sExtrOcrTxt := ExtrTextFromPrintEmfFiles(sEmfDir); _Trace('출력 이미지에서 텍스트 추출(E) .. OK', 3); end; SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0); end; // emf를 png로 변환 후 수집 sThumbIds := gMgSvc.SendPrintEmfFiles(sEmfDir, sWorkDir_ + 'PrtThum\', PO.PrtCollThumLimit, crtDelete); if bCfActive then begin if ProcessContentFilter_AfterExit(sExtrOcrTxt, true) then exit; end; end else if bIsSplAnal then begin var sConv: String := GetRunExePathDir + DIR_CONF + EXE_SPL; if FileExists(sConv) and not IsHD then // HEC, GEC에서 OCR 처리 안함 24_0723 10:31:59 kku begin if (sSpoolPath = '') or not FileExists(sSpoolPath) then begin sSpoolPath := GetSpoolCopyPath; _Trace('GetSpoolCopyPath(2) .. Path=%s', [sSpoolPath]); end; if FileExists(sSpoolPath) then begin if bIsSplAnal and IsPJLAndLanguagePLW(sSpoolPath) then bIsSplAnal := false; if not IsPrintWaterHook and (PP.PrintWater <> pwNone) then begin // 분석이 오래 걸려서 미리 취소 시켜준다. 25_0417 09:22:20 kku CancelJob; end; var sFName: String := GetValidFileName(ExtractFileName(sDocName), '#'); var sExportPath: String := ExtractFilePath(sSpoolPath) + sFName + '.png'; if PrtInfo.sPtrName = '' then PrtInfo.sPtrName := sPrtName; if PrtInfo.sDocName = '' then PrtInfo.sDocName := sDocName; // 브라우저에서 실물 프린터 출력 시 선명도 많이 떨어진다.. // 아래처럼 옵션 추가 24_0731 15:44:42 kku if bIsSplAnal then SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRINTWATER_PROGRESS, 4, 0); if bIsSplAnal and ExtractImagesFromSpool(sConv, '-$ 65XSD4234455S4PLET58 -unicode -imgxres 150 -imgyres 150 -imgbitcount 8 "%s" "%s"', sSpoolPath, 'col_', sExportPath, 300000, 0{PO.PrtCollThumLimit}, PP.PrintWater = pwNone) then begin SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRINTWATER_PROGRESS, 0, 0); SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0); if not FileExists(sSpoolPath) then sSpoolPath := ''; if PO.Print.bCollectOutput then begin var bDoExtr: Boolean := true; var ExtList: TStringList := gMgSvc.PrefModel.PrtOcrTxtExtList; if ExtList.Count > 0 then begin if FileExists(sDocPath) then begin var sExt: String := GetFileExt(sDocPath).ToUpper; if ExtList.IndexOf(sExt) = - 1 then bDoExtr := false; end else if ExtList.IndexOf('*NF') = -1 then bDoExtr := false; end; if bDoExtr then begin _Trace('출력 이미지에서 텍스트 추출 ..', 3); sExtrOcrTxt := ExtrTextFromPrintImgFiles(sExportPath); SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0); _Trace('출력 이미지에서 텍스트 추출 .. OK', 3); end; end; sThumbIds := gMgSvc.SendPrintImgFiles(sExportPath, sWorkDir_ + 'PrtThum\', PO.PrtCollThumLimit, crtDelete); if bCfActive then begin if ProcessContentFilter_AfterExit(sExtrOcrTxt, true) then exit; end; end else begin SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRINTWATER_PROGRESS, 0, 0); AddIgrPrtSpool(sPrtName); bIsSplAnal := false; end; // DeleteDir(ExtractFilePath(sExportPath)); // 이거 언제 지워야 하나.. end; end; end; end; if not bBlock and (PP.PrintWater <> pwNone) then begin var sConv: String := GetRunExePathDir + DIR_CONF + EXE_SPL; // {$IFDEF DEBUG} // bForceSpoolWater := true; // {$ENDIF} if (bForceSpoolWater or not bIsHook) and FileExists(sConv) then begin if (sSpoolPath = '') or not FileExists(sSpoolPath) then begin sSpoolPath := GetSpoolCopyPath('WTask'); _Trace('GetSpoolCopyPath(3) .. Path=%s', [sSpoolPath]); end; if FileExists(sSpoolPath) then begin // 워드는 부수, 한부씩인쇄 값을 가져올 수 없어서 아래처럼 처리 if CompareText('winword.exe', sPName) = 0 then PrtInfo.DevMode.dmCollate := 1; if bIsSplAnal and IsPJL(sSpoolPath) then begin _Trace('PJL 포맷 확인됨', 1); if not IsPJLAndLanguagePLW(sSpoolPath) then begin var bCollate: Boolean; var nCopies: Integer := GetQtyFromPJL(sSpoolPath, bCollate); if nCopies > 0 then begin PrtInfo.dwCopyCount := nCopies; PrtInfo.DevMode.dmCopies := nCopies; if bCollate then PrtInfo.DevMode.dmCollate := 1; _Trace('PJL 포맷 부수 정보 : %d', [nCopies], 1); end; end else begin _Trace('PJL 포맷, PLW 스플 확인됨. 스플 분석 중지.', 1); bIsSplAnal := false; // PJL 포맷에서 LANGUAGE=PLW 사용하면 spl2pdf가 분석하지 못한다 25_0723 15:26:37 kku end; end; if bIsSplAnal then begin var sFName: String := GetValidFileName(ExtractFileName(sDocName), '#'); var sExportPath: String := ExtractFilePath(sSpoolPath) + sFName + '.png'; // {$IFNDEF DEBUG} if IsPrtSpl2Pdf then begin case CUSTOMER_TYPE of CUSTOMER_SHCI, CUSTOMER_SHSC : begin if (CompareText('InsideBank.exe', sPName) <> 0) and (CompareText('excel.exe', sPName) <> 0) and (CompareText('POWERPNT.EXE', sPName) <> 0) and (CompareText('Hwp.exe', sPName) <> 0) then sExportPath := CutFileExt(sExportPath) + '.pdf'; end; // CUSTOMER_SHCI : // begin // if (CompareText('InsideBank.exe', sPName) <> 0) and // (CompareText('Hwp.exe', sPName) <> 0) then // sExportPath := CutFileExt(sExportPath) + '.pdf'; // end; end; end; // {$ENDIF} if PrtInfo.sPtrName = '' then begin PrtInfo.sPtrName := sPrtName; // TTgTrace.T('[W] Force PtrName="%s"', [PrtInfo.sPtrName]); end; if PrtInfo.sDocName = '' then PrtInfo.sDocName := sDocName; var sParam: String := Format('-$ 65XSD4234455S4PLET58 -unicode -imgbitcount 24 -imgxres %d -imgyres %d', [PPO.PrtDPI, PPO.PrtDPI]) + ' "%s" "%s"'; if IsPrintWaterHook then begin bIsSplAnal := IsSpoolAnal(sPrtName); if bIsSplAnal then begin SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRINTWATER_PROGRESS, 1, NativeUInt(PChar(IntToStr(PrtInfo.dwTotalPage) + '|' + ExtractFilePath(sSpoolPath) + '|' + sFName))); // 후킹방식일 경우 분석 실패 체크를 위해 여기서 분석을 해준다 25_0728 13:10:48 kku if not ExtractImagesFromSpool(sConv, sParam, sSpoolPath, 'prt0_', sExportPath, 300000, 0) then begin AddIgrPrtSpool(sPrtName); bIsSplAnal := false; end; end; if not bIsSplAnal then SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRINTWATER_PROGRESS, 0, 0); end else begin SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRINTWATER_PROGRESS, 1, NativeUInt(PChar(IntToStr(PrtInfo.dwTotalPage) + '|' + ExtractFilePath(sSpoolPath) + '|' + sFName))); end; if bIsSplAnal then begin // 잡 삭제 직전에 업데이트 if (aJob <> nil) and not aJob.WorkEnd then CancelJob; bWaterMark := true; TThdExecuteEndNoti.Create(gMgSvc.RcvHwnd, GetPrintLog(LOGCODE_EVENT_PRINTER), true, PrtInfo, sPort, sConv, sParam, sSpoolPath, sExportPath, sPrtDocId).StartThread; sSpoolPath := ''; // 삭제 안되게 빈값 처리 24_1212 16:39:40 kku exit; end; end; end else _Trace('프린터 출력 감지 .. Spool 수집 실패... Printer=%s, Doc=%s', [sPrtName, sDocName], 2); end else begin // 워터마크 유효성 체크 if CheckValidPrintWater then // KOCES에서는 워터마크 안되도 차단 안되게함 23_0413 13:35:37 kku begin if gMgSvc.RecentPrintDocName <> sDocName then begin sBlockReson := 'WT'; _Trace('Fail .. PrintWatermark .. Recent="%s", Doc="%s"', [gMgSvc.RecentPrintDocName, sDocName]); bBlock := true; end; end; end; end; SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 3, 0); sData := sPrtName + '|' + sDocName; if bBlock and not bApprovalPost then begin if CollectPrintInfo(sDocPath, sExtrTxt, sExtrOcrTxt, '', '') then begin SendPrintLog(LOGCODE_PREVENT_PRINTER); _Trace('프린터 출력 감지 .. 차단, 출력 승인 요청. Printer=%s, Doc=%s', [sPrtName, sDocName], 2); exit; end; if bApprovalPost then begin // 사후 결재 처리를 통해 false로 변경될 수 있다. 25_1127 09:28:25 kku goto LB_DoPrint; end; CancelJob; sData := sData + '|PV'; if sBlockReson <> '' then sData := sData + '|' + sBlockReson; // else if PP.PrintWater <> pwNone then // sData := sData + '|WT'; if gMgSvc.IsNewApi then SendPrintLog(LOGCODE_PREVENT_PRINTER) else gMgSvc.SendEventLog(URI_USER_ACTION, LOGCODE_PREVENT_PRINTER, Format('Printer : %s, Document : %s', [sPrtName, sDocName]) + BooleanToStr(sBlockReson = '', '', ', ' + sBlockReson)); end else begin LB_DoPrint : if aJob <> nil then begin // 스풀중에는 resume 명령이 먹지 않아서 추가 25_0623 10:29:50 kku while (aJob <> nil) and aJob.IsSpooling2 do begin if i = 4000 then // 200초간 기다려준다 23_0525 08:03:11 kku break; Inc(i); Sleep(50); end; // SetPrtJobFromHelperApp(aJob, JOB_CONTROL_RESUME); aJob.ResumePrtJob(true); end; if not bIsHook and gMgSvc.IsPrtWaterExcept then // 후킹의 경우 HPCMD_CHECK_PRINTWATER_EXCEPT_EX를 통해 처리, 초기화 함 25_1105 19:39:27 kku gMgSvc.IsPrtWaterExcept := false; if gMgSvc.IsNewApi then SendPrintLog(LOGCODE_EVENT_PRINTER) else gMgSvc.SendEventLog(URI_USER_ACTION, LOGCODE_EVENT_PRINTER, Format('Printer : %s, Document : %s', [sPrtName, sDocName]), false); end; if IsDivPopup then begin if bBlock then begin if PP.bPopup then gMgSvc.PopupMessage(TYPE_MSG_PREVENT_PRINTER, sData); end else begin if PO.PrtAllowPopup then begin if (PP.PrintWater <> pwNone) and PPO.PrtWaterPop and (gMgSvc.RecentPrintDocName = sDocName) then gMgSvc.PopupMessage(TYPE_MSG_EVENT_PRINTWATER, sData) else gMgSvc.PopupMessage(TYPE_MSG_PREVENT_PRINTER, sData); end; end; end else begin if PP.bPopup then begin if bBlock or (PP.PrintKind = pkLog) then begin gMgSvc.PopupMessage(TYPE_MSG_PREVENT_PRINTER, sData); end else if (PP.PrintWater <> pwNone) and PPO.PrtWaterPop then begin if (gMgSvc.RecentPrintDocName = sDocName) then gMgSvc.PopupMessage(TYPE_MSG_EVENT_PRINTWATER, sData); end; end; end; if aJob <> nil then aJob.WorkEnd := true; finally Finalize(PrtInfo); if FileExists(sSpoolPath) then DeleteFile(PChar(sSpoolPath)); if DirectoryExists(sEmfDir) then DeleteDir(sEmfDir); end; end; finally bIsWorking_ := false; CurJob_ := nil; if pPhJob <> nil then Dispose(pPhJob); if aJob <> nil then aJob.ResumePrtJob(true); end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. ProcessPrintJob()'); end; end; procedure TThdPrintWork.Execute; var Job: TPrtJobInfo; bInitFs: Boolean; begin // 외부 DRM 사용준비 bInitFs := true; fas_ := nil; case CUSTOMER_TYPE of CUSTOMER_WELFNI : SetDSD_CODE(DSD_CODE_WFNI); CUSTOMER_WELFND : SetDSD_CODE(DSD_CODE_WFND); else bInitFs := false; end; if bInitFs then begin var sFsDir: String := GetRunExePathDir + 'fsdinit'; if not DirectoryExists(sFsDir) then sFsDir := GetRunExePathDir + DIR_CONF + 'fsdinit'; if DirectoryExists(sFsDir) then begin CoInitializeEx(nil, COINIT_APARTMENTTHREADED); fas_ := TTgFasoo.Create(sFsDir); end; end; while not Terminated and not GetWorkStop do begin try Lock; try if (JobList_ <> nil) and (JobList_.Count > 0) then begin Job := JobList_[0]; JobList_.Delete(0); end else Job := nil; finally Unlock; end; if Job <> nil then ProcessPrintJob(Job) else Sleep(50); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. Execute()'); end; end; end; end.