{*******************************************************} { } { Tocsg.KvFilter } { } { Copyright (C) 2022 kku } { } {*******************************************************} unit Tocsg.KvFilter; interface {$IFNDEF _HE_} {$DEFINE _IMG_PARSER_} {$ENDIF} uses {$IFDEF _IMG_PARSER_} imageen, iexMetaHelpers, {$ENDIF} Tocsg.Obj, System.SysUtils, System.Classes, WinAPI.Windows, Tocsg.KvFilter.Types, Tocsg.KvFilter.adinfo, Tocsg.Thread; const DLL_KVFILTER = 'kvfilter.dll'; DLL_KVHWP = 'kvhvp.dll'; EXE_KV_TASK = 'kvoop.exe'; EXE_KV_HWP = 'KvCttSchw.exe'; // * KeyView Filter interface revision. To be assigned to KV_GetFilterInterfaceEx(). KVFLTINTERFACE_REVISION = 6; type // KvSetTimeout() 때문에 필요없어짐 22_0720 10:20:35 kku // TThdKvFilterTimeout = class(TTgThread) // protected // nTOMilSec_: Integer; // dwResetTick_: DWORD; // procedure Execute; override; // public // Constructor Create(nTOSec: Integer); // // procedure ResetTimeout; // procedure EndTimeout; // end; TKvFilter = class(TTgObject) private {$IFDEF _IMG_PARSER_} bDoFree_: Boolean; ImgEn_: TImageEn; {$ENDIF} nLastError_: Integer; hKvFilter_: THandle; KvCtrl_: TKvFltInterfaceEx; MemAllocator_: TKVMemoryStream; pFilter_: Pointer; FnKV_GetFilterInterfaceEx_: TKV_GetFilterInterfaceEx; FnKV_GetKvErrorCodeEx_: TKV_GetKvErrorCodeEx; FnKV_FilterConfig_: TKV_FilterConfig; nFilterTOSec_: Integer; bUseHwpDll_: Boolean; // ThdTimeout_: TThdKvFilterTimeout; sKvBinDir_, sKvHwpExe_: String; bProcAnsi_: Boolean; // 영문윈도우에서 한글 깨지는 현상 보완을 위함 24_0514 09:17:24 kku public Constructor Create(sDllDir: String = ''; nFilterTOSec: Integer = 0); // nFilterTOSec 필터 타임아웃 추가 22_0720 07:43:25 kku Destructor Destroy; override; procedure DoSummaryInfo(sSrcFile: String; aList: TStrings); function GetDocInfoFile(sSrcFile: String): TAdDocInfo; function FilterFile(sSrcFile, sDstFile: String): KVErrorCode; property FilterTOSec: Integer write nFilterTOSec_; property UseHwpDll: Boolean write bUseHwpDll_; property KvBinDir: String read sKvBinDir_; property KvLastError: Integer read nLastError_; end; implementation uses CttSchDefine, Tocsg.Exception, Tocsg.Convert, Tocsg.DateTime, Tocsg.Process, Tocsg.Path, superobject, Tocsg.Files, GlobalDefine; function _Malloc(pX: Pointer{PKvMemoryStream}; cb: LONGLONG): Pointer; pascal; begin Result := AllocMem(cb); // GetMem(Result, cb); end; procedure _Free(pX: Pointer{PKvMemoryStream}; ptr: Pointer); pascal; begin FreeMem(ptr); end; function _Realloc(pX: Pointer{PKvMemoryStream}; ptr: Pointer; cb: LONGLONG): Pointer; pascal; begin ReallocMem(ptr, cb); Result := ptr; // Result := ReallocMemory(ptr, cb); end; function _Calloc(pX: Pointer{PKvMemoryStream}; ne, s: LONGLONG): Pointer; pascal; begin Result := AllocMem(ne); ZeroMemory(Result, s); // GetMem(Result, ne); // ZeroMemory(Result, s); end; { TThdKvFilterTimeout } //Constructor TThdKvFilterTimeout.Create(nTOSec: Integer); //begin // Inherited Create; // nTOMilSec_ := nTOSec * 1000; // dwResetTick_ := 0; // StartThread; //end; // //procedure TThdKvFilterTimeout.ResetTimeout; //begin // Lock; // try // dwResetTick_ := GetTickCount; // finally // Unlock; // end; //end; // //procedure TThdKvFilterTimeout.EndTimeout; //begin // Lock; // try // dwResetTick_ := 0; // finally // Unlock; // end; //end; // //procedure TThdKvFilterTimeout.Execute; //var // dwResetTick: DWORD; //begin // while not Terminated and not GetWorkStop do // begin // if nTOMilSec_ > 0 then // begin // try // Lock; // try // dwResetTick := dwResetTick_; // finally // Unlock; // end; // // if dwResetTick > 0 then // begin // if (GetTickCount - dwResetTick) > nTOMilSec_ then // begin // // 두번 죽여야 됨 22_0720 08:54:56 kku // TerminateProcessByName(EXE_KV_TASK); // Sleep(1000); // TerminateProcessByName(EXE_KV_TASK); // EndTimeout; // end; // end; // except // on E: Exception do // ETgException.TraceException(Self, E, 'Fail .. Execute()'); // end; // end; // Sleep(1000); // end; //end; {$IFDEF _IMG_PARSER_} function ImgToTxtFile(sSrcPath: String; sDstPath: String): Boolean; var sOcr: String; StrList: TStringList; begin Result := false; sOcr := GetRunExePathDir + DIR_CONF + EXE_OCR; if not FileExists(sOcr) then sOcr := GetRunExePathDir + EXE_OCR; if FileExists(sOcr) then begin if ForceDirectories(ExtractFilePath(sDstPath)) then begin ExecuteAppWaitUntilTerminate(sOcr, Format('"%s" "%s"', [sSrcPath, sDstPath]), SW_HIDE); Result := FileExists(sDstPath); end; end; end; {$ENDIF} { TKvFilter} Constructor TKvFilter.Create(sDllDir: String = ''; nFilterTOSec: Integer = 0); procedure InitProcedure; var sDllPath: String; nResult: KVErrorCode; begin if sDllDir = '' then begin sDllDir := GetRunExePathDir; if not FileExists(sDllDir + DLL_KVFILTER) then sDllDir := sDllDir + 'bin\'; end else sDllDir := IncludeTrailingPathDelimiter(sDllDir); sDllPath := sDllDir + DLL_KVFILTER; if FileExists(sDllDir + DLL_KVHWP) and FileExists(GetRunExePathDir + EXE_KV_HWP) then sKvHwpExe_ := GetRunExePathDir + EXE_KV_HWP; hKvFilter_ := LoadLibrary(PChar(sDllPath)); if hKvFilter_ = 0 then begin _Trace('Fail .. LoadLibrary(), Path="%s"', [sDllPath]); nLastError_ := KVERR_DLLNotFound; exit; end; sKvBinDir_ := sDllPath; FnKV_GetFilterInterfaceEx_ := GetProcAddress(hKvFilter_, 'KV_GetFilterInterfaceEx'); if not Assigned(FnKV_GetFilterInterfaceEx_) then begin _Trace('Fail .. KV_GetFilterInterfaceEx() is NULL, Path="%s"', [sDllPath]); nLastError_ := KVERR_General; exit; end; FnKV_GetKvErrorCodeEx_ := GetProcAddress(hKvFilter_, 'KV_GetKvErrorCodeEx'); if not Assigned(FnKV_GetKvErrorCodeEx_) then begin _Trace('Fail .. KV_GetKvErrorCodeEx() is NULL, Path="%s"', [sDllPath]); nLastError_ := KVERR_General; exit; end; FnKV_FilterConfig_ := GetProcAddress(hKvFilter_, 'KV_FilterConfig'); if not Assigned(FnKV_FilterConfig_) then begin _Trace('Fail .. KV_FilterConfig() is NULL, Path="%s"', [sDllPath]); nLastError_ := KVERR_General; exit; end; nResult := FnKV_GetFilterInterfaceEx_(@KvCtrl_, KVFLTINTERFACE_REVISION); if nResult <> KVERR_Success then begin _Trace('Fail .. KV_GetFilterInterfaceEx() .. ErrCode=%d, Path="%s"', [nResult, sDllPath]); nLastError_ := nResult; exit; end; MemAllocator_.pMemoryStreamPrivateData := nil; MemAllocator_.KvMalloc := _Malloc; MemAllocator_.KvFree := _Free; MemAllocator_.KvRealloc := _Realloc; MemAllocator_.KvCalloc := _Calloc; pFilter_ := KvCtrl_.KvInit(@MemAllocator_, nil, PAnsiChar(AnsiString(sDllDir)), KVCS_UTF8, 0); end; begin Inherited Create; bProcAnsi_ := GetUserDefaultLangID <> 1042; sKvHwpExe_ := ''; nLastError_ := 0; nFilterTOSec_ := nFilterTOSec; if nFilterTOSec_ = 0 then nFilterTOSec_ := 7; ZeroMemory(@KvCtrl_, SizeOf(KvCtrl_)); pFilter_ := nil; bUseHwpDll_ := true; InitProcedure; if pFilter_ <> nil then KvCtrl_.KvSetTimeout(pFilter_, nFilterTOSec_); {$IFDEF _IMG_PARSER_} ImgEn_ := TImageEn.Create(nil); bDoFree_ := true; // bDoFree_ := false; // if aImgEn <> nil then // begin // ImgEn_ := aImgEn; // end else begin // ImgEn_ := nil; //// ImgEn_ := TImageEn.Create(nil); //// bDoFree_ := true; // end; {$ENDIF} // ThdTimeout_ := TThdKvFilterTimeout.Create(nFilterTOSec_); end; Destructor TKvFilter.Destroy; begin // TerminateProcessByName(EXE_KV_TASK); // 이거 하고 두번째 TKvFilter.Destroy() 시 KvShutdown() 에서 크러쉬 됨 22_0720 10:28:30 kku // FreeAndNil(ThdTimeout_); try // 여기서 크러쉬가 나는데... 원인을 찾을때까지는 봉인한다. // 해제 시 앱이 강제종료 되는 치면적인 문제 있음 // 이거 안하면 릭이 발생함... 22_0712 16:06:12 kku // if (pFilter_ <> nil) and Assigned(KvCtrl_.KvShutdown) then // KvCtrl_.KvShutdown(pFilter_); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. Destroy() - KvShutdown()'); end; if hKvFilter_ <> 0 then FreeLibrary(hKvFilter_); // KvShutdown() 처리를 안하면 kvoop.exe가 남아 있다.. 그래서 처리 추가 22_0720 10:37:07 kku TerminateProcessByName(EXE_KV_TASK); {$IFDEF _IMG_PARSER_} if (ImgEn_ <> nil) and bDoFree_ then FreeAndNil(ImgEn_); {$ENDIF} Inherited; end; procedure TKvFilter.DoSummaryInfo(sSrcFile: String; aList: TStrings); var si: TKVSummaryInfoEx; i: Integer; sInfo: String; pEnt: PKvSumInfoElemEx; ft: TFileTime; begin aList.Clear; try if (pFilter_ <> nil) and Assigned(KvCtrl_.KvGetOLESummaryInfoFile) and Assigned(KvCtrl_.KvFreeOLESummaryInfo) then begin ZeroMemory(@si, SizeOf(si)); if KvCtrl_.KvGetOLESummaryInfoFile(pFilter_, PAnsiChar(AnsiString(sSrcFile)), @si) <> KVERR_Success then begin _Trace('Fail .. DoSummaryInfo() .. 1'); exit; end; try if si.nElem = 0 then exit; for i := 0 to si.nElem - 1 do begin pEnt := PKvSumInfoElemEx(NativeInt(si.pElem) + (SizeOf(TKvSumInfoElemEx) * i)); if pEnt.nIsValid = 0 then continue; sInfo := Format('%d %d ', [pEnt.nIsValid, pEnt.nType]); case pEnt.nType of KV_String : sInfo := sInfo + Format('"%s"', [PUTF8Char(pEnt.pData)]); KV_Int4 : sInfo := sInfo + Format('%d', [Integer(pEnt.pData)]); KV_DateTime : begin CopyMemory(@ft, pEnt.pData, 8); if i = 9 then begin sInfo := sInfo + Format('%d Minutes', [LONGLONG(ft) div 600000000]); end else begin sInfo := sInfo + DateTimeToStr(ConvFileTimeToDateTime(ft)); end; end; KV_ClipBoard : sInfo := sInfo + 'Clipboard data'; KV_Bool : sInfo := sInfo + BooleanToStr(Integer(pEnt.pData) <> 0, 'true', 'false'); KV_Unicode : sInfo := sInfo + Format('U"%s"', [PChar(pEnt.pData)]); KV_IEEE8 : sInfo := sInfo + Format('%f', [pEnt.pData]); KV_Other : sInfo := sInfo + 'Other'; else sInfo := sInfo + Format('Bad element type! - %d', [pEnt.nType]); end; aList.Add(sInfo + Format(' %s', [BooleanToStr(pEnt.nIsValid = 1, AnsiString(pEnt.pcType), '(null)')])); end; finally KvCtrl_.KvFreeOLESummaryInfo(pFilter_, @si); end; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. DoSummaryInfo()'); end; end; function TKvFilter.GetDocInfoFile(sSrcFile: String): TAdDocInfo; begin ZeroMemory(@Result, SizeOf(Result)); if (pFilter_ <> nil) and Assigned(KvCtrl_.KvGetDocInfoFile) then KvCtrl_.KvGetDocInfoFile(pFilter_, PAnsiChar(AnsiString(sSrcFile)), @Result); end; function TKvFilter.FilterFile(sSrcFile, sDstFile: String): KVErrorCode; var sExt, sTemp: String; begin // _Trace('FilterFile() .. Path=%s', [sSrcFile]); // if not FileExists(sSrcFile) then // begin // Result := -2; // exit; // end; Result := -1; try sExt := GetFileExt(sSrcFile).ToUpper; if (sExt = 'TXT') or (sExt = 'LOG') or (sExt = 'HTML') or (sExt = 'HTM') or (sExt = 'XML') or (sExt = 'JSON') or (sExt = 'CSV') or (sExt = 'INI') then begin if CopyFile(PChar(sSrcFile), PChar(sDstFile), false) then Result := KVERR_Success else Result := KVERR_CreateOutputFileFailed; end else {$IFDEF _IMG_PARSER_} if (sExt = 'DCM') and (ImgEn_ <> nil) and ImgEn_.IO.LoadFromFile(sSrcFile) then begin var StrList: TStringList := TStringList.Create; try ImgEn_.IO.Params.Dicom_WriteToStrings(StrList, [diProprietary, diChildTags]); if StrList.Text <> '' then begin StrList.SaveToFile(sDstFile, TEncoding.UTF8); Result := KVERR_Success; end; finally FreeAndNil(StrList); end; end else if (Pos(sExt, OCR_IMG_EXTS) > 0) and ImgToTxtFile(sSrcFile, sDstFile) then begin Result := KVERR_Success; end else {$ENDIF} if (pFilter_ <> nil) and Assigned(KvCtrl_.KvFilterFile) then begin if bUseHwpDll_ and (sKvHwpExe_ <> '') and (sExt = 'HWP') or (sExt = 'HWPX') then begin Result := -2; sTemp := GetRunExePathDir + '$KvHvpInfo.j$on'; var O: ISuperObject := SO; O.S['Src'] := sSrcFile; O.S['Dest'] := sDstFile; if SaveJsonObjToFile(O, sTemp) then begin ExecuteAppWaitUntilTerminate(sKvHwpExe_, Format('"%s"', [sTemp]), SW_HIDE, 5000); if FileExists(sDstFile) then Result := KVERR_Success; end; end else begin // ThdTimeout_.ResetTimeout; // Result := KvCtrl_.KvFilterFile(pFilter_, PAnsiChar(AnsiString(sSrcFile)), PAnsiChar(AnsiString(sDstFile)), nil); // ThdTimeout_.EndTimeout; if bProcAnsi_ then begin var sBkPath: UTF8String := 'C:\ProgramData\HE\kvansi\'; if ForceDirectories(sBkPath) then begin sBkPath := sBkPath + IntToStr(GetTickCount); try Result := KvCtrl_.KvFilterFile(pFilter_, PUTF8Char(UTF8String(sSrcFile)), PUTF8Char(UTF8String(sBkPath)), nil); if Result = KVERR_Success then MoveFile_wait(sBkPath, sDstFile); finally if FileExists(sBkPath) then DeleteFileA(PAnsiChar(sBkPath)); end; end; end else Result := KvCtrl_.KvFilterFile(pFilter_, PUTF8Char(UTF8String(sSrcFile)), PUTF8Char(UTF8String(sDstFile)), nil); end; end; except on E: Exception do begin ETgException.TraceException(Self, E, 'Fail .. FilterFile()'); Result := -99; end; end; end; end.