코딩캣: 코딩하는 고양이.
Windows 쉘 익스텐션 개발 가이드 - (7) 비트맵 및 폴더 메뉴 (3/3)
API/COM
2021. 2. 10. 10:06

입문자를 위한 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)

 

두 번째 쉘 익스텐션: 디렉토리 창에서 마우스 오른쪽 버튼을 클릭하는 것을 처리하기

쉘 버전 4.71 및 그 이후 버전에서 여러분은 바탕화면 또는 파일 시스템의 디렉토리를 보여주는 아무 Windows 탐색기 창에서 마우스 오른쪽 클릭을 했을 때 나타나는 컨텍스트 메뉴를 수정할 수 있습니다. 이러한 쉘 익스텐션을 작성하는 방법은 여타 컨텍스트 메뉴 익스텐션을 작성하는 것과 비슷합니다. 다만 두 가지가 다릅니다.

 

- IShellExtInit::Initialize 메소드의 매개 변수(parameter)가 다르게 사용됩니다.

- 쉘 익스텐션이 다른 레지스트리 키에 등록됩니다.

 

필자는 이 글에서 본 쉘 익스텐션을 작성하는 모든 과정을 설명하지는 않겠습니다. 전체적인 과정은 첨부한 예제 프로젝트를 참고하시기 바랍니다.

 

IShellExtInit::Initialize에서 나타나는 차이

InitializepidlFolder 매개 변수(parameter)를 가지고 있는데, 지금까지 우리는 이를 무시하여 왔습니다. 왜냐하면 이것은 줄곧 NULL이었기 때문입니다. 드디어 이제부터 이 매개 변수(parameter)는 의미 있는 용도를 갖게 됩니다! 이 매개 변수(parameter)는 마우스 오른쪽 클릭이 발생한 디렉토리의 PIDL입니다. 두 번째 매개 변수(parameter)인 IDataObject 인터페이스형 포인터는 NULL이 됩니다. 왜냐하면 선택된 파일이 없기 때문입니다.

다음은 예제 프로젝트에서 Initialize 메소드의 구현을 가져온 것입니다.

 

STDMETHODIMP CBkgndCtxMenuExt::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDO, HKEY hkeyProgID) {
    // SHGetPathFromIDList API를 사용하여
    // PIDL로부터 일반적인 디렉토리 경로를 구합니다.
    return SHGetPathFromIDList(pidlFolder, m_szDirClickedIn) ? S_OK : E_INVALIDARG;
}

 

SHGetPathFromIDList 함수는 폴더의 PIDL을 받아서 문자열 형태의 전체 경로를 반환합니다. 이 경로는 나중에 사용하기 위해 멤버 변수에 보관해 둡니다. 함수의 성공 및 실패 여부에 따라 BOOL 값이 반환됩니다.

 

IShellExtInit::GetCommandString에서 나타나는 차이

Windows XP부터 Windows 탐색기는 GetCommandString에서 반환되는 동사(verb)의 이름을 검사합니다. 그리고 서로 일치하는 동사를 가진 메뉴 항목을 삭제합니다. 우리가 구현할 GetCommandString 메소드도 GCS_VERB를 포함하고 있는지 확인해야 합니다. 그리고 각 메뉴 항목에 따라 다른 동사(verb)를 반환해야 합니다.

 

STDMETHODIMP CBkgndCtxMenuExt::GetCommandString(UINT uCmd, UINT uFlags, UINT * puReserved, LPSTR pszName, UINT cchMax) {
    USES_CONVERSION;
    
    static LPCTSTR szItem0Verb = _T("CPBkgndExt0");
    static LPCTSTR szItem1Verb = _T("CPBkgndExt1");
    
    // 우리는 두 개의 메뉴 항목을 추가했으므로 idCmd가 0 또는 1이어야만 합니다.
    if ( uCmd > 1 )
        return E_INVALIDARG;
    
    // ‘플라이-바이(fly-by)’ 도움말 문자열 반환하는 내용은 생략합니다.
    
    // 새로 추가하고 있는 두 메뉴 항목 각각에 대한 동사(verb)를 반환합니다.
    if (uFlags == GCS_VERBA)
        lstrcpynA(pszName, T2CA((uCmd == 0) ? szItem0Verb : szItem1Verb), cchMax);
    else if (uFlags == GCS_VERBW)
    lstrcpynW((LPWSTR) pszName, T2CW((uCmd == 0) ? szItem0Verb : szItem1Verb), cchMax);
    
    return S_OK;
}

 

이러한 유형의 쉘 익스텐션은 HKCR\Directory\Background\ShellEx\ContextMenuHandlers에 등록됩니다. RGS 스크립트는 다음과 같이 구성합니다.

 

HKCR {
    NoRemove Directory {
        NoRemove Background {
            NoRemove ShellEx {
                NoRemove ContextMenuHandlers {
                    ForceRemove SimpleBkgndExtension = s '{9E5E1445-6CEA-4761-8E45-AA19F654571E}'
                }
            }
        }
    }
}

 

위와 같은 두 가지의 차이에도 불구하고, 쉘 익스텐션은 다른 컨텍스트 메뉴 익스텐션과 동일하게 작동합니다. 그렇지만 IContextMenu::QueryContextMenu에서 한 가지 알아둘 사항이 있습니다. Windows 98 및 Windows 2000에서 uIndex 매개 변수(parameter)는 항상 -1 다시 말하면 0xFFFFFFFF인 듯 합니다. InsertMenu에 인덱스 값으로 -1을 전달하는 것은 새로 추가될 메뉴 항목이 컨텍스트 메뉴의 최하단에 추가됨을 뜻합니다. 그러나 여러분이 uIndex의 값을 1 증가시켜서 영(0)으로 만든다면, InsertMenu0이 된 uIndex를 전달하게 되는데, 이는 컨텍스트 메뉴의 최상단에 새로운 메뉴 항목이 추가됨을 의미합니다. 예제 프로젝트의 QueryContextMenu를 참고하셔서 어떻게 메뉴 항목이 적절한 위치에 추가되는지를 확인하시기 바랍니다.

수정된 컨텍스트 메뉴는 다음과 같이 생겼습니다. 두 개의 메뉴 항목이 컨텍스트 메뉴 하단에 추가되었습니다. 필자의 짧은 식견으로 보았을 때, 이와 같은 방식으로 컨텍스트 메뉴의 끝 부분에 메뉴 항목들을 추가하는 것은 사용자 편의성에 중대한 문제가 있을 것입니다.

사용자가 [등록 정보] 또는 [속성] 항목을 선택하고자 할 때, 습관적으로 마우스 오른쪽 클릭을 하여 맨 마지막 메뉴 항목을 클릭합니다. 그런데 우리가 추가하고자 하는 메뉴 항목이 [등록 정보] 또는 [속성] 항목보다 더 아래에 있게 되면, 사용자들이 익숙하게 써오던 쉘 사용을 방해하는 것이 되고, 사용자들은 ‘깊은 빡침’을 느끼며 항의성 이메일을 보내올 수 있습니다. ^^;;

 

컨텍스트 메뉴의 최하단에 항목을 추가하는 행위는 사용자의 편의성을 해칠 수 있다.

 

다음 단계에서 다룰 내용

다음 8 단계에서는 Windows 탐색기에서 “자세히” 보기 모드를 선택하였을 때 나타나는 열(column)을 사용자화할 수 있는 컬럼 핸들러 익스텐션(column handler extension)에 대해 다루어 보겠습니다.

 

계속 읽기

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

다음 게시글: Windows 쉘 익스텐션 개발 가이드 - (8) 자세히 모드 (1/2)

 

'API/COM' 카테고리의 다른 글
더 보기...
태그 : 
댓글