495 lines
16 KiB
Plaintext
495 lines
16 KiB
Plaintext
{*******************************************************}
|
|
{ }
|
|
{ ProcessUninstall }
|
|
{ }
|
|
{ Copyright (C) 2022 kku }
|
|
{ }
|
|
{*******************************************************}
|
|
|
|
unit ProcessUninstall;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils, Winapi.Windows, System.Classes;
|
|
|
|
function UninstallXPrt(bIgrDelCfg: Boolean): Boolean;
|
|
function UninstallCrmHE(dwExitIgrPid: DWORD = 0; bIgrDelCfg: Boolean = false): Boolean;
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$IFDEF _HE_}
|
|
ManagerService,
|
|
{$ENDIF}
|
|
Tocsg.Path, Tocsg.Process, Tocsg.Service, Tocsg.Files, GlobalDefine,
|
|
Tocsg.Win32, Tocsg.Safe, Tocsg.Firewall, Winapi.ActiveX,
|
|
Tocsg.Shell, Tocsg.Registry, superobject, DefineHelper, Tocsg.Trace,
|
|
GlobalOutAddInDefine, Tocsg.Exception, Tocsg.Disk, Tocsg.Driver, Condition, madCodeHook;
|
|
|
|
var
|
|
arrForceDelConfFiles: array [0..20] of String =
|
|
(EXE_MG, EXE_GC, EXE_RT, EXE_IC, DLL_HOOK, DLL_HOOK32,
|
|
EXE_HP, EXE_LB, DLL_SHELL, EXE_OCR, DAT_CTTSCHRSTDATA_EXP,
|
|
DLL_OT, DLL_OT64, EXE_KF{KFTC}, DLL_OVI_DRM, DLL_OVI_AIP,
|
|
DLL_ST64, DLL_FAS64, DAT_FILEEXP, DLL_PDFIUM, EXE_BROWSER_SECU);
|
|
|
|
arrXprintFiles: array [0..9] of String =
|
|
('BSonehlink.dll', 'BSonehlink64.dll', 'BSoneprint.dll', 'BSoneprint64.dll',
|
|
'BSoneprintclient.exe', 'BSoneprintclient64.exe', 'BSonex32.sys', 'BSonex64.sys',
|
|
'Newtonsoft.Json.dll', 'xPrintTest.exe');
|
|
|
|
function HasRes(sId: String): Boolean;
|
|
var
|
|
res: TResourceStream;
|
|
begin
|
|
Result := false;
|
|
try
|
|
res := TResourceStream.Create(HInstance, sId, 'raw');
|
|
if res <> nil then
|
|
begin
|
|
Result := true;
|
|
res.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(E, Format('Fail .. HasRes(), ID="%s"', [sId]));
|
|
end;
|
|
end;
|
|
|
|
function UninstallXPrt(bIgrDelCfg: Boolean): Boolean;
|
|
var
|
|
xPrtDir, sFName, sTemp: String;
|
|
i, nTO: Integer;
|
|
IgrList: TStringList;
|
|
begin
|
|
Result := false;
|
|
try
|
|
xPrtDir := 'C:\ProgramData\bsoneprint\';
|
|
if FileExists(xPrtDir + EXE_xPrintSvc) then
|
|
begin
|
|
if not bIgrDelCfg then
|
|
begin
|
|
TerminateProcessByName(EXE_xPrintUI);
|
|
Sleep(500);
|
|
end else begin
|
|
if GetProcessPidByName(EXE_xPrintUI) > 0 then
|
|
begin
|
|
TTgTrace.T('Fail .. UninstallXPrt() .. xPrint UI 실행 중', 1);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
// ServiceStop('BSonePrintService');
|
|
// UninstallService('BSonePrintService');
|
|
ExecuteAppWaitUntilTerminate(xPrtDir + EXE_xPrintSvc, '-uninstall', SW_HIDE, 10000);
|
|
|
|
nTO := 0;
|
|
// 서비스 프로세스가 종료될때까지 최대 15초간 기다려준다 25_0826 18:00:56 kku
|
|
while (GetProcessPidByName('BSoneprintclient.exe') <> 0) or
|
|
(GetProcessPidByName(EXE_xPrintSvc) <> 0) do
|
|
begin
|
|
Inc(nTO);
|
|
Sleep(500);
|
|
|
|
if nTO >= 30 then
|
|
break;
|
|
end;
|
|
|
|
for i := Low(arrXprintFiles) to High(arrXprintFiles) do
|
|
begin
|
|
sFName := arrXprintFiles[i];
|
|
if FileExists(xPrtDir + sFName) and
|
|
not DeleteFile(PChar(xPrtDir + sFName)) then
|
|
begin
|
|
sTemp := '$d-' + sFName + '-' + FormatDateTime('yymmddhhnnss', Now);
|
|
MoveFile_wait(xPrtDir + sFName, xPrtDir + sTemp, 2);
|
|
DeleteFile(PChar(xPrtDir + sTemp));
|
|
end;
|
|
end;
|
|
|
|
Guard(IgrList, TStringList.Create);
|
|
if bIgrDelCfg then
|
|
begin
|
|
IgrList.Add('Data\');
|
|
IgrList.Add('logs\');
|
|
IgrList.Add('partbill.ini');
|
|
IgrList.Add('bsoneprofile.conf');
|
|
end;
|
|
DeleteDir(xPrtDir, true, true, IgrList);
|
|
Result := true;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(E, 'Fail .. UninstallXPrt()');
|
|
end;
|
|
end;
|
|
|
|
procedure ReleaseDriveSecu;
|
|
var
|
|
i: Integer;
|
|
dwLogicalDrv: DWORD;
|
|
sDrive: String;
|
|
DriveInfo: TDriveInfo;
|
|
begin
|
|
try
|
|
dwLogicalDrv := GetLogicalDrives;
|
|
for i := 0 to 31 do
|
|
if (dwLogicalDrv and (1 shl i)) > 0 then
|
|
begin
|
|
sDrive := Format('%s:\', [Char(Integer('A')+i)]);
|
|
if not DirectoryExists(sDrive) then
|
|
continue;
|
|
|
|
if UpCase(sDrive[1]) = 'C' then
|
|
continue;
|
|
|
|
if IsReadOnlyByWriteProbe(sDrive) = 1 then
|
|
begin
|
|
GetDriveDetail(sDrive, @DriveInfo);
|
|
SetDriveReadOnly(sDrive, DriveInfo.nDiskNum, false);
|
|
end;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(E, 'Fail .. ReleaseDriveSecu()');
|
|
end;
|
|
end;
|
|
|
|
function UninstallCrmHE(dwExitIgrPid: DWORD = 0; bIgrDelCfg: Boolean = false): Boolean;
|
|
var
|
|
sTemp, sFName,
|
|
sSysDir, sInstDir, sDataDir: String;
|
|
nTO: Integer;
|
|
KillMtx,
|
|
UnInstMtx: TTgMutex;
|
|
IgrList: TStringList;
|
|
i: Integer;
|
|
bDoRemove,
|
|
bRestartExplorer: Boolean;
|
|
begin
|
|
Result := false;
|
|
bRestartExplorer := false;
|
|
|
|
if not bIgrDelCfg then
|
|
UnInstMtx := TTgMutex.Create(MUTEX_UNINSTALL)
|
|
else
|
|
UnInstMtx := nil;
|
|
Guard(KillMtx, TTgMutex.Create(MUTEX_KILL));
|
|
|
|
try
|
|
nTO := 0;
|
|
// 먼저... 알아서 죽도록 기다림
|
|
while MutexExists(MUTEX_AGENT) or MutexExists(MUTEX_SERVICE) do
|
|
begin
|
|
Inc(nTO);
|
|
Sleep(500);
|
|
|
|
if nTO >= 20 then
|
|
break;
|
|
end;
|
|
finally
|
|
if UnInstMtx <> nil then
|
|
FreeAndNil(UnInstMtx);
|
|
end;
|
|
Sleep(1000);
|
|
|
|
{$IFNDEF _SILENT_}
|
|
{$IF not Defined(_SHSC_) or not Defined(_SHCI_)}
|
|
// 신한신용정보, 신한투자증권은 보안프로그램에 쓰기테스트 파일이 감지된다 25_1030 19:45:39 kku
|
|
case CUSTOMER_TYPE of
|
|
CUSTOMER_SHSC,
|
|
CUSTOMER_SHCI : ;
|
|
else ReleaseDriveSecu;
|
|
end;
|
|
{$IFEND}
|
|
{$ENDIF} // _SILENT_
|
|
|
|
sInstDir := GetProgramFilesDir + DIR_HE;
|
|
if not DirectoryExists(sInstDir) then
|
|
begin
|
|
Result := true;
|
|
exit;
|
|
end;
|
|
|
|
if DirectoryExists(sInstDir + 'Language\') then
|
|
DeleteDir(sInstDir + 'Language\');
|
|
|
|
TerminateProcessByName(EXE_SV);
|
|
TerminateProcessByName(EXE_SL);
|
|
TerminateProcessByName(EXE_HE, dwExitIgrPid);
|
|
TerminateProcessByName('javaw.exe');
|
|
TerminateProcessByName(EXE_MG);
|
|
TerminateProcessByName(EXE_RT);
|
|
TerminateProcessByName(EXE_GC);
|
|
TerminateProcessByName(EXE_IC);
|
|
TerminateProcessByName(EXE_KF);
|
|
TerminateProcessByName(EXE_CS);
|
|
TerminateProcessByName(EXE_CW);
|
|
TerminateProcessByName(EXE_KV);
|
|
TerminateProcessByName(EXE_AIP);
|
|
TerminateProcessByName(EXE_BROWSER_SECU);
|
|
TerminateProcessByName('xPrintTest.exe');
|
|
TerminateProcessByName('BSOne-AIP-Decrypt-OP.exe');
|
|
Sleep(500);
|
|
TerminateProcessByName(EXE_KV); // 바로 재실행되서 한번 더 시도해준다
|
|
|
|
bDoRemove := not bIgrDelCfg;
|
|
StopInjectionDriver(BS1HOOK_DRIVERTAG);
|
|
|
|
// 아웃룩 플러그인(Bs1out.dll, Bs1out64.dll) 업데이트가 있을때만 활성화 시켜준다 25_0924 14:08:42 kku
|
|
// if ExistsKey(HKEY_CLASSES_ROOT, REG_BS1OutlookAddInKey) then
|
|
// TerminateProcessByName('outlook.exe');
|
|
|
|
sSysDir := GetSystemDir;
|
|
// 이거 살림. SetVisibleService(false) 사용하던 구버전 업데이트 시 필요 25_0423 12:39:56 kku
|
|
SetVisibleService(NAME_SERVICE, true);
|
|
Sleep(1000);
|
|
|
|
if bDoRemove then
|
|
begin
|
|
if ExistsKey(HKEY_CLASSES_ROOT, REG_BS1OutlookAddInKey) then
|
|
begin
|
|
TerminateProcessByName('outlook.exe');
|
|
Sleep(500);
|
|
ExecutePath_hide('regsvr32.exe', Format('/u /s "%s"', [sInstDir + DIR_CONF + DLL_ADDIN]));
|
|
ExecutePath_hide('regsvr32.exe', Format('/u /s "%s"', [sInstDir + DIR_CONF + DLL_ADDIN64]));
|
|
end;
|
|
|
|
if FileExists(sInstDir + DIR_CONF + DLL_OT) then
|
|
ExecutePath_hide('regsvr32.exe', Format('/u /s "%s"', [sInstDir + DIR_CONF + DLL_OT]));
|
|
if FileExists(sInstDir + DIR_CONF + DLL_OT64) then
|
|
ExecutePath_hide('regsvr32.exe', Format('/u /s "%s"', [sInstDir + DIR_CONF + DLL_OT64]));
|
|
|
|
sTemp := sInstDir + DIR_CONF + DLL_OVI_DRM;
|
|
if ExistsKey(HKEY_LOCAL_MACHINE, REG_BS1_OVI_DRM) and FileExists(sTemp) then
|
|
begin
|
|
ExecutePath_hide('regsvr32.exe', Format('/u /s "%s"', [sTemp]));
|
|
bRestartExplorer := true;
|
|
end;
|
|
sTemp := sInstDir + DIR_CONF + DLL_OVI_AIP;
|
|
if ExistsKey(HKEY_LOCAL_MACHINE, REG_BS1_OVI_AIP) and FileExists(sTemp) then
|
|
begin
|
|
ExecutePath_hide('regsvr32.exe', Format('/u /s "%s"', [sTemp]));
|
|
bRestartExplorer := true;
|
|
end;
|
|
// 파수 DLL 해제
|
|
sTemp := sInstDir + DIR_CONF + DLL_FAS64;
|
|
// 컴퓨터\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\TypeLib\{D3135A34-059E-4A47-AAD4-0D84F41DC165}
|
|
if ExistsKey(HKEY_CLASSES_ROOT, 'TypeLib\{D3135A34-059E-4A47-AAD4-0D84F41DC165}\1.0\0\win64') and FileExists(sTemp) then
|
|
begin
|
|
ExecutePath_hide('regsvr32.exe', Format('/u /s "%s"', [sTemp]));
|
|
bRestartExplorer := true;
|
|
end;
|
|
end;
|
|
|
|
if ServiceExists(NAME_SERVICE) then
|
|
begin
|
|
if FileExists(sSysDir + DLL_SV) then
|
|
UninstallServiceDll(sSysDir + DLL_SV)
|
|
else
|
|
UninstallService(NAME_SERVICE);
|
|
|
|
DelRegKey(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\SafeBoot\Minimal\SvcCrmHe');
|
|
DelRegKey(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\SafeBoot\Network\SvcCrmHe');
|
|
end;
|
|
|
|
if ServiceExists(NAME_SERVICE_OLD) then
|
|
UninstallService(NAME_SERVICE_OLD);
|
|
Sleep(500);
|
|
|
|
DeleteFile(PChar(sSysDir + EXE_SV));
|
|
DeleteFile(PChar(sSysDir + DLL_SV));
|
|
DeleteFile(PChar(sSysDir + EXE_SL));
|
|
DeleteFile(PChar(sSysDir + BAT_HE));
|
|
|
|
DeleteFile(PChar(sInstDir + 'conf\d_' + EXE_MG));
|
|
DeleteFile_wait(sInstDir + DAT_AIPUP);
|
|
|
|
if bDoRemove then
|
|
begin
|
|
// 화면보호기 되돌림 23_0612 15:30:46 kku
|
|
var sUserSid: String := GetRegRecentUserSid;
|
|
if sUserSid <> '' then
|
|
begin
|
|
sTemp := GetRegValueAsString(HKEY_USERS, sUserSid + '\Control Panel\Desktop', 'toBkSCRNSAVE.EXE');
|
|
if sTemp <> '' then
|
|
begin
|
|
SetRegValueString(HKEY_USERS, sUserSid + '\Control Panel\Desktop', 'SCRNSAVE.EXE', sTemp, true);
|
|
DelRegValue(HKEY_USERS, sUserSid + '\Control Panel\Desktop', 'toBkSCRNSAVE.EXE');
|
|
end;
|
|
end;
|
|
|
|
// 에어로 피크 해제 설정 제거 25_0924 13:31:03 kku
|
|
sTemp := sUserSid + '\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced';
|
|
DelRegValue(HKEY_USERS, sTemp, 'DisablePreviewDesktop');
|
|
DelRegValue(HKEY_USERS, sTemp, 'DisablePreviewWindow');
|
|
|
|
// 크롬, 엣지 설정 되돌리기 23_1227 11:29:54 kku
|
|
if GetRegValueAsInteger(HKEY_LOCAL_MACHINE,
|
|
'SOFTWARE\Policies\Microsoft\Edge', 'BS1') = 9 then
|
|
DelRegKey(HKEY_LOCAL_MACHINE, 'SOFTWARE\Policies\Microsoft\Edge');
|
|
|
|
if GetRegValueAsInteger(HKEY_LOCAL_MACHINE,
|
|
'SOFTWARE\Policies\Google\Chrome', 'BS1') = 9 then
|
|
DelRegKey(HKEY_LOCAL_MACHINE, 'SOFTWARE\Policies\Google\Chrome');
|
|
|
|
// 위치 서비스 강제 활성 복원 25_0827 14:27:30 kku
|
|
if GetRegValueAsInteger(HKEY_LOCAL_MACHINE, 'SOFTWARE\Policies\Microsoft\Windows\AppPrivacy', 'BsLaal') = 1 then
|
|
begin
|
|
DelRegValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Policies\Microsoft\Windows\AppPrivacy', 'LetAppsAccessLocation');
|
|
DelRegValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Policies\Microsoft\Windows\AppPrivacy', 'BsLaal');
|
|
end;
|
|
|
|
// 완전히 삭제 전 처리
|
|
|
|
// 쉘 DLL 정리
|
|
sTemp := sInstDir + DIR_CONF + DLL_SHELL;
|
|
if ExistsKey(HKEY_CLASSES_ROOT, REG_BS1_SHELL) and FileExists(sTemp) then
|
|
begin
|
|
ExecutePath_hide('regsvr32.exe', '/s /u "' + sTemp + '"');
|
|
Sleep(500);
|
|
DeleteFile(PChar(sTemp));
|
|
|
|
// if not DeleteFile(PChar(sInstDir + DIR_CONF + DLL_SHELL)) then
|
|
if FileExists(sTemp) then
|
|
bRestartExplorer := true;
|
|
end;
|
|
end;
|
|
|
|
if bRestartExplorer then
|
|
begin
|
|
TerminateProcessByName('explorer.exe');
|
|
Sleep(500);
|
|
ExecuteAppAsUser('msedge.exe', 'explorer.exe', '', SW_HIDE);
|
|
end;
|
|
|
|
if not bIgrDelCfg or HasRes('RS_XPRINT_DATA') then
|
|
UninstallXPrt(bIgrDelCfg);
|
|
|
|
for i := Low(arrForceDelConfFiles) to High(arrForceDelConfFiles) do
|
|
begin
|
|
sFName := arrForceDelConfFiles[i];
|
|
if FileExists(sInstDir + DIR_CONF + sFName) and
|
|
not DeleteFile(PChar(sInstDir + DIR_CONF + sFName)) then
|
|
begin
|
|
sTemp := '$d-' + sFName + '-' + FormatDateTime('yymmddhhnnss', Now);
|
|
MoveFile_wait(sInstDir + DIR_CONF + sFName, sInstDir + DIR_CONF + sTemp, 2);
|
|
|
|
if sFName = DLL_HOOK then
|
|
begin
|
|
// EjectModuleFromPath2(sInstDir + DIR_CONF + DLL_HOOK, 'olk.exe|ms-teams.exe|msedgewebview2.exe');
|
|
// Sleep(500);
|
|
// EjectModuleFromPath2(sInstDir + DIR_CONF + DLL_HOOK, 'olk.exe|ms-teams.exe|msedgewebview2.exe');
|
|
end else
|
|
if sFName = DLL_HOOK32 then
|
|
begin
|
|
// ExecutePath_hide(sInstDir + DIR_CONF + EXE_HP, '-clearhook');
|
|
Sleep(500);
|
|
end else begin
|
|
TerminateProcessByName(sFName);
|
|
end;
|
|
DeleteFile(PChar(sInstDir + sTemp)); // 다시 삭제 시도하고 안되면 나중에 정리로 맡김 22_0922 09:16:48 kku
|
|
end;
|
|
end;
|
|
|
|
sDataDir := sInstDir[1] + ':\ProgramData\Tocsg\bs1\';
|
|
if DirectoryExists(sDataDir) then
|
|
begin
|
|
DeleteFile(PChar(sDataDir + 'CI.bmp'));
|
|
var arrData: array [0..1] of String;
|
|
arrData[0] := DLL_HOOK;
|
|
arrData[1] := DLL_HOOK32;
|
|
for i := Low(arrData) to High(arrData) do
|
|
begin
|
|
sFName := arrData[i];
|
|
if FileExists(sDataDir + sFName) and
|
|
not DeleteFile(PChar(sDataDir + sFName)) then
|
|
begin
|
|
sTemp := '$d-' + sFName + '-' + FormatDateTime('yymmddhhnnss', Now);
|
|
MoveFile_wait(sDataDir + sFName, sDataDir + sTemp, 2);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
IgrList := nil;
|
|
if bIgrDelCfg then
|
|
begin
|
|
IgrList := TStringList.Create;
|
|
IgrList.Add(DIR_CONF);
|
|
IgrList.Add(DIR_CACHE);
|
|
IgrList.Add(DIR_KV);
|
|
// IgrList.Add(DIR_AIP12);
|
|
IgrList.Add(DIR_AIP14);
|
|
IgrList.Add(DIR_AIP17);
|
|
IgrList.Add(DAT_PREF);
|
|
IgrList.Add(DAT_AGENT);
|
|
IgrList.Add(DAT_COMPANY);
|
|
IgrList.Add(DAT_CTTSCH);
|
|
IgrList.Add(DAT_CLTFLD);
|
|
IgrList.Add(DAT_RULE);
|
|
IgrList.Add(DAT_PRTWTEXCEPT);
|
|
IgrList.Add('prevfeature.ini');
|
|
IgrList.Add('browpolicy.ini');
|
|
IgrList.Add(NAME_PREF + '-01' + EXT_PROP);
|
|
IgrList.Add(NAME_PREF + '-02' + EXT_PROP);
|
|
IgrList.Add(NAME_PREF + '-03' + EXT_PROP);
|
|
IgrList.Add(NAME_PREF + '-04' + EXT_PROP);
|
|
IgrList.Add(NAME_PREF + '-05' + EXT_PROP);
|
|
IgrList.Add(DAT_CAMPEXCEPT);
|
|
IgrList.Add(DAT_FIXDISK);
|
|
IgrList.Add(DAT_CAMPN);
|
|
IgrList.Add(PROP_USERINFO);
|
|
IgrList.Add(INI_HE);
|
|
IgrList.Add('Log');
|
|
IgrList.Add('Data');
|
|
IgrList.Add('Resource');
|
|
IgrList.Add('sltgd.dat');
|
|
IgrList.Add(EXE_SPL);
|
|
IgrList.Add('spl2pdf_lib');
|
|
IgrList.Add('AIP');
|
|
IgrList.Add(ExcludeTrailingPathDelimiter(DIR_CTTSCHRST));
|
|
IgrList.Add(EXE_SafePCUninst);
|
|
DeleteDir('C:\ProgramData\HE\');
|
|
|
|
// 마지막 정책 수신값 정리, 업데이트 이후 정책 새로 업데이트를 위함 23_0907 10:27:09 kku
|
|
DelRegValue(HKEY_LOCAL_MACHINE, REG_HE, 'LP');
|
|
end else begin
|
|
// 완전히 삭제 후 처리
|
|
// 방화벽 설정 정리 22_0519 09:41:28 kku
|
|
CoInitialize(nil);
|
|
try
|
|
ClearCrmFwPolicy(nil, true);
|
|
finally
|
|
CoUninitialize;
|
|
end;
|
|
|
|
// 레지스트리 정리
|
|
DelRegKey(HKEY_LOCAL_MACHINE, REG_HE);
|
|
end;
|
|
|
|
try
|
|
// nTO := 0;
|
|
// while (nTO < 20) and not Result do
|
|
// begin
|
|
// DeleteDir(sInstDir, true, true, IgrList);
|
|
// Sleep(500);
|
|
// Inc(nTO);
|
|
// Result := not DirectoryExists(sInstDir);
|
|
//
|
|
// // 음.. 패치는 전체 다 삭제 안되는게 정상이니까 일단 이렇게 처리...
|
|
// if (IgrList <> nil) and (nTO > 2) then
|
|
// break;
|
|
// end;
|
|
|
|
// 훅 DLL 삭제가 안될 수 있기 때문에 이젠 파일이 남아있어도 정상처리 해줌 22_0922 10:13:44 kku
|
|
DeleteDir(sInstDir, true, true, IgrList);
|
|
Result := true;
|
|
finally
|
|
if IgrList <> nil then
|
|
FreeAndNil(IgrList);
|
|
end;
|
|
end;
|
|
|
|
end.
|