^(코딩캣)^ = @"코딩"하는 고양이;

Windows 쉘 익스텐션 개발 가이드 - (6) 보내기 메뉴

API/COM
2021. 2. 9. 20:36

입문자를 위한 Windows Shell Extension 개발 가이드

본 게시물은 ‘codeproject.com’에 게시된 “The Complete Idiot's Guide to Writing Shell Extensions” 시리즈를 우리말로 번역한 것입니다.

원문의 주소는 “https://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=152”입니다. 원문은 2000년에 작성되었지만 네이티브 수준에서 Windows 운영체제가 근본적으로 바뀌지 않는 이상 현재에도 여전히 유효한 내용입니다. 다만 소스코드가 Visual C++ 6.0을 기준으로 작성되었기 때문에 현재 버전의 Visual Studio에서 자동으로 생성해주는 코드의 형태와는 다소 차이가 있을 수 있음을 감안하시기 바랍니다.

또한 본 게시물은 원문을 최대한 직역하는 것을 지향하고 있으나, 우리말로 읽었을 때 보다 매끄럽게 하기 위하여 부득이 의역, 어순 조정 및 어휘 조정이 있음을 양해 바랍니다.

 

  1. 목차
  2. 쉘 익스텐션(Shell Extension)을 작성하기 위한 단계별 튜토리얼
    1. 파트 1
    2. 파트 2
  3. 여러 개의 파일에 대해 한번에 작동하는 쉘 익스텐션(Shell Extension)
    1. 파트 1
    2. 파트 2
  4. 파일에 대해 ‘팝업(Popup)’ 설명을 보여주는 쉘 익스텐션(Shell Extension)
  5. 사용자 정의 ‘드래그 앤 드롭(Drag and Drop)’ 기능을 제공하는 쉘 익스텐션(Shell Extension)
  6. 파일에 대한 ‘등록 정보’(또는 ‘속성’) 다이얼로그에 페이지를 추가하는 쉘 익스텐션(Shell Extension)
    1. 파트 1
    2. 파트 2
  7. ‘보내기(Send To)’ 메뉴에서 사용될 수 있는 쉘 익스텐션(Shell Extension)
  8. 컨텍스트 메뉴에 그림 출력하는 쉘 익스텐션(Shell Extension)
    및 디렉토리의 빈 공간에서 마우스 오른쪽 클릭에 응답하는 컨텍스트 메뉴 익스텐션(Shell Extension)
    1. 파트 1
    2. 파트 2
    3. 파트 3
  9. Windows 탐색기에서 “자세히” 보기 모드를 선택할 때 나타나는 열 항목을 추가하는 쉘 익스텐션(Shell Extension)
    1. 파트 1
    2. 파트 2
  10. 특정 형식의 파일에 대해 아이콘을 사용자화 하는 쉘 익스텐션(Shell Extension)

 

6 단계. ‘보내기(Send To)’ 메뉴에서 사용될 수 있는 쉘 익스텐션(Shell Extension)

실습 프로젝트 다운로드

ShellExtGuide6_demo.zip
99.3 kB

 

들어가기에 앞서......

여러분은 이제 6 단계까지 왔습니다. 필자는 상대적으로 덜 사용되는 쉘 익스텐션인 ‘드롭 핸들러(Drop Handler)’를 여러분에게 소개해 드리고자 합니다. 이 유형의 쉘 익스텐션은 ‘드롭’의 목표가 되는 파일에 따라 앞으로 호출될 쉘 익스텐션이 결정되고 Windows 탐색기의 ‘드래그 앤 드롭’ 기능을 프로그램적으로 새롭게 확장할 수 있습니다.

이번 단계에서는 여러분이 쉘 익스텐션의 기본에 대해 알고 있고, 쉘(shell)과 상호작용하기 위해 사용되는 MFC 클래스들에 친숙하다고 가정합니다. 여러분이 MFC 클래스에 대해 다시 알아보고자 한다면, 4 단계로 되돌아가시기 바랍니다. 4 단계는 이번 단계에서 사용하는 것과 같은 기법을 사용합니다.

4 단계에서 필자는 마우스 오른쪽 버튼을 누른 상태로 드래그 앤 드롭을 할 때 호출되는 드래그 앤 드롭 핸들러에 대해 설명하였습니다. Windows 탐색기는 또한 마우스 왼쪽 버튼을 누른 상태로 특정 파일에 ‘드롭’하는 드래그 앤 드롭 동작을 하는 동안에도 쉘 익스텐션이 호출될 수 있도록 만들어졌습니다. 예를 들어 WinZip은 다른 파일들을 드래그하여 .zip 파일 아이콘에 ’드롭’ 하였을 때 .zip 파일에 해당 파일들을 추가할 수 있는 쉘 익스텐션을 제공하고 있습니다. 여러분이 .zip 파일로 다른 파일들을 드래그하여 가져오면 Windows 탐색기는 .zip 파일이 ‘드롭’ 동작의 대상이 될 수 있음을 알리기 위하여 .zip 파일의 아이콘을 강조하고 마우스 포인터도 [+] 모양의 아이콘이 포함된 모양으로 바꿉니다.

 

드래그 동작이 zip 파일 위를 지나갈 때 마우스 포인터에 [+] 표시가 추가된다.

 

만일 드래그 중인 마우스 포인터가 ‘드롭’할 대상이 아닌 파일 위를 지나갈 때는 아무것도 변하지 않습니다.

 

드래그 동작이 exe 파일 위로 지나갈 때는 마우스 포인터에 아무 변화도 없다.

 

드롭 핸들러는 WinZip의 경우와 같이 여러분만의 파일 형식을 개발할 때 매우 유용하게 쓰일 수 있습니다. 드롭 핸들러를 더 흥미롭게 다루기 위해, “보내기(Send To)” 메뉴에 항목을 추가할 수도 있습니다. “보내기(Send To)” 메뉴는 SendTo라는 폴더의 항목을 보여줍니다. Windows 9x의 경우 이 폴더는 Windows 디렉토리 안에 있고, Windows NT 기반의 운영체제의 경우 사용자 프로필 디렉토리 안에 있습니다. 이전 버전의 Windows의 경우 SendTo 디렉토리는 단순히 바로가기 파일들만을 가지고 있었지만, Shell Power Toys 같은 서드파티 앱을 포함하여 새로운 버전의 Windows의 경우 다음과 같이 바로가기가 아닌 파일들도 보여줄 수 있습니다.

 

컨텍스트 메뉴의 ‘보내기(N)’ 메뉴(1).

 

위 그림에서 숨어있는 드롭 핸들러를 발견하지 못했다면 SendTo 폴더에 들어있는 파일 목록을 보시기 바랍니다.

 

12/23/05  3:39a   129 3½ Floppy (A).lnk
12/28/05  1:42p     0 Desktop (create shortcut).DeskLink
12/28/05  1:42p     0 Mail Recipient.MAPIMail
12/23/05 12:31p     0 My Documents.mydocs
 5/25/06 11:05a 1,267 Notepad.lnk

 

.DeskLink와 같은 이상한 확장명이 있음에 주목하시기 바랍니다. 이들 0 바이트 파일은 “보내기(Send To)” 메뉴에 나타나기 위해 존재하는 파일들이고, 이들에 대한 쉘 익스텐션은 레지스트리에 등록되어 있습니다. 이들은 open이나 print와 같은 동사(verb)를 갖지 않기에 비록 일반적인 동작을 하는 것은 아니지만, 이들이 가지고 있는 것이 바로 드롭 핸들러입니다. 여러분이 이들 항목 중 하나를 “보내기(Send To)” 메뉴에서 선택하였을 때, Windows 탐색기는 해당 파일에 관련된 드롭 핸들러를 호출하게 됩니다.

‘드롭’ 동작 대상의 유형에 따라 메뉴 항목을 분류하면 다음과 같습니다.

 

컨텍스트 메뉴의 ‘보내기(N)’ 메뉴(2).

 

이번 단계의 예제 프로젝트는 예전에 사용되었던 “아무 폴더로 보내기(Send To Any Folder)”라는 도구의 클론이 되겠습니다. 이것은 선택한 파일을 여러분의 컴퓨터에서 접근 가능한 임의의 폴더로 이동 또는 복사되게 합니다.

 

초기화 인터페이스

이제 여러분은 지금까지 설명했던 프로젝트 생성 및 클래스 추가 등의 과정에 대해서는 익숙해 지셨을 것입니다. 그러므로 필자는 Visual C++ 마법사에 대한 설명은 생략하겠습니다. 새로운 ATL COM 프로젝트를 생성하고 그 이름을 SendToClone으로 지정합니다. 또한 CSendToShlExt라는 이름으로 C++ 클래스를 추가합니다.

드롭 핸들러는 드롭의 대상이 되는 하나의 파일에 대해서만 실행될 것이기 때문에, 쉘 익스텐션의 초기화는 IPersistFile 인터페이스를 통해 수행됩니다. IPersistFile 인터페이스는 한 번에 하나의 파일에 대해서만 호출되는 쉘 익스텐션이 사용함을 기억하시기 바랍니다.

IPersistFile은 여러 가지 메소드들을 가지고 있지만, 쉘 익스텐션에서는 Load 메소드만 구현하면 됩니다.

먼저 CSendToShlExt이 구현하는 인터페이스 목록에 IPersistFile을 추가해야 합니다. SendToShlExt.h를 열고 다음과 같이 추가합니다.

 

#include <comdef.h>
#include <shlobj.h>
 
class CSendToShlExt : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CSendToShlExt, &CLSID_SendToShlExt>,
    public IPersistFile { // 새로 추가
    BEGIN_COM_MAP(CSendToShlExt)
        COM_INTERFACE_ENTRY(IPersistFile)
    END_COM_MAP()
    
    public:
        // IPersistFile
        STDMETHOD(GetClassID)(LPCLSID)     { return E_NOTIMPL; } // 새로 추가
        STDMETHOD(IsDirty)()                { return E_NOTIMPL; } // 새로 추가
        STDMETHOD(Load)(LPCOLESTR, DWORD)   { return S_OK;      } // 새로 추가
        STDMETHOD(Save)(LPCOLESTR, BOOL)    { return E_NOTIMPL; } // 새로 추가
        STDMETHOD(SaveCompleted)(LPCOLESTR) { return E_NOTIMPL; } // 새로 추가
        STDMETHOD(GetCurFile)(LPOLESTR*)    { return E_NOTIMPL; } // 새로 추가
        
        // ...

 

그런데 Load는 별 다른 작업 없이 그냥 S_OK를 반환하고 있습니다. Load 메소드는 ‘드롭’의 대상이 되는 파일의 전체 경로를 전달받기는 하지만, 지금 만들고 있는 쉘 익스텐션에서는 이를 사용하지 않을 것이므로 무시합니다.

 

드래그 앤 드롭 작동에 참여하기

이 작동을 구현하기 위하여 우리는 ‘드롭’이 발생하는 원천, 즉 Windows 탐색기와 통신할 필요가 있습니다. 우리의 쉘 익스텐션은 드래그되는 파일의 목록을 전달받고, Windows 탐색기에게 사용자의 ‘드롭’ 동작을 응할 것인지 여부를 말합니다. 이 통신 과정은 IDropTarget 인터페이스를 통해 수행됩니다. IDropTarget 인터페이스에는 다음과 같은 메소드가 들어 있습니다.

- DragEnter: 사용자의 드래그 동작이 특정 파일 위를 지나갈 때 호출됩니다. 이 메소드의 반환 값은 사용자가 해당 파일 위에 ‘드롭’할 때 Windows 탐색기에게 그 ‘드롭’ 동작에 응할 것인지 여부를 나타냅니다.

- DragOver: 쉘 익스텐션에서는 호출되지 않습니다.

- DragLeave: 사용자의 드래그 동작이 특정 파일 위에서 ‘드롭’하지 않고 그냥 벗어났을 때 호출됩니다.

- Drop: 사용자가 대상 파일에 ‘드롭’했을 때 호출됩니다. 쉘 익스텐션이 실질적으로 작동하는 부분입니다.

CSendToShlExt 클래스에 IDropTarget 인터페이스를 추가하기 위하여 SendToShlExt.h를 열고 다음과 같이 내용을 추가합니다.

 

class CSendToShlExt : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CSendToShlExt, &CLSID_SendToShlExt>,
    public IPersistFile,
    public IDropTarget { // 새로 추가
    BEGIN_COM_MAP(CSendToShlExt)
        COM_INTERFACE_ENTRY(IPersistFile)
        COM_INTERFACE_ENTRY(IDropTarget) // 새로 추가
    END_COM_MAP()
    
    public:
        // IDropTarget
        STDMETHODIMP DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect); // 새로 추가
        STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) { return E_NOTIMPL; } // 새로 추가
        STDMETHODIMP DragLeave(); // 새로 추가
        STDMETHODIMP Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect); // 새로 추가
        
    protected:
        CStringList m_lsDroppedFiles; // 새로 추가
}

 

이전 단계에서 실습한 바와 같이, 우리는 드래그되고 있었던 파일들의 목록을 문자열 리스트로 보관하겠습니다. DragOver 메소드는 호출될 일이 없을 것이므로 특별히 구현할 것도 없습니다. 이제부터 나머지 세 가지 메소드에 대해 다루어 보겠습니다.

 

DragEnter

DragEnter의 원형은 다음과 같습니다.

HRESULT IDropTarget::DragEnter(
    IDataObject* pDataObj,
    DWORD        grfKeyState,
    POINTL       pt,
    DWORD*       pdwEffect);

 

pDataObjIDataObject 인터페이스형 포인터로서 드래그되고 있는 파일들을 하나씩 열거할 수 있습니다.

grfKeyState는 플래그(flag)들의 집합으로서 Shift 키의 눌림 여부 및 어느 마우스 버튼이 클릭되었는지를 전달합니다.

ptPOINT와 동일하게 작동하는 POINTL 구조체로서 마우스 포인터의 좌표를 나타냅니다.

pdwEffectDWORD에 대한 포인터로서, 우리가 ‘드롭’ 동작을 허용할 것인지, 허용한다면 마우스 포인터에 어떤 아이콘을 덧붙일 것인지 여부를 Windows 탐색기에게 반환할 장소입니다.

앞서 언급하였듯이, DragEnter는 일반적으로 사용자의 드래그 동작이 처음으로 대상이 될 수 있는 파일 위로 지나갈 때 호출된다고 하였습니다. 그러나 이 함수는 또한 사용자가 “보내기(Send To)” 메뉴의 항목을 클릭하였을 때도 호출됩니다. 따라서 우리는 기능적으로 드래그 앤 드롭이 발생하지 않은 때에도 DragEnter에서 작업을 수행할 수 있습니다.

DragEnter 구현은 먼저 드래그되고 있는 파일들의 목록으로 문자열 리스트를 채울 것입니다. 파일 시스템에 존재하는 어떤 개체이든 복사와 이동이 가능하기 때문에, 이 쉘 익스텐션은 모든 파일과 디렉토리를 받아들입니다. 이를 위해 DragEnter가 시작하는 내용은 여러분도 이미 친숙해지셨을 COleDataObjectIDataObject에 연결시키고 하나씩 열거하는 작업입니다.

 

STDMETHODIMP CSendToShlExt::DragEnter(IDataObject * pDataObj, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect) {
    AFX_MANAGE_STATE(AfxGetStaticModuleState());  // MFC 초기화
    
    COleDataObject dataobj;
    TCHAR          szItem[MAX_PATH];
    UINT           uNumFiles;
    HGLOBAL        hglobal;
    HDROP          hdrop;
     
    dataobj.Attach(pDataObj, FALSE);
    
    // 데이터 오브젝트로부터 리스트의 항목들을 하나씩 읽어옵니다.
    // 이들 항목은 HDROP 형태로 보관되어있기 때문에 HDROP 핸들을 가져와서 드래그 앤 드롭 API를 적용합니다.
    hglobal = dataobj.GetGlobalData(CF_HDROP);
    
    if (hdrop != NULL) {
        hdrop = (HDROP)GlobalLock(hglobal);
        
        uNumFiles = DragQueryFile(hdrop, 0xFFFFFFFF, NULL, 0);
        
        for (UINT uFile = 0; uFile < uNumFiles; uFile++) {
            if (DragQueryFile(hdrop, uFile, szItem, MAX_PATH) != 0)
            m_lsDroppedFiles.AddTail(szItem);
        }
        
        GlobalUnlock(hglobal);
    }
    
    // ...

 

이제 pdwEffect를 통해 값을 반환할 차례입니다. 우리가 반환할 수 있는 효과에는 다음과 같은 것들이 있습니다.

- DROPEFFECT_COPY: Windows 탐색기에게 드래그된 파일이 본 쉘 익스텐션에 의해 복사될 것임을 알려줍니다.

- DROPEFFECT_MOVE: Windows 탐색기에게 드래그된 파일이 본 쉘 익스텐션에 의해 이동될 것임을 알려줍니다.

- DROPEFFECT_LINK: Windows 탐색기에게 드래그된 파일이 본 쉘 익스텐션에 의해 링크될 것임을 알려줍니다.

- DROPEFFECT_NONE: Windows 탐색기에게 본 쉘 익스텐션은 드래그된 파일들을 받지 않을 것임을 알려줍니다.

 

우리가 반환하게 될 단 하나의 효과는 DROPEFFECT_COPY입니다.

본 쉘 익스텐션에서는 DROPEFFECT_MOVE를 반환하지 않겠습니다. 왜냐하면 이 효과는 ‘드롭’ 동작이 발생할 때 Windows 탐색기가 원래 있던 파일들을 삭제할 수 있기 때문입니다.

또한 DROPEFFECT_LINE를 반환할 수 있습니다. 그러나 마우스 포인터 모양이 바로가기 아이콘을 만들 때처럼 작은 화살표가 덧붙어서 나타나게 됩니다. 이 경우 사용자에게 오해를 불러일으킬 소지가 있습니다.

클립보드를 읽을 수 없어서 파일 리스트가 비어있다면, Windows 탐색기에게 ‘드롭’ 작업을 받지 않음을 알려주기 위하여 DROPEFFECT_NONE을 반환합니다.

 

    if (m_lsDroppedFiles.GetCount() > 0) {
        *pdwEffect = DROPEFFECT_COPY;
        return S_OK;
    } else {
        *pdwEffect = DROPEFFECT_NONE;
        return E_INVALIDARG;
    }
}

 

DragLeave

DragLeave는 사용자의 드래그 동작이 우리가 설정할 목표 아이콘을 벗어났을 경우에 호출됩니다. 이 메소드는 “보내기(Send To)” 메뉴에서는 사용되지 않습니다만, SendTo 폴더를 열고 해당 아이콘에 직접 드래그 동작이 가해질 경우에는 호출될 수 있습니다.

우리는 여기서 리소스 정리 같은 작업을 할 일이 없으므로 S_OK만 반환해줍니다.

 

STDMETHODIMP CSendToShlExt::DragLeave() {
    return S_OK;
}

 

Drop

사용자가 “보내기(Send To)” 메뉴 항목에서 우리가 설정할 항목을 클릭했을 때, Windows 탐색기는 Drop을 호출합니다. 이 메소드의 원형은 다음과 같습니다.

 

HRESULT IDropTarget::Drop(IDataObject * pDataObj, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);

 

첫 번째 매개 변수(parameter)는 DragEnter와 동일합니다.

Drop 메소드는 pdwEffect 파라미터를 통해 최종 효과를 반환해야 합니다.

우리가 구현할 Drop 메소드는 메인 다이얼로그 창을 생성하고 파일 이름들의 목록을 여기로 전달할 것입니다. 다이얼로그 안에서 관계된 모든 작업을 수행할 것이고, DoModal 메소드가 값을 반환할 때 우리는 ‘드롭’ 작업에 대한 최종 효과를 Windows 탐색기에게 반환할 것입니다.

 

STDMETHODIMP CSendToShlExt::Drop(IDataObject * pDataObj, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect) {
    AFX_MANAGE_STATE(AfxGetStaticModuleState());  // MFC 초기화
    
    CSendToCloneDlg dlg(&m_lsDroppedFiles);
    
    dlg.DoModal();
    
    *pdwEffect = DROPEFFECT_COPY;
    
    return S_OK;
}

 

다이얼로그는 다음과 같이 생겼습니다.

 

테마가 적용되지 않은 쉘 익스텐션 다이얼로그.

 

딱히 복잡하지 않은 MFC 다이얼로그입니다. 이에 대한 소스 코드는 SendToCloneDlg.cpp에서 보실 수 있습니다. 필자는 이미 “CShellFileOp - SHFileOperation의 래퍼 클래스”라는 게시글을 통해 CShellFileOp 클래스를 소개한 바 있고, 이를 사용하여 실제로 파일을 옮기거나 복사하였습니다.

2 단계에서 다루었던 쉘 익스텐션과 마찬가지로 우리는 Windows XP에서 실행되는 경우 테마 적용을 하기 위해 매니페스트(manifest)를 DLL에 리소스로서 추가할 필요가 있습니다. 그러나 이런 경우 매니페스트만을 추가하는 것만으로는 충분하지 않습니다. 왜냐하면 다이얼로그를 생성하고 관리하는 소스 코드는 MFC 내부에 있기 때문입니다.

MFC 자체가 ISOLATION_AWARE_ENABLED 심볼을 적용하여 컴파일되지 않은 관계로, 테마를 사용하기 위해 꼭 필요한 IsolationAwareXxx 래퍼를 사용할 수 없습니다.

이 상황을 잘 설명한 글로 Dave Anderson 님의 뉴스그룹 스레드가 있습니다. 이 글을 요약하자면, 우리가 다이얼로그를 보여주기 전에, Windows가 우리가 추가한 매니페스트를 사용하고 우리의 다이얼로그에 버전 6.0 공용 컨트롤을 적용할 수 있도록 우리는 activation context API를 사용해야 합니다. 이에 대한 코드는 CActCtx 클래스에 캡슐화되어 있고 Drop 메소드 안에서 사용됩니다.

 

STDMETHODIMP CSendToShlExt::Drop(...) {
    AFX_MANAGE_STATE(AfxGetStaticModuleState());  // MFC 초기화
    
    CSendToCloneDlg dlg(&m_lsDroppedFiles);
    CAxtCtx ctx;
    
    dlg.DoModal();
    *pdwEffect = DROPEFFECT_COPY;
    
    return S_OK;
}

 

이와 같이 코드를 수정하면 다이얼로그는 Windows XP 테마가 적용되어 표시될 것입니다.

 

 

잠깐! Windows 탐색기에게 우리가 만든 드롭 핸들러를 어떻게 알려줄 수 있을까요? 어떻게 하면 “보내기(Send To)” 메뉴에 새로운 항목을 추가시킬 수 있을까요? 다음 절에서 설명하겠습니다.

 

쉘 익스텐션을 등록하기

드롭 핸들러를 등록하는 것은 다른 쉘 익스텐션을 등록하는 것과는 다소 다릅니다. HKEY_CLASSES_ROOT의 하위 키로 ‘연결 프로그램’에 대한 내용을 생성해야 하기 때문입니다. AppWizard가 생성한 RGS 스크립트가 다음과 같이 있을 텐데, 표시된 부분은 여러분이 직접 추가하거나 수정할 부분을 뜻합니다.

 

HKCR {
    .SendToClone = s 'CLSID\{B7F3240E-0E29-11D4-8D3B-80CD3621FB09}' // 새로 추가
    NoRemove CLSID {
        ForceRemove {B7F3240E-0E29-11D4-8D3B-80CD3621FB09} = s 'Send To Any Folder Clone' { // 기본값 수정 가능
            InprocServer32 = s '%MODULE%' {
                val ThreadingModel = s 'Apartment'
            }
            
            val NeverShowExt = s '' // 새로 추가
            DefaultIcon = s '%MODULE%,0' // 새로 추가
            shellex { // 새로 추가
                DropHandler = s '{B7F3240E-0E29-11D4-8D3B-80CD3621FB09}' // 새로 추가
            }
        }
    }
}

 

첫 번째 줄이 ‘연결 프로그램’에 대한 것입니다.

이 줄은 새로운 확장명을 정의합니다. .SendToClone이라는 확장명은 우리가 만들 드롭 핸들러가 적용될 대상 아이콘을 만들기 위해서만 사용될 것입니다. .SendToClone 레지스트리 키의 기본 값이 CLSID\라는 접두어가 붙은 것을 주의하셔야 합니다. 이것은 Windows 탐색기에게 해당 연결 프로그램에 대한 내용이 HKCR\CLSID에 있음을 알려줍니다. 원래 이런 내용들은 보편적으로 쓰이는 확장명처럼 HKEY_CLASSES_ROOT 위치에 저장됩니다. 예를 들어 확장명 .txt에 대한 레지스트리 키는 다시 HKEY\txtfile를 가리킵니다. 그러나 모든 데이터를 한 곳에 몰아서 보관하기 위해서 드롭 핸들러에 대한 연결 프로그램 정보는 관습적으로 CLSID 레지스트리 키 위치에 보관되는 것으로 보입니다.

“Send To Any Folder Clone” 문자열은, 여러분이 SendTo 폴더를 열고 해당 확장명에 대한 0 바이트 파일을 선택했을 때, Windows 탐색기에서 파일 유형을 설명하기 위해 보여줄 문자열입니다.

NeverShowExt 값은 Windows 탐색기에게 .SendToClone의 확장명을 갖는 파일에 대해 그 확장명을 보여주어서는 안 된다고 알려주는 값입니다.

DefaultIcon 키는 .SendToClone 확장명을 갖는 파일에 대해 사용할 아이콘들의 위치를 담고 있습니다.

마지막으로 DropHandler라는 서브 키를 갖는 ShellEx 레지스트리 키가 있습니다. 해당 확장명을 갖는 파일에 대해서는 하나의 드롭 핸들러만이 존재할 것이기 때문에 DropHandler의 하위 키를 두고 그 위치에 GUID를 넣는 대신, DropHandler에 직접 GUID를 넣겠습니다.

이제 남은 것은 SendTo 폴더에 우리의 메뉴 항목이 나타나도록 파일을 생성하는 것입니다. 이 과정은 DllRegisterServer에서 수행할 수 있고, DllUnregisterServer를 통해 파일을 삭제할 수도 있습니다. 파일 생성의 예는 다음과 같습니다.

 

    // ...
    
    LPITEMIDLIST pidl;
    TCHAR        szSendtoPath[MAX_PATH];
    HANDLE       hFile;
    LPMALLOC     pMalloc;
    
    if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_SENDTO, &pidl))) {
        if (SHGetPathFromIDList(pidl, szSendtoPath)) {
            PathAppend(szSendtoPath, _T("Some other folder.SendToClone"));
            
            hFile = CreateFile(szSendtoPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
            CloseHandle(hFile);
        }
        
        if (SUCCEEDED(SHGetMalloc(&pMalloc))) {
            pMalloc->Free ( pidl );
            pMalloc->Release();
        }
    }
    
    // ...

 

“보내기(Send To)” 메뉴 항목은 다음과 같이 바뀌어 있습니다.

 

[단계 6]의 쉘 익스텐션 실행 결과.

 

DllUnregisterServer는 파일을 삭제하기 위해 유사한 코드가 작성되어 있습니다. 이 코드는 모든 버전의 Windows에서 작동됩니다(물론 4.0 이상의 버전입니다).

여러분이 작성할 쉘 익스텐션이 쉘 버전 4.71 이상의 버전에서만 작동될 것으로 알고 있는 상황이라면, SHGetSprcialFolderLocation을 사용하는 대신 SHGetSpecialFolderPath 함수를 사용할 수 있습니다.

Windows NT 기반의 운영체제에서는, “승인된(approved)” 익스텐션 목록에 본 쉘 익스텐션을 추가합니다. 이 작업은 예제 프로젝트의 DllRegisterServerDllUnregisterServer 함수에 적혀있습니다.

 

다음 단계에서 다룰 내용

다음 7 단계에서, 필자는 독자들의 질문에 답하고 새로운 두 가지 유형의 컨텍스트 메뉴 익스텐션에 대해 보여줄 것입니다.

 

계속 읽기

이전 게시글: Windows 쉘 익스텐션 개발 가이드 - (5) 속성 다이얼로그 (2/2)

다음 게시글: Windows 쉘 익스텐션 개발 가이드 - (7) 비트맵 및 폴더 메뉴 (1/3)

 

카테고리 “API/COM”
more...