종종 특정 폴더를 삭제 하려고 할 때에 삭제 할 수 없다는 메시지가 나온다. 이럴 경우에 보통은 해당 파일을 다른 application에서 사용하고 있어서 삭제가 불가능한 경우도 있지만, 분명히 모든 application을 종료 했는데도 삭제가 불가능한 경우가 있다. 이럴 때 최고의 선택은 역시 리부팅~~. 하지만 서버 상에서 이런 파일이 남아 있다던가 하면 리부팅은 좀 힘들고 다른 방법을 찾아야한다.
일단 수동으로 사용자가 직접 삭제를 하거나 그럴 경우에는 copylock(http://file.naver.com/pc/view.html?fnum=76052&cat=34)이라는 프로그램을 추천한다. 이 프로그램으로 해당 핸들을 날리면 리부팅을 하지 않아도 삭제가 가능하다. 바이러스 문제라든가 이미 날아간 핸들을 가지고 있는 드문 경우는 결국 리부팅을 해야겠지만..
리부팅를 해서도 않되고 자동으로 해당 핸들을 딱 날려주는 것을 만드려면 아래의 방법을 사용해야한다. 일단 MS에서 제공하는 아래의 프로그램을 다운 받아야한다(http://technet.microsoft.com/en-us/sysinternals/bb896655.aspx) Handle이라는 commandline tool인데, 간단하면서도 확실하게 메모리 뒤져서 해당 파일을 삭제 시켜주는데, command line tool이라서 그리 친절하지가 않다. 참고적으로 친절한 UI를 제공하는 tool은 Process Explorer라고 비슷한 위치에서 다운 받을 수 있다.(http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx)
handle의 자세한 사용법은 위의 주소에 영어로 나와있어서(^^;), 천천히 읽어보면 그리 어렵지는 않고, 여기서는 바로 사용법과 내가 사용했던 예제를 보여줘서 서버 등의 경우에 어떤 process를 자동으로 죽여주는 방법을 보여주겠다. 일단 handle의 아래 command를 사용하면 해당 파일에 현재 winodows 메모리 상에 있는 process들이 소유하고 있는 handle의 리스트가 나온다

위의 그림과 같이 한번은 Agree를 해줘야하는데, 한번만 해주면 다시는 않뜨니 Agree 해주고 결과를 보면,

대략 위와 같은 파일이 만들어 졌을 것이다. Process Explorer를 보면 GUI가 지원되어서 현재 Process 들이 내 PC에서 무슨 일을 하는지 dynamic하게 볼 수가 있지만, 사용자가 수작업으로 지워져야하므로 반복되는 루틴에는 handle을 사용하는 편이 낫다. 아무튼 위의 파일을 해석을 하자면, pid라는게 있어서, 어떤 process인지 process ID가 있고, 해당 process가 점유하고 있는 자원들의 리스트가 나오는데, 좌측의 16진수가 handle의 ID 값이다.
이 두개의 값을 이용해서 해당 파일의 process를 날릴 수가 있다. 사실 두개를 "꼭" 알아야지만 날릴수가 있다. 일반적인 파일 이름 가지고는 handle에서 해당 파일의 핸들을 날릴 수 있는 UI를 제공하지 않는다. 그래서 hadle로 위와 같이 result.txt에 현재 process의 상태를 저장 해 놓은 다음에, result.txt에서 원하는 파일의 pid 값과 handle id 값을 가져와야지만 원하는 작업을 할 수가 있다. 여기서는 이를 하기 위해서 마법의~ vb script를 사용하였다.
저 밑에 아래의 vbs 소스는 실제 지금 사용중인 소스를 추려 놓은 것인데, 원리는 간단하다. result.txt 파일을 읽어와서 파일 이름을 검색한다. 원하는 파일이 있을 경우 해당 파일의 pid 값과 handle id 값을 배열에 저장 해 놓았다가, handle command로 해당 파일을 날려버리는 것이다. 파일의 handle을 날릴 경우의 handle command 사용법은 아래와 같다
-p로 프로세스 아이디를 명시하고, -c로 핸들의 아이디를 명시한다. -y의 경우는 무조건 yes라고 지정해 주어서, 혹시나 뭐 물어볼 경우에도 자동으로 해당 command를 끝내기 위한 것이다.
Dim toKill(18)
toKill(0) = "AAA.dll"
toKill(1) = "BBB.dll"
..........................
toKill(17) = "\Source" '폴더가 통째로 남아 있는 경우도 있다.
Sub Sleep_Second(intSeconds) ' 특정 시간(초단위) wait 하는 함수
dteStart = Time()
dteEnd = DateAdd("s", intSeconds, dteStart)
While dteEnd > Time()
DoNothing
Wend
End Sub
Sub DoNothing
'While/Wend has quirks when it is empty
End Sub
Function stringCompare(target) ' 결과파일(result.txt) 내에서 삭제하려는 파일이 있는지 string 비교
Dim i, isFound
isFound = false
for i = 0 to 17
if len(target) >= len(toKill(i)) then
if right(target,len(toKill(i))) = toKill(i) then
isFound = true
stringCompare = len(toKill(i))
exit for
end if
end if
next
if not isFound then
stringCompare = 0 'not Found
end if
end Function
'-------------------------------------------------------------------------------
' Kill Process
'-------------------------------------------------------------------------------
Dim strHandleURL, strHandleprefix, count, i, compareResult
Dim strResultAddress(20)
strHandleURL = "c:\handle.txt"
strDeleteHandleURL = "c:\deletehandle.bat"
Set Shell = CreateObject("WScript.Shell")
ReturnCode = Shell.Run("cmd /c " + strDeleteHandleURL) ' c:\handle.txt 파일을 삭제하는 batch 파일이다. 삭제를 안해도 큰 상관없이 계속 덮이 씌우는 것 같다.
strCommand = "cmd /c handle -a > " + strHandleURL '실제 handle command로 현재 process 상태를 저장
ReturnCode = Shell.Run(strCommand)
Sleep_Second(2) ' 2초간 waiting
Set fs = CreateObject("Scripting.FileSystemObject")
if fs.fileExists(strHandleURL) then
Set objFileInput = fs.OpenTextFile(strHandleURL,1)
count = 1
Do While objFileInput.AtEndOfStream = false
strTemp = objFileInput.readLine
strTemp = trim(strTemp)
compareResult = stringCompare(strTemp)
if compareResult > 0 then
strResultAddress(count) = CStr(left(strTemp,5))
count = count + 1
end if
loop
for i = 1 to (count -1) ' 발견된 핸들 날리기, process ID는 따로 검색하지 않았다. 4는 system, 2348은 explorer이다.
strCommand = "cmd /c handle -p 4 -c " + trim(strResultAddress(i)) + " -y"
ReturnCode = Shell.Run(strCommand)
strCommand = "cmd /c handle -p 2348 -c " + trim(strResultAddress(i)) + " -y"
ReturnCode = Shell.Run(strCommand)
next
end if
Sleep_Second(1)
'-------------------------------------------------------------------------------
' 종료
'-------------------------------------------------------------------------------
set Shell = nothing
set fs = nothing
설명이 몇가지 빠진 부분이있는데, 이 스크립트에서는 result,txt를 처음에 무조건 지워준다. 않지워도 잘 돌아가는 것 같지만 깔끔하게 하려고 지워준다. 그리고 toKill이라는 배열에다가 핸들을 날려버릴 파일 명을 적어 놨다. 그리고 process ID 같은 경우가 좀 어려운 경우인데, 여기 case에서는 system(PID:4)과 explorer(PID:2348)에 해당되는 파일만 날려주어서 위와 같이 hard coding 하였는데, 다른 process에서도 문제가 있을 경우는 해당 프로세스를 추가 해야 할 것이다.(PID가 고정이라는 가정아래.. 아니면 PID를 찾는 루틴도 추가 해야 할 것이다)
조금 소스가 지저분 할 수도 있지만(^^;), 대략적인 flow를 보면 그리 어렵지 않기 때문에 더이상의 detail한 설명은 생략하고, 이런식으로 자동으로 핸들을 없애주는 스크립트를 만들어서 서버 상에서 scheduler 등에 걸어주면 주기적으로 메모리를 깔끔하게 청소를 해주어서, 다른 작업에 영향을 미치는(종종 뻑나게 만드는) 현상을 많이 줄일 수 있을 것이다.





덧글