윈도우에서 폴더 삭제시 비어있는 폴더가 아닌 경우
일일히 폴더 안의 파일을 삭제해줘야 하는데 (DeleteFile후에 RemoveDirectory)
쉘을 사용할 경우 한번에 삭제할 수 있다.
(어떤 사람들은 system("삭제명령")을 쓰라는 얘기도 있는데 이건 너무한 것 같다.) 

이 쉘 함수가 SHFileOperation()으로 파일 및 폴더의 삭제, 복사, 리네임 등 flag에 따라 많은 일을 해준다.
(옵션에 따라 휴지통에 지울 수 도 있다.)

문제는 Vista 이후 부터는  IFileOperation 를 사용하길 권장하고 있으며
실제로 Windows 7에서 SHFileOperation()을 사용할 경우 삭제시 파일을 못찾겠다는 둥 오동작을 한다.

결국 여러 WIndows OS 버전을 만족 시킬려면 윈도우 버전에 따라  SHFileOperation()를 사용할지
IFileOperation()를 사용할지 선택적으로 동작하도록 해줘야 한다.


Vista 전의 OS에선 SHFileOperation()을 통해 Recursive한 디렉토리 삭제 샘플 
bool CCommonUtil::RemoveUseShellUnderVista(_tstring& filename, bool useRecycleBin = true)
{
	// 파일명 끝에 \0\0 이 있어야 정상동작.
	// 그렇지 않은 경우 SHFileOperation()는 1026을 돌려준다.
	_tstring from = filename;
	from.resize(filename.size() + 2);
	from += _T("\0\0");

	SHFILEOPSTRUCT shFileOpStruct = {0,};
	shFileOpStruct.hwnd					= NULL;
	shFileOpStruct.wFunc					= FO_DELETE;
	shFileOpStruct.pFrom					= from.c_str();
	shFileOpStruct.pTo					= NULL;
	shFileOpStruct.fFlags					= FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
	shFileOpStruct.fAnyOperationsAborted	= FALSE;
	shFileOpStruct.hNameMappings			= NULL;
	shFileOpStruct.lpszProgressTitle			= NULL;
	if( useRecycleBin )	// 휴지통에 넣기
	{ 
		shFileOpStruct.fFlags |= FOF_ALLOWUNDO;
	}	

	int ret = SHFileOperation(&shFileOpStruct);
	if( ret == 0 )
	{
		return true;
	}
	else
	{
		printf("FAILED SHFileOperation[%d]\n", ret);
		return false;
	}
}



Vista 부터 이후 OS에선 IFileOperation()을 통해 Recursive한 디렉토리 삭제 샘플
#include "Shobjidl.h"
bool CCommonUtil::RemoveUseShellSinceVista(_tstring& filename, bool useRecycleBin = true)
{
	bool ret = false;

	const TCHAR* pFrom = NULL;
	#ifdef UNICODE
		pFrom = filename.c_str();
	#else
		USES_CONVERSION;
		pFrom = A2W(filename.c_str());
	#endif
	
	IFileOperation* pfo;

	HRESULT hr;
	hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
	if (SUCCEEDED(hr))
	{
		HRESULT hr = CoCreateInstance(CLSID_FileOperation, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pfo));
		if (SUCCEEDED(hr))
		{
			DWORD flags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
			if( useRecycleBin ) { flags |= FOF_ALLOWUNDO; }  // 휴지통에 넣기

			hr = pfo->SetOperationFlags(flags);
			if (SUCCEEDED(hr))
			{
				IShellItem *psiFrom = NULL;
				hr = SHCreateItemFromParsingName(pFrom, NULL, IID_PPV_ARGS(&psiFrom));
				if (SUCCEEDED(hr))
				{
					if (SUCCEEDED(hr))
					{
						hr = pfo->DeleteItem(psiFrom, NULL);
						if (SUCCEEDED(hr))
							printf("success\n");
						else
							printf("fail\n");
					}
					psiFrom->Release();
				}
				if (SUCCEEDED(hr))
				{
					hr = pfo->PerformOperations();
					if (SUCCEEDED(hr))
					{
						ret = true;
					}
				}
			}
			pfo->Release();
		}
	}
	CoUninitialize();

	return ret;
}



주의 1. XP에서 SHFileOperation와 IFileOperation가 모두 구현된 상태에서 실행할 경우
만약, 두 함수를 구현한 후 XP에서 실행시
"프로시저 시작 지점 SHCreateItemFromParsingName을(를) DLL SHELL32.dll에서 찾을 수 없습니다."
 
에러가 발생한다.
해당 함수가 Vista 이후의 shell32.dll 부터 있는 것이기 때문인데
현재 "프로젝트 속성 > 링커 > 입력 > 지연 로드된 DLL" 에 shell32.dll 를 등록하면 된다.


주의 2. 휴지통에 넣는 기능은 네트워크 드라이브에 있는 파일을 지울땐 적용되지 않는다.
일단 확인한것은 네트워크 드라이브로 연결된 하드의 파일/폴더 삭제시엔 휴지통으로 들어가지 않는다.
로컬 파일이 아니기 때문에 복원문제가 복잡하고 보장할 수 없기 때문으로 생각되기 때문에 당연한 듯 하다.
이런 경우도 복원이 보장되야 한다면 휴지통 기능을 직접 구현해야 하겠다.



추가로..
부록 1. 현재 어떤 윈도우 버전(타입)인지 구하는 함수
int GetWindowsType(void)
{
/*
return value :
   -1 : 버전얻기 실패
	1 : Windows 95,  
	2 : Windows 98,  
	3 : Windows ME,  
	4 : Windows NT,
	5 : Windows 2000,  
	6 : Windows XP,  
	7 : Windows 2003,
	8 : Windows Vista, 2008
	9 : Windows 7, 2008 R2
*/

	int nVersion= -1;
	OSVERSIONINFOEX osvi = {0,};
	BOOL version_ex_flag = 0;

	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

	if( !(version_ex_flag = GetVersionEx((OSVERSIONINFO *)&osvi)) )
	{
		osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
		if( !GetVersionEx((OSVERSIONINFO *)&osvi) )
			return -1;
	}

	switch(osvi.dwPlatformId)
	{
	case VER_PLATFORM_WIN32_WINDOWS:	// 윈도우즈 9x 기반의 운영체제인 경우
		{
			if( osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0 )
			{
				nVersion = 1;			// Windows 95
			}
			else if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10)
			{
				nVersion = 2;			// Windows 98
			}
			else if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) 
			{
				nVersion = 3;			// Windows ME
			}
		}
		break;
	case VER_PLATFORM_WIN32_NT:	// NT 기술 기반의 운영체제인 경우
		{	
			if( osvi.dwMajorVersion <= 4 ) 
			{
				nVersion = 4;			// Windows NT
			}
			else if( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 ) 
			{
				nVersion = 5;			//Windows 2000
			}
			else if(version_ex_flag) 
			{
				if( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
				{
					nVersion = 6;		// Windows XP
				}
				else if( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
				{
					nVersion = 7;		// Windows 2003
				}
				else if( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0)
				{
					nVersion = 8;		// Windows Vista, 2008
				}
				else if( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1)
				{
					nVersion = 9;		// WIndows7, 2008 R2
				}
			}
		}  
		break;
	}
	return nVersion;
}


 

'C++' 카테고리의 다른 글

VisualC++ 과 C++0x  (0) 2011.05.30
Regular Expression (정규 표현식)  (0) 2011.05.22
_MSC_VER 를 이용한 Visual Studio 버전별 코드 작성  (0) 2011.05.09
WinINet 과 WinHTTP 샘플  (0) 2011.05.03
윈도우 공유폴더 연결 방법 ( net use )  (0) 2011.04.05
VC++ ADODB SP 호출  (0) 2011.03.30
warning LNK4099: 'vc90.pdb'  (0) 2011.03.25
CRT 이야기 [펌]  (0) 2011.02.14