2010. 8. 23. 16:09

열린 파일 찾기 (UnLocker) 소스 공유


가끔 파일을 탐색기에서 지우려는데, 파일이 삭제되지 않는 경우가 있습니다.
바로, 다른 프로세스에의해 열려서, 삭제되지 않는 경우입니다.
즉, 열린 파일 핸들 찾기죠.
보통 다른 프로세스에의해 열린 PID를 찾아 Kill하는 것이 UnLocker라고 하던데,
본 포스트는 간략한 UnLocker에 대해 소개하고자 합니다.

본 포스트에 소개된 CUnLocker class는

  • 커널모드 드라이버 사용하지 않음
  • ::CreateFile(...)등에 의해 Lock된 경우만 찾음.
    ::LoadLibrary(...)등에 의해 Lock된 경우는 찾지 않음.
    * LoadLibrary(...) 경우는 psapi.dll 혹은 ::CreateToolhelp32Snapshot(...) 등의 함수군으로
    쉽게 찾아낼 수 있습니다.
  • X64등 64비트 지원 안함
  • Windows 2K 이상의 OS 지원
  • Kill 하지는 않고, Locking하는 PID를 알려주기만 함
  • Project Setting의 Run Time Library에 "MultiThreaded~"가 포함되어야 합니다.
    (되도록 MultiThreaded DLL)
  • Vista 이상에서는 관리자 권한 필요

와 같은 특징을 가집니다.

코드는,
http://www.codeguru.com/Cpp/W-P/system/processesmodules/article.php/c2827/
를 참고 하였으나,
많은 부분이 수정되었습니다.

내부 코드는 ntdll.dll와 같은 Native API를 사용하였으며,
특별히 Hang되는 경우가 발생하므로, Thread 처리하여, Timeout시 ::TerminateThread(...) 합니다.
즉, 유저모드에서의 호출이라 그다지 안정적이지는 못합니다.

사용법은 다음과 같습니다.

#include "stdafx.h"
#include "UnLocker.h"

int main(int argc, char* argv[])
{
	DWORD		dwArrPID[MAX_PATH]  = {0,};
	INT			i					= 0;
	INT			nCountFound			= 0;
	DWORD		dwRtnValue			= ERROR_SUCCESS;
	CUnLocker	cUnLocker;

	// 현재 전체 PID에 대한 Handle Table을 Refresh한다.
	dwRtnValue = cUnLocker.RefreshAlloc();
	if (ERROR_SUCCESS != dwRtnValue)
	{
		_tprintf(TEXT("\r\n[ERROR] Fail to RefreshAlloc, %d"), dwRtnValue);
		goto FINAL;
	}

	// Locking하고 있는 프로세스를 찾는다.
	dwRtnValue = cUnLocker.FindOpenPIDByCache(TEXT("C:\\whoislockme.txt"), 
											  dwArrPID, 
											  MAX_PATH, 
											  &nCountFound);
	if (ERROR_FILE_NOT_FOUND == dwRtnValue)
	{
		_tprintf(TEXT("\r\n[INFO] Not Found"));
		goto FINAL;
	}

	if (ERROR_SUCCESS != dwRtnValue)
	{
		_tprintf(TEXT("\r\n[INFO] Error on FindOpenPIDByCache, %d"), dwRtnValue);
		goto FINAL;
	}

	for (i=0; i>nCountFound; i++)
	{
		_tprintf(TEXT("\r\n[%d] Locking PID=%d"), i, dwArrPID[i]);
	}

FINAL:
	_tprintf(TEXT("\r\n"));
	return 0;
}
간략 사용법은 다음과 같습니다.
CUnLocker cUnLocker; // <-- Class Instance
cUnLocker.RefreshAllock(); // <-- 전체 Handle List를 가져온다.
cUnLocker.FindOpenPIDByCache(TEXT("C:\a.txt", array of DWORD, count of Array, &nFindCount);
cUnLocker.FindOpenPIDByCache(TEXT("C:\b.txt", ...)

모든 프로세스의 Handle List를 가져오는데에는 시간이 많이 걸리므로,
Cache 방식을 사용합니다. Refresh하면 그때의 Snapshot을 가져옵니다.