본문 바로가기
Study/MFC

파일/폴더 삭제 SHFileOperation와 IFileOperation

by 뿡뿡대마왕 2011. 9. 28.
반응형
출처: http://yamoe.tistory.com/218

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

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

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

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


Vista 전의 OS에선 SHFileOperation()을 통해 Recursive한 디렉토리 삭제 샘플 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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한 디렉토리 삭제 샘플
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#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. 현재 어떤 윈도우 버전(타입)인지 구하는 함수
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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;
}
반응형

댓글