{*******************************************************} { } { ThdExecuteEndNoti } { } { Copyright (C) 2023 kku } { } {*******************************************************} unit ThdExecuteEndNoti; interface uses {$IFDEF _HE_} ManagerPrint, {$ENDIF} System.SysUtils, System.Classes, Tocsg.Thread, Winapi.Messages, Winapi.Windows, Tocsg.Printer, Vcl.Graphics, superobject; {$I Define.inc} const WM_NOTIEXECUTE_END = WM_USER + 2654; type TThdExecuteEndNoti = class(TTgThread) private hRcvWnd_: HWND; sExePath_, sParam_, sSrcPath_, sDestPath_: String; // pAssoc_: Pointer; bUseWM_: Boolean; OLog_: ISuperObject; sPrtDocId_, sPortInfo_: String; PrtInfo_: TPrtJobDevInfo; {$IFDEF _HE_} PrtEnt_: PPrtEnt; {$ENDIF} protected procedure Execute; override; public Constructor Create(hRcvWnd: HWND; OLog: ISuperObject; bUseWM: Boolean; sExePath, sParam, sSrcPath, sDestPath: String; pAssoc: Pointer); overload; Constructor Create(hRcvWnd: HWND; OLog: ISuperObject; bUseWM: Boolean; aPrtInfo: TPrtJobDevInfo; sPortInfo, sExePath, sParam, sSrcPath, sDestPath, sPrtDocId: String); overload; {$IFDEF _HE_} Constructor Create(hRcvWnd: HWND; OLog: ISuperObject; aPrtEnt: PPrtEnt; sExePath, sParam, sSrcPath, sDestPath: String; pAssoc: Pointer); overload; {$ENDIF} // property Assoc: Pointer read pAssoc_; property DestPath: String read sDestPath_; property PrtInfo: TPrtJobDevInfo read PrtInfo_; {$IFDEF _HE_} property PrtEnt: PPrtEnt read PrtEnt_; {$ENDIF} property PortInfo: String read sPortInfo_; property OLog: IsuperObject read OLog_; property UseWM: Boolean read bUseWM_; property PrtDocId: String read sPrtDocId_; property WorkState: TTgThreadState read GetWorkState; end; function ExtractImagesFromSpool(sExePath, sParam, sSrcPath, sHeadStr: String; var sDestPath: String; nWaitMSec: Integer; nExtrMax: Integer = 0 ; bDelSrcPath: Boolean = true): Boolean; function ExtractJpgFromTiff(sTifPath: String): Integer; function ExtractPngFromTiff(sTifPath: String; aTiff: TWICImage = nil): Integer; function ExtractPngFromPng(sPngPath: String): Integer; implementation uses {$IFDEF _HE_} ManagerService, {$ENDIF} Tocsg.Process, Tocsg.Path, Tocsg.Trace, Tocsg.Exception, GlobalDefine, Tocsg.Files, PngImageList, Tocsg.Safe, Vcl.Imaging.pngimage, Vcl.Imaging.jpeg, Winapi.ActiveX, ManagerModel, Condition, CttSchDefine, ManagerCampaign; Constructor TThdExecuteEndNoti.Create(hRcvWnd: HWND; OLog: ISuperObject; bUseWM: Boolean; sExePath, sParam, sSrcPath, sDestPath: String; pAssoc: Pointer); begin Inherited Create; ZeroMemory(@PrtInfo_, SizeOf(PrtInfo_)); sPortInfo_ := ''; sPrtDocId_ := ''; OLog_ := OLog; bUseWM_ := bUseWM; hRcvWnd_ := hRcvWnd; sExePath_ := sExePath; sParam_ := sParam; sSrcPath_ := sSrcPath; sDestPath_ := sDestPath; // pAssoc_ := pAssoc; {$IFDEF _HE_} PrtEnt_ := nil; {$ENDIF} FreeOnTerminate := true; end; Constructor TThdExecuteEndNoti.Create(hRcvWnd: HWND; OLog: ISuperObject; bUseWM: Boolean; aPrtInfo: TPrtJobDevInfo; sPortInfo, sExePath, sParam, sSrcPath, sDestPath, sPrtDocId: String); begin Create(hRcvWnd, OLog, bUseWM, sExePath, sParam, sSrcPath, sDestPath, nil); PrtInfo_ := aPrtInfo; sPortInfo_ := sPortInfo; sPrtDocId_ := sPrtDocId; end; {$IFDEF _HE_} Constructor TThdExecuteEndNoti.Create(hRcvWnd: HWND; OLog: ISuperObject; aPrtEnt: PPrtEnt; sExePath, sParam, sSrcPath, sDestPath: String; pAssoc: Pointer); begin Create(hRcvWnd, OLog, aPrtEnt.WInfo.bUseWM, sExePath, sParam, sSrcPath, sDestPath, pAssoc); PrtEnt_ := aPrtEnt; sPortInfo_ := PrtEnt_.WInfo.sPdfPath; end; {$ENDIF} procedure ExtractSpoolLib(var nWaitMSec: Integer); var sTempDir, sConfDir: String; begin nWaitMSec := -1; sConfDir := ''; sTempDir := ''; try {$IFDEF _HE_} sConfDir := GetRunExePathDir + DIR_CONF + 'spl2pdf_lib'; if not DirectoryExists(sConfDir) and (gMgSvc <> nil) then begin sTempDir := GetWindowsDir; if sTempDir <> '' then begin sTempDir := sTempDir[1] + Format(':\Users\%s\AppData\Local\Temp\VeryPDF\spl2pdf_lib', [ExtractFileName(gMgSvc.UserName)]); if not FileExists(sTempDir + 'pcl.dll') then sTempDir := GetWindowsDir + 'Temp\VeryPDF\spl2pdf_lib'; if DirectoryExists(sTempDir) and not DirectoryExists(sConfDir) then begin TTgTrace.T('프린터 출력 감지 .. Spool 에서 문서 추출 .. 라이브러리 추출 ..', 2); CopyDirSub(sTempDir, sConfDir); end else begin nWaitMSec := 5000; TTgTrace.T('프린터 출력 감지 .. Spool 에서 문서 추출 .. 라이브러리 추출 .. 대기', 2); end; end; end; {$ELSE} sConfDir := GetRunExePathDir + 'spl2pdf_lib'; if not DirectoryExists(sConfDir) then begin sTempDir := GetUserDir; if sTempDir <> '' then begin sTempDir := IncludeTrailingPathDelimiter(sTempDir) + 'AppData\Local\Temp\VeryPDF\spl2pdf_lib'; if DirectoryExists(sTempDir) and not DirectoryExists(sConfDir) then CopyDirSub(sTempDir, sConfDir) else nWaitMSec := 5000; end; end; {$ENDIF} except on E: Exception do ETgException.TraceException(E, 'Fail .. ExtractSpoolLib()'); end; end; function ExtractJpgFromTiff(sTifPath: String): Integer; var tif: TWICImage; jpg: TJpegImage; sDestJpgPath: String; ms: TMemoryStream; begin Result := 0; try sDestJpgPath := CutFileExt(sTifPath); Guard(ms, TMemoryStream.Create); ms.LoadFromFile(sTifPath); Guard(jpg, TJpegImage.Create); Guard(tif, TWICImage.Create); Repeat try tif.LoadFromStream(ms); jpg.Assign(tif); jpg.SaveToFile(Format('%s-%.3d.jpg', [sDestJpgPath, Result + 1])); Inc(Result); tif.FrameIndex := Result; except on E: Exception do begin ETgException.TraceException(E, 'Fail .. ExtractJpgFromTiff() ... extract'); break; end; end; Until Result >= tif.FrameCount; except on E: Exception do ETgException.TraceException(E, 'Fail .. ExtractJpgFromTiff()'); end; end; function ExtractPngFromTiff(sTifPath: String; aTiff: TWICImage = nil): Integer; var tif: TWICImage; png: TPngImage; sDestPngPath: String; bFreeTfi: Boolean; ms: TMemoryStream; begin Result := 0; try sDestPngPath := CutFileExt(sTifPath); bFreeTfi := false; Guard(png, TPngImage.Create); if aTiff = nil then begin tif := TWICImage.Create; bFreeTfi := true; end else begin tif := aTiff; tif.FrameIndex := 0; end; Guard(ms, TMemoryStream.Create); try ms.LoadFromFile(sTifPath); Repeat try tif.LoadFromStream(ms); png.Assign(tif); png.SaveToFile(Format('%s-%.3d.png', [sDestPngPath, Result + 1])); Inc(Result); tif.FrameIndex := Result; except on E: Exception do begin ETgException.TraceException(E, 'Fail .. ExtractPngFromTiff() ... extract'); break; end; end; Until Result >= tif.FrameCount; finally if bFreeTfi then FreeAndNil(tif); end; except on E: Exception do ETgException.TraceException(E, 'Fail .. ExtractPngFromTiff()'); end; end; // spl2pdf.exe 버그로 png 파일이 하나로 합쳐져서 내보내기가 됨. // 나눠주는 작업을 하기 위해 추가 24_0207 14:53:04 kku function ExtractPngFromPng(sPngPath: String): Integer; const SIG_PNG: array [0..3] of Byte = ($89, $50, $4E, $47); var ms: TMemoryStream; fs: TFileStream; png: TPngImage; arrChk: array [0..3] of Byte; llPos, llPngSt, llRead: LONGLONG; sIgExtPath: String; begin Result := 0; try if not FileExists(sPngPath) then exit; Guard(ms, TMemoryStream.Create); ms.LoadFromFile(sPngPath); sIgExtPath := CutFileExt(sPngPath); llPos := 0; llPngSt := -1; while ms.Position < ms.Size do begin ms.Position := llPos; if ms.Read(arrChk, 4) <> 4 then break; if CompareMem(@SIG_PNG, @arrChk, 4) then begin if llPngSt <> -1 then begin // 파일 끝, 저장 Inc(Result); Guard(fs, TFileStream.Create(sIgExtPath + Format('_%.4d.png', [Result]), fmCreate)); llPos := ms.Position - 4; ms.Position := llPngSt; llRead := llPos - llPngSt; if (ms.Position + llRead) < ms.Size then fs.CopyFrom(ms, llRead) else fs.CopyFrom(ms, ms.Size - ms.Position); llPngSt := llPos; Inc(llPos, 4); end else llPngSt := ms.Position - 4; end; Inc(llPos); end; // if (llPngSt <> -1) and (Result > 0) then // 이렇게 하면 안됨.. spl2pdf에서 파일을 여러개 나눴을 경우 두번째가 _0002 이기 때문에 첫번째 파일을 _0001로 바꿔줘야함 24_0208 08:14:46 kku if llPngSt <> -1 then begin Inc(Result); Guard(fs, TFileStream.Create(sIgExtPath + Format('_%.4d.png', [Result]), fmCreate)); ms.Position := llPngSt; fs.CopyFrom(ms, ms.Size - ms.Position); end; except on E: Exception do ETgException.TraceException(E, 'Fail .. ExtractPngFromPng()'); end; end; function ExtractImagesFromSpool(sExePath, sParam, sSrcPath, sHeadStr: String; var sDestPath: String; nWaitMSec: Integer; nExtrMax: Integer = 0 ; bDelSrcPath: Boolean = true): Boolean; Label LB_TryExtrDoc; var nTry, nTO: Integer; nDefCnt, nProcCnt: Integer; sExt, sDestDir: String; function ExtractImgWaitUntilTerminate(sPath, sParam: String; dwVisible: DWORD; nTimeOutMilSec: Integer = -1): Boolean; var PI: TProcessInformation; StartupInfo: TStartupInfo; sDir, sName: String; dwWaitResult, dwExecuteTick: DWORD; begin ZeroMemory(@PI, SizeOf(PI)); ZeroMemory(@StartupInfo, SizeOf(StartupInfo)); StartupInfo.cb := Sizeof(StartupInfo); StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USEPOSITION; StartupInfo.wShowWindow := dwVisible; sDir := ExtractFilePath(sPath); sName := ExtractFileName(sPath); Result := CreateProcess(nil, PChar(Format('%s %s', [sPath, sParam])), { pointer to command line string } nil, { pointer to process security attributes } nil, { pointer to thread security attributes } false, { handle inheritance flag } CREATE_NEW_CONSOLE or { creation flags } NORMAL_PRIORITY_CLASS, nil, { pointer to new environment block } nil, { pointer to current directory name } StartupInfo, { pointer to STARTUPINFO } PI); { pointer to PROCESS_INF } if Result then begin dwExecuteTick := GetTickCount; while true do begin dwWaitResult := WaitForSingleObject(PI.hProcess, 50); if dwWaitResult <> WAIT_TIMEOUT then break; if nExtrMax > 0 then begin nProcCnt := CountFileExt(sDestDir, [sExt]) - nDefCnt; if nProcCnt >= nExtrMax then begin TerminateProcess(PI.hProcess, 999); exit; end; end; // 메세지 없이 계속 돌아가는 상황을 위해서 빠져나오는 타임아웃을 추가함 (tcptunnel.exe 관련을 위해 추가) if (nTimeOutMilSec > 0) and ((GetTickCount - dwExecuteTick) > nTimeOutMilSec) then begin TerminateProcess(PI.hProcess, 999); exit; end; end; // GetExitCodeProcess(PI.hProcess, dwExitCode); end; end; begin Result := false; try TTgTrace.T('프린터 출력 감지 .. Spool 에서 문서 추출 ..', 2); ExtractSpoolLib(nWaitMSec); nTry := 0; sExt := GetFileExt(sDestPath); sDestDir := ExtractFilePath(sDestPath); sDestPath := sDestDir + sHeadStr + FormatDateTime('yyyymmddhhnnss', Now) + '.' + sExt; if nExtrMax > 0 then nDefCnt := CountFileExt(sDestDir, [sExt]); LB_TryExtrDoc : nProcCnt := 0; // {$IFDEF _SPL2PDF_} // sDestPath := CutFileExt(sDestPath) + '.pdf'; // ExtractImgWaitUntilTerminate(sExePath, Format('-$ 65XSD4234455S4PLET58 -unicode -imgxres 600 -imgyres 600 -imgbitcount 24 "%s" "%s"', [sSrcPath, sDestPath]), SW_HIDE, nWaitMSec); // // Exit(true); // {$ELSE} ExtractImgWaitUntilTerminate(sExePath, Format(sParam, [sSrcPath, sDestPath]), SW_HIDE, nWaitMSec); // {$ENDIF} nTO := 0; while not FileExists(sDestPath) do begin if nTO >= 20 then break; Inc(nTO); Sleep(100); end; if FileExists(sDestPath) or FileExists(CutFileExt(sDestPath) + '_0001.' + sExt) then begin if ExtractPngFromPng(sDestPath) > 0 then DeleteFile(PChar(sDestPath)); if not FileExists(sDestPath) then sDestPath := CutFileExt(sDestPath) + '_0001.' + sExt; Result := CountFileExt(ExtractFilePath(sDestPath), [sExt]) > 0; // Result := ExtractPngFromTiff(sDestPath, aTiff) > 0; end else begin if nTry < 2 then begin Inc(nTry); TTgTrace.T('프린터 출력 감지 .. Spool 에서 문서 추출 .. 실패 .. 다시 시도 %d', [nTry], 2); ExtractSpoolLib(nWaitMSec); Sleep(500); goto LB_TryExtrDoc; end; end; // CJ:: if bDelSrcPath then DeleteFile(PChar(sSrcPath)); except on E: Exception do ETgException.TraceException(E, 'Fail .. ExtractImagesFromSpool()'); end; end; procedure TThdExecuteEndNoti.Execute; Label LB_TryExtrDoc; var PO: TPrefModel; begin try // CoInitialize(nil); // try SetWorkState(tsWorking); PO := gMgSvc.ModePolicy; if sDestPath_ = '' then begin // EMF 방식 if PO.IsPrtCollectThum then begin var sEmfDir: String := gMgSvc.MgPrt.DataDir + PrtEnt_.sId + '\'; if DirectoryExists(sEmfDir) then begin SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0); if (OLog_ <> nil) and (OLog_.S['COMPONENT_THUMBNAIL_ID'] = '') then OLog_.S['COMPONENT_THUMBNAIL_ID'] := gMgSvc.SendPrintEmfFiles(sEmfDir, 'C:\ProgramData\HE\PrtThum\', PO.PrtCollThumLimit, crtDelete); var bDoExtr: Boolean := true; if PrtEnt_ <> nil then begin var ExtList: TStringList := gMgSvc.PrefModel.PrtOcrTxtExtList; if ExtList.Count > 0 then begin if FileExists(PrtEnt_.sFPath) then begin var sExt: String := GetFileExt(PrtEnt_.sFPath).ToUpper; if ExtList.IndexOf(sExt) = - 1 then bDoExtr := false; end else if ExtList.IndexOf('*NF') = -1 then bDoExtr := false; end; end; if bDoExtr then begin if (OLog_ <> nil) and (OLog_.S['OCR_BODY'] = '') then OLog_.S['OCR_BODY'] := ExtrTextFromPrintEmfFiles(sEmfDir); end; SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0); end; SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0); // 재출력일 경우 한단계 더 넘겨야 함.. end; end else begin if not FileExists(sDestPath_) and not FileExists(CutFileExt(sDestPath_) + '_0001.' + GetFileExt(sDestPath_)) then // ExtractImagesFromSpool()를 미리한 경우가 있다. 25_0728 13:13:04 kku begin ExtractImagesFromSpool(sExePath_, sParam_, sSrcPath_, 'prt_', sDestPath_, 300000, 0); end; if FileExists(sDestPath_) and (PrtEnt_ <> nil) and (PrtEnt_.sThumbIds = '') and (OLog_ <> nil) then begin // 썸네일 수집 25_0421 09:52:53 kku if PO.IsPrtCollectThum then begin SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0); if (OLog_ <> nil) and (OLog_.S['COMPONENT_THUMBNAIL_ID'] = '') then begin OLog_.S['COMPONENT_THUMBNAIL_ID'] := gMgSvc.SendPrintImgFiles(sDestPath_, 'C:\ProgramData\HE\PrtThum\', PO.PrtCollThumLimit); end; if PO.Print.bCollectOutput and (OLog_.S['OCR_BODY'] = '') then begin var bDoExtr: Boolean := true; if PrtEnt_ <> nil then begin var ExtList: TStringList := gMgSvc.PrefModel.PrtOcrTxtExtList; if ExtList.Count > 0 then begin if FileExists(PrtEnt_.sFPath) then begin var sExt: String := GetFileExt(PrtEnt_.sFPath).ToUpper; if ExtList.IndexOf(sExt) = - 1 then bDoExtr := false; end else if ExtList.IndexOf('*NF') = -1 then bDoExtr := false; end; end; if bDoExtr then begin if (OLog_ <> nil) and (OLog_.S['OCR_BODY'] = '') then OLog_.S['OCR_BODY'] := ExtrTextFromPrintImgFiles(sDestPath_); end; SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0); end; SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0); // 재출력일 경우 한단계 더 넘겨야 함.. end; end; end; SendMessage(hRcvWnd_, WM_NOTIEXECUTE_END, 0, NativeUInt(Self)); SetWorkState(tsCompleted); // finally // CoUninitialize; // end; except on E: Exception do begin ETgException.TraceException(Self, E, 'Fail .. Execute()'); SetWorkState(tsFail); end; end; end; end.