동적 동사를 사용하여 바로 가기 메뉴 사용자 지정
바로 가기 메뉴 처리기는 상황에 맞는 메뉴 처리기 또는 동사 처리기라고도 합니다. 바로 가기 메뉴 처리기는 파일 형식 처리기의 형식입니다.
이 항목은 다음과 같이 구성됩니다.
- 정적 동사 및 동적 동사 정보
- 바로 가기 메뉴 처리기가 동적 동사에서 작동하는 방법
- 정규화되지 않은 동사 이름으로 인한 충돌 방지
- 동적 동사를 사용하여 바로 가기 메뉴 처리기 등록
- IContextMenu 인터페이스 구현
- 관련 항목
정적 동사 및 동적 동사 정보
정적 동사 메서드 중 하나를 사용하여 바로 가기 메뉴를 구현하는 것이 좋습니다. 상황에 맞는 메뉴 처리기 만들기의 "정적 동사를 사용하여 바로 가기 메뉴 사용자 지정" 섹션에 제공된 지침을 따르는 것이 좋습니다. Windows 7 이상에서 정적 동사에 대한 동적 동작을 가져오려면 상황에 맞는 메뉴 처리기 만들기에서 "정적 동사에 대한 동적 동작 가져오기"를 참조하세요. 정적 동사 구현 및 방지할 동적 동사에 대한 자세한 내용은 바로 가기 메뉴에 대한 정적 또는 동적 동사 선택을 참조 하세요.
파일 형식에 대한 동적 동사를 등록하여 파일 형식의 바로 가기 메뉴를 확장해야 하는 경우 이 항목의 뒷부분에 제공된 지침을 따릅니다.
참고 항목
32비트 애플리케이션의 컨텍스트에서 작동하는 처리기를 등록할 때 64비트 Windows에 대한 특별한 고려 사항이 있습니다. 32비트 애플리케이션의 컨텍스트에서 셸 동사가 호출될 때 WOW64 하위 시스템은 파일 시스템 액세스를 일부 경로로 리디렉션합니다. .exe 처리기가 해당 경로 중 하나에 저장되면 이 컨텍스트에서 액세스할 수 없습니다. 따라서 해결 방법으로 리디렉션되지 않는 경로에 .exe를 저장하거나 실제 버전을 시작하는 .exe의 스텁 버전을 저장합니다.
바로 가기 메뉴 처리기가 동적 동사에서 작동하는 방법
IUnknown 외에도 바로 가기 메뉴 처리기는 소유자 그리기 메뉴 항목을 구현하는 데 필요한 메시징을 처리하기 위해 다음 추가 인터페이스를 내보냅니다.
- IShellExtInit (필수)
- IContextMenu (필수)
- IContextMenu2 (선택 사항)
- IContextMenu3 (선택 사항)
소유자가 그린 메뉴 항목에 대한 자세한 내용은 메뉴 사용의 소유자 그리기 메뉴 항목 만들기 섹션을 참조하세요.
셸은 IShellExtInit 인터페이스를 사용하여 처리기를 초기화합니다. 셸에서 IShellExtInit::Initialize를 호출하면 개체 이름과 파일이 포함된 폴더의 PIDL(항목 식별자 목록)에 대한 포인터가 있는 데이터 개체를 전달합니다. hkeyProgID 매개 변수는 바로 가기 메뉴 핸들이 등록된 레지스트리 위치입니다. IShellExtInit::Initialize 메서드는 데이터 개체에서 파일 이름을 추출하고 나중에 사용할 수 있도록 이름과 폴더의 포인터를 PIDL(항목 식별자 목록)에 저장해야 합니다. 처리기 초기화에 대한 자세한 내용은 IShellExtInit 구현을 참조하세요.
바로 가기 메뉴에 동사가 표시되면 먼저 검색된 다음 사용자에게 표시되고 마지막으로 호출됩니다. 다음 목록에서는 다음 세 단계를 자세히 설명합니다.
- 셸은 항목 또는 시스템의 상태를 기반으로 할 수 있는 동사 집합을 반환하는 IContextMenu::QueryContextMenu를 호출합니다.
- 시스템은 메서드가 바로 가기 메뉴에 항목을 추가하는 데 사용할 수 있는 HMENU 핸들을 전달합니다.
- 사용자가 처리기의 항목 중 하나를 클릭하면 셸에서 IContextMenu::InvokeCommand를 호출합니다. 그런 다음 처리기는 적절한 명령을 실행할 수 있습니다.
정규화되지 않은 동사 이름으로 인한 충돌 방지
동사는 형식별로 등록되므로 다른 항목의 동사에 동일한 동사 이름을 사용할 수 있습니다. 이렇게 하면 애플리케이션이 항목 형식과 무관하게 공통 동사를 참조할 수 있습니다. 이 기능은 유용하지만 정규화되지 않은 이름을 사용하면 동일한 동사 이름을 선택하는 여러 ISV(독립 소프트웨어 공급업체)와 충돌할 수 있습니다. 이를 방지하려면 항상 다음과 같이 ISV 이름의 접두사 동사를 접두사로 지정합니다.
ISV_Name.verb
항상 애플리케이션별 ProgID를 사용합니다. 파일 이름 확장명을 ISV 제공 ProgID에 매핑하는 규칙을 채택하면 잠재적인 충돌을 방지할 수 있습니다. 그러나 일부 항목 유형은 이 매핑을 사용하지 않으므로 공급업체 고유 이름이 필요합니다. 해당 동사가 이미 등록되어 있을 수 있는 기존 ProgID에 동사를 추가할 때는 먼저 이전 동사의 레지스트리 키를 제거한 후 고유한 동사를 추가해야 합니다. 두 동사에서 동사 정보를 병합하지 않도록 해야 합니다. 이렇게 하지 않으면 예측할 수 없는 동작이 발생합니다.
동적 동사를 사용하여 바로 가기 메뉴 처리기 등록
바로 가기 메뉴 처리기는 파일 형식 또는 폴더와 연결됩니다. 파일 형식의 경우 처리기는 다음 하위 키 아래에 등록됩니다.
HKEY_CLASSES_ROOT
Program ID
shellex
ContextMenuHandlers
바로 가기 메뉴 처리기를 파일 형식 또는 폴더와 연결하려면 먼저 ContextMenuHandlers 하위 키 아래에 하위 키를 만듭니다. 처리기의 하위 키 이름을 지정하고 하위 키의 기본값을 CLSID(처리기 클래스 식별자) GUID의 문자열 형식으로 설정합니다.
그런 다음, 바로 가기 메뉴 처리기를 다른 종류의 폴더와 연결하려면 다음 예제와 같이 FolderType 하위 키에서 파일 형식과 동일한 방식으로 처리기를 등록합니다.
HKEY_CLASSES_ROOT
FolderType
shellex
ContextMenuHandlers
처리기를 등록할 수 있는 폴더 유형에 대한 자세한 내용은 셸 확장 처리기 등록을 참조 하세요.
파일 형식에 연결된 바로 가기 메뉴가 있는 경우 개체를 두 번 클릭하면 일반적으로 기본 명령이 시작되고 처리기의 IContextMenu::QueryContextMenu 메서드가 호출되지 않습니다. 개체를 두 번 클릭할 때 처리기의 IContextMenu::QueryContextMenu 메서드를 호출하도록 지정하려면 다음과 같이 처리기의 CLSID 하위 키 아래에 하위 키를 만듭니다.
HKEY_CLASSES_ROOT
CLSID
{00000000-1111-2222-3333-444444444444}
shellex
MayChangeDefaultMenu
처리기와 연결된 개체를 두 번 클릭하면 uFlags 매개 변수에 설정된 CMF_DEFAULTONLY 플래그를 사용하여 IContextMenu::QueryContextMenu가 호출됩니다.
바로 가기 메뉴 처리기는 바로 가기 메뉴의 기본 동사를 변경해야 하는 경우에만 MayChangeDefaultMenu 하위 키를 설정해야 합니다. 이 하위 키를 설정하면 연결된 항목을 두 번 클릭하면 시스템에서 처리기의 DLL을 로드합니다. 처리기가 기본 동사를 변경하지 않으면 시스템이 불필요하게 DLL을 로드하므로 이 하위 키를 설정하면 안 됩니다.
다음 예제에서는 .myp 파일 형식에 대한 바로 가기 메뉴 처리기를 사용하도록 설정하는 레지스트리 항목을 보여 줍니다. 처리기의 CLSID 하위 키에는 사용자가 관련 개체를 두 번 클릭할 때 처리기가 호출되도록 하는 MayChangeDefaultMenu 하위 키가 포함됩니다.
HKEY_CLASSES_ROOT
.myp
(Default) = MyProgram.1
CLSID
{00000000-1111-2222-3333-444444444444}
InProcServer32
(Default) = C:\MyDir\MyCommand.dll
ThreadingModel = Apartment
shellex
MayChangeDefaultMenu
MyProgram.1
(Default) = MyProgram Application
shellex
ContextMenuHandler
MyCommand = {00000000-1111-2222-3333-444444444444}
IContextMenu 인터페이스 구현
IContextMenu 는 가장 강력하지만 구현하기 가장 복잡한 방법입니다. 정적 동사 메서드 중 하나를 사용하여 동사를 구현하는 것이 좋습니다. 자세한 내용은 바로 가기 메뉴에 대한 정적 또는 동적 동사 선택을 참조 하세요. IContextMenu에는 GetCommandString, InvokeCommand 및 QueryContextMenu의 세 가지 메서드가 있으며 여기서 자세히 설명합니다.
IContextMenu::GetCommandString 메서드
처리기의 IContextMenu::GetCommandString 메서드는 동사의 정식 이름을 반환하는 데 사용됩니다. 이 메서드는 선택 사항입니다. Windows XP 및 이전 버전의 Windows에서 Windows 탐색기에 상태 표시줄이 있는 경우 이 메서드는 메뉴 항목의 상태 표시줄에 표시되는 도움말 텍스트를 검색하는 데 사용됩니다.
idCmd 매개 변수는 IContextMenu::QueryContextMenu가 호출될 때 정의된 명령의 식별자 오프셋을 보유합니다. 도움말 문자열이 요청되면 uFlags가 GCS_HELPTEXTW 설정됩니다. 도움말 문자열을 pszName 버퍼에 복사하여 PWSTR로 캐스팅합니다. 동사 문자열은 uFlags를 GCS_VERBW 설정하여 요청됩니다. 도움말 문자열 과 마찬가지로 pszName에 적절한 문자열을 복사합니다. GCS_VALIDATEA 및 GCS_VALIDATEW 플래그는 바로 가기 메뉴 처리기에서 사용되지 않습니다.
다음 예제에서는 이 항목의 IContextMenu::QueryContextMenu 메서드 섹션에 제공된 IContextMenu::QueryContextMenu 예제에 해당하는 IContextMenu::GetCommandString의 간단한 구현을 보여 줍니다. 처리기는 메뉴 항목을 하나만 추가하므로 반환할 수 있는 문자열 집합이 하나만 있습니다. 이 메서드는 idCmd가 유효한지 여부를 테스트하고, 유효한 경우 요청된 문자열을 반환합니다.
StringCchCopy 함수는 pszName에 요청된 문자열을 복사하여 복사된 문자열이 cchName에 지정된 버퍼의 크기를 초과하지 않도록 하는 데 사용됩니다. 이 예제에서는 Windows 2000 이후 Windows 탐색기에서만 사용되었기 때문에 uFlags의 유니코드 값에 대한 지원만 구현합니다.
IFACEMETHODIMP CMenuExtension::GetCommandString(UINT idCommand,
UINT uFlags,
UINT *pReserved,
PSTR pszName,
UINT cchName)
{
HRESULT hr = E_INVALIDARG;
if (idCommand == IDM_DISPLAY)
{
switch (uFlags)
{
case GCS_HELPTEXTW:
// Only useful for pre-Vista versions of Windows that
// have a Status bar.
hr = StringCchCopyW(reinterpret_cast<PWSTR>(pszName),
cchName,
L"Display File Name");
break;
case GCS_VERBW:
// GCS_VERBW is an optional feature that enables a caller
// to discover the canonical name for the verb passed in
// through idCommand.
hr = StringCchCopyW(reinterpret_cast<PWSTR>(pszName),
cchName,
L"DisplayFileName");
break;
}
}
return hr;
}
IContextMenu::InvokeCommand 메서드
이 메서드는 사용자가 메뉴 항목을 클릭하여 처리기에 연결된 명령을 실행하도록 지시할 때 호출됩니다. pici 매개 변수는 필요한 정보가 포함된 구조를 가리킵니다.
pici는 Shlobj.h에서 CMINVOKECOMMANDINFO 구조체로 선언되지만 실제로는 CMINVOKECOMMANDINFOEX 구조를 가리키는 경우가 많습니다. 이 구조체는 CMINVOKECOMMANDINFO의 확장 버전이며 유니코드 문자열을 전달할 수 있도록 하는 몇 가지 추가 멤버가 있습니다.
pici의 cbSize 멤버를 확인하여 전달된 구조를 확인합니다. CMINVOKECOMMANDINFOEX 구조이고 fMask 멤버에 CMIC_MASK_UNICODE 플래그 집합이 있는 경우 pici를 CMINVOKECOMMANDINFOEX로 캐스팅합니다. 이렇게 하면 애플리케이션에서 구조체의 마지막 5개 멤버에 포함된 유니코드 정보를 사용할 수 있습니다.
구조체의 lpVerb 또는 lpVerbW 멤버는 실행할 명령을 식별하는 데 사용됩니다. 명령은 다음 두 가지 방법 중 하나로 식별됩니다.
- 명령의 동사 문자열로
- 명령의 식별자 오프셋 기준
이러한 두 사례를 구분하려면 ANSI 케이스의 경우 lpVerb의 상위 단어 또는 유니코드 케이스의 경우 lpVerbW를 검사. 상위 단어가 0 이 아닌 경우 lpVerb 또는 lpVerbW 는 동사 문자열을 보유합니다. 상위 단어가 0이면 명령 오프셋이 lpVerb의 하위 단어에 있습니다.
다음 예제에서는 이 섹션 전후에 제공된 IContextMenu::QueryContextMenu 및 IContextMenu::GetCommandString 예제에 해당하는 IContextMenu::InvokeCommand의 간단한 구현을 보여 줍니다. 메서드는 먼저 전달되는 구조를 결정합니다. 그런 다음 명령이 오프셋 또는 동사로 식별되는지 여부를 결정합니다. lpVerb 또는 lpVerbW에 유효한 동사 또는 오프셋이 있으면 메서드에 메시지 상자가 표시됩니다.
STDMETHODIMP CShellExtension::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
BOOL fEx = FALSE;
BOOL fUnicode = FALSE;
if(lpcmi->cbSize == sizeof(CMINVOKECOMMANDINFOEX))
{
fEx = TRUE;
if((lpcmi->fMask & CMIC_MASK_UNICODE))
{
fUnicode = TRUE;
}
}
if( !fUnicode && HIWORD(lpcmi->lpVerb))
{
if(StrCmpIA(lpcmi->lpVerb, m_pszVerb))
{
return E_FAIL;
}
}
else if( fUnicode && HIWORD(((CMINVOKECOMMANDINFOEX *) lpcmi)->lpVerbW))
{
if(StrCmpIW(((CMINVOKECOMMANDINFOEX *)lpcmi)->lpVerbW, m_pwszVerb))
{
return E_FAIL;
}
}
else if(LOWORD(lpcmi->lpVerb) != IDM_DISPLAY)
{
return E_FAIL;
}
else
{
MessageBox(lpcmi->hwnd,
"The File Name",
"File Name",
MB_OK|MB_ICONINFORMATION);
}
return S_OK;
}
IContextMenu::QueryContextMenu 메서드
셸은 IContextMenu::QueryContextMenu를 호출하여 바로 가기 메뉴 처리기가 메뉴에 해당 메뉴 항목을 추가할 수 있도록 합니다. hmenu 매개 변수의 HMENU 핸들을 전달합니다. indexMenu 매개 변수는 추가할 첫 번째 메뉴 항목에 사용할 인덱스로 설정됩니다.
처리기에서 추가되는 모든 메뉴 항목에는 idCmdFirst 및 idCmdLast 매개 변수의 값 사이에 속하는 식별자가 있어야 합니다. 일반적으로 첫 번째 명령 식별자는 idCmdFirst로 설정되며 각 추가 명령에 대해 1씩 증가합니다. 이 방법을 사용하면 idCmdLast를 초과하지 않고 Shell에서 둘 이상의 처리기를 호출하는 경우 사용 가능한 식별자 수를 최대화할 수 있습니다.
항목 식별자의 명령 오프셋은 idCmdFirst의 식별자와 값 간의 차이입니다. 셸에서 IContextMenu::GetCommandString 또는 IContextMenu::InvokeCommand를 호출하는 경우 항목을 식별하는 데 사용할 수 있으므로 처리기가 바로 가기 메뉴에 추가하는 각 항목의 오프셋을 저장합니다.
또한 추가하는 각 명령에 동사를 할당해야 합니다. 동사는 IContextMenu::InvokeCommand가 호출될 때 오프셋 대신 명령을 식별하는 데 사용할 수 있는 문자열입니다. 또한 ShellExecuteEx와 같은 함수에서 바로 가기 메뉴 명령을 실행하는 데도 사용됩니다.
바로 가기 메뉴 처리기와 관련된 uFlags 매개 변수를 통해 전달할 수 있는 세 가지 플래그가 있습니다. 이 내용은 다음 표에 설명되어 있습니다.
플래그 | 설명 |
---|---|
CMF_DEFAULTONLY | 사용자는 일반적으로 개체를 두 번 클릭하여 기본 명령을 선택했습니다. IContextMenu::QueryContextMenu 는 메뉴를 수정하지 않고 셸로 컨트롤을 반환해야 합니다. |
CMF_NODEFAULT | 메뉴의 항목이 기본 항목이 아니어야 합니다. 메서드는 메뉴에 해당 명령을 추가해야 합니다. |
CMF_NORMAL | 바로 가기 메뉴가 정상적으로 표시됩니다. 메서드는 메뉴에 해당 명령을 추가해야 합니다. |
InsertMenu 또는 InsertMenuItem을 사용하여 메뉴 항목을 목록에 추가합니다. 그런 다음 심각도가 SEVERITY_SUCCESS 설정된 HRESULT 값을 반환합니다. 코드 값을 할당된 가장 큰 명령 식별자의 오프셋과 하나(1)로 설정합니다. 예를 들어 idCmdFirst가 5로 설정되어 있고 명령 식별자가 5, 7 및 8인 세 개의 항목을 메뉴에 추가한다고 가정합니다. 반환 값은 .이어야 MAKE_HRESULT(SEVERITY_SUCCESS, 0, 8 - 5 + 1)
합니다.
다음 예제에서는 단일 명령을 삽입하는 IContextMenu::QueryContextMenu의 간단한 구현을 보여 줍니다. 명령에 대한 식별자 오프셋은 0으로 설정된 IDM_DISPLAY. m_pszVerb 및 m_pwszVerb 변수는 연결된 언어 독립적 동사 문자열을 ANSI 및 유니코드 형식으로 저장하는 데 사용되는 프라이빗 변수입니다.
#define IDM_DISPLAY 0
STDMETHODIMP CMenuExtension::QueryContextMenu(HMENU hMenu,
UINT indexMenu,
UINT idCmdFirst,
UINT idCmdLast,
UINT uFlags)
{
HRESULT hr;
if(!(CMF_DEFAULTONLY & uFlags))
{
InsertMenu(hMenu,
indexMenu,
MF_STRING | MF_BYPOSITION,
idCmdFirst + IDM_DISPLAY,
"&Display File Name");
hr = StringCbCopyA(m_pszVerb, sizeof(m_pszVerb), "display");
hr = StringCbCopyW(m_pwszVerb, sizeof(m_pwszVerb), L"display");
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_DISPLAY + 1));
}
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
}
다른 동사 구현 작업은 상황에 맞는 메뉴 처리기 만들기를 참조 하세요.
관련 항목