Compartilhar via


Personalizar um Menu de Atalho usando Verbos Dinâmicos

Os manipuladores de menu de atalho também são conhecidos como manipuladores de menu de contexto ou manipuladores de verbos. Um manipulador de menu de atalho é um tipo de manipulador de tipo de arquivo.

Este tópico é organizado da seguinte maneira:

Sobre Verbos Estáticos e Dinâmicos

Recomendamos fortemente a implementação de um menu de atalho usando um dos métodos de verbos estáticos. Recomendamos que você siga as instruções fornecidas na seção "Personalizar um Menu de Atalho usando Verbos Estáticos" de Criar Manipuladores de Menu de Contexto. Para obter comportamento dinâmico para verbos estáticos no Windows 7 e posterior, consulte "Obtendo comportamento dinâmico para verbos estáticos" em Criar Manipuladores de Menu de Contexto. Para obter detalhes sobre a implementação de verbos estáticos e quais verbos dinâmicos evitar, confira Escolher um Verbo Estático ou Dinâmico para o Menu de Atalho.

Se você precisar estender o menu de atalho para um tipo de arquivo registrando um verbo dinâmico para o tipo de arquivo, siga as instruções fornecidas posteriormente neste tópico.

Observação

Há considerações especiais para o Windows de 64 bits ao registrar manipuladores que funcionam no contexto de aplicativos de 32 bits: quando verbos do Shell são invocados no contexto de um aplicativo de 32 bits, o subsistema WOW64 redireciona o acesso ao sistema de arquivos para alguns caminhos. Se o manipulador de .exe estiver armazenado em um desses caminhos, ele não estará acessível nesse contexto. Portanto, como solução alternativa, armazene seus .exe em um caminho que não seja redirecionado ou armazene uma versão stub do .exe que inicie a versão real.

 

Como os Manipuladores de Menu de Atalho Funcionam com Verbos Dinâmicos

Além do IUnknown, os manipuladores de menu de atalho exportam as seguintes interfaces adicionais para lidar com as mensagens necessárias para implementar itens de menu desenhados pelo proprietário:

Para obter mais informações sobre itens de menu desenhados pelo proprietário, confira a seção Criar Itens de Menu Desenhados pelo Proprietário em Usar Menus.

O Shell usa a interface IShellExtInit para inicializar o manipulador. Quando o Shell chama IShellExtInit::Initialize, ele passa um objeto de dados com o nome do objeto e um ponteiro para uma lista de identificadores de item (PIDL) da pasta que contém o arquivo. O parâmetro hkeyProgID é o local do registro sob o qual o identificador de menu de atalho está registrado. O método IShellExtInit::Initialize deve extrair o nome do arquivo do objeto de dados e armazenar o nome e o ponteiro da pasta para uma lista de identificadores de item (PIDL) para uso posterior. Para obter mais informações sobre a inicialização do manipulador, confira Implementar o IShellExtInit.

Quando os verbos são apresentados em um menu de atalho, eles são primeiro descobertos, depois apresentados ao usuário e, por fim, invocados. A lista a seguir descreve essas três etapas em mais detalhes:

  1. O Shell chama IContextMenu::QueryContextMenu, que retorna um conjunto de verbos que podem ser baseados no estado dos itens ou do sistema.
  2. O sistema passa um identificador HMENU que o método pode usar para adicionar itens ao menu de atalho.
  3. Se o usuário clicar em um dos itens do manipulador, o Shell chamará IContextMenu::InvokeCommand. Em seguida, o manipulador pode executar o comando apropriado.

Evitar Colisões Devido a Nomes Verbais Não Qualificados

Como os verbos são registrados por tipo, o mesmo nome de verbo pode ser usado para verbos em itens diferentes. Isso permite que os aplicativos se refiram a verbos comuns independentemente do tipo de item. Embora esse recurso seja útil, o uso de nomes não qualificados pode resultar em colisões com vários ISVs (fornecedores independentes de software) que escolhem o mesmo nome de verbo. Para evitar isso, sempre prefixe verbos com o nome ISV da seguinte maneira:

ISV_Name.verb

Sempre use um ProgID específico do aplicativo. A adoção da convenção de mapear a extensão de nome de arquivo para um ISV desde que o ProgID evite possíveis colisões. No entanto, como alguns tipos de item não usam esse mapeamento, há a necessidade de nomes exclusivos do fornecedor. Ao adicionar um verbo a um ProgID existente que já pode ter esse verbo registrado, primeiro remova a chave do Registro para o verbo antigo antes de adicionar seu próprio verbo. Faça isso para evitar mesclar as informações verbais dos dois verbos. A falha ao fazer isso resulta em um comportamento imprevisível.

Registrar um Manipulador de Menu de Atalho com um Verbo Dinâmico

Os manipuladores do menu de atalho são associados a um tipo de arquivo ou a uma pasta. Para tipos de arquivo, o manipulador é registrado sob a seguinte subchave.

HKEY_CLASSES_ROOT
   Program ID
      shellex
         ContextMenuHandlers

Para associar um manipulador de menu de atalho a um tipo de arquivo ou a uma pasta, primeiro crie uma subchave na subchave ContextMenuHandlers. Nomeie a subchave para o manipulador e defina o valor padrão da subchave para a forma de cadeia de caracteres do GUID do CLSID (identificador de classe) do manipulador.

Em seguida, para associar um manipulador de menu de atalho a diferentes tipos de pastas, registre o manipulador da mesma maneira que faria para um tipo de arquivo, mas na subchave FolderType, conforme mostrado no exemplo a seguir.

HKEY_CLASSES_ROOT
   FolderType
      shellex
         ContextMenuHandlers

Para obter mais informações sobre para quais tipos de pasta você pode registrar manipuladores, confira Registrar Manipuladores de Extensão do Shell.

Se um tipo de arquivo tiver um menu de atalho associado a ele, clicar duas vezes em um objeto normalmente inicia o comando padrão e o método IContextMenu::QueryContextMenu do manipulador não é chamado. Para especificar que o método IContextMenu::QueryContextMenu do manipulador deve ser chamado quando um objeto é clicado duas vezes, crie uma subchave sob a subchave CLSID do manipulador, conforme mostrado aqui.

HKEY_CLASSES_ROOT
   CLSID
      {00000000-1111-2222-3333-444444444444}
         shellex
            MayChangeDefaultMenu

Quando um objeto associado ao manipulador é clicado duas vezes, IContextMenu::QueryContextMenu é chamado com o sinalizador CMF_DEFAULTONLY definido no parâmetro uFlags.

Os manipuladores de menu de atalho devem definir a subchave MayChangeDefaultMenu somente se precisarem alterar o verbo padrão do menu de atalho. A definição dessa subchave força o sistema a carregar a DLL do manipulador quando um item associado é clicado duas vezes. Se o manipulador não alterar o verbo padrão, você não deve definir essa subchave porque isso faz com que o sistema carregue a DLL desnecessariamente.

O exemplo a seguir ilustra entradas do registro que habilitam um manipulador de menu de atalho para um tipo de arquivo .myp. A subchave CLSID do manipulador inclui uma subchave MayChangeDefaultMenu para garantir que o manipulador seja chamado quando o usuário clicar duas vezes em um objeto relacionado.

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}

Implementar a Interface IContextMenu

IContextMenu é o método mais poderoso, mas também o mais complicado de implementar. É altamente recomendável que você implemente um verbo usando um dos métodos de verbo estático. Para obter mais informações, confira Escolher um Verbo Estático ou Dinâmico para o Menu de Atalho. IContextMenu tem três métodos, GetCommandString, InvokeCommand e QueryContextMenu, que são discutidos aqui em detalhes.

Método IContextMenu::GetCommandString

O método IContextMenu::GetCommandString do manipulador é usado para retornar o nome canônico de um verbo. Esse método é opcional. No Windows XP e em versões anteriores do Windows, quando o Windows Explorer tem uma barra de status, esse método é usado para recuperar o texto de ajuda exibido na barra de status de um item de menu.

O parâmetro idCmd contém o deslocamento do identificador do comando que foi definido quando IContextMenu::QueryContextMenu foi chamado. Se uma cadeia de caracteres de ajuda for solicitada, uFlags será definido como GCS_HELPTEXTW. Copie a cadeia de caracteres de ajuda para o buffer pszName, convertendo-a em um PWSTR. A cadeia de caracteres do verbo é solicitada definindo uFlags como GCS_VERBW. Copie a cadeia de caracteres apropriada para pszName, assim como com a cadeia de caracteres de ajuda. Os sinalizadores GCS_VALIDATEA e GCS_VALIDATEW não são usados por manipuladores de menu de atalho.

O exemplo a seguir mostra uma implementação simples de IContextMenu::GetCommandString que corresponde ao exemplo IContextMenu::QueryContextMenu fornecido na seção Método IContextMenu::QueryContextMenu deste tópico. Como o manipulador adiciona apenas um item de menu, há apenas um conjunto de cadeias de caracteres que pode ser retornado. O método testa se idCmd é válido e, se for, retorna a cadeia de caracteres solicitada.

A função StringCchCopy é usada para copiar a cadeia de caracteres solicitada para pszName para garantir que a cadeia de caracteres copiada não exceda o tamanho do buffer especificado por cchName. Este exemplo só implementa suporte para os valores Unicode de uFlags, porque somente eles têm sido usados no Windows Explorer desde o Windows 2000.

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;
}

Método IContextMenu::InvokeCommand

Esse método é chamado quando um usuário clica em um item de menu para dizer ao manipulador para executar o comando associado. O parâmetro pici aponta para uma estrutura que contém as informações necessárias.

Embora pici seja declarado em Shlobj.h como uma estrutura CMINVOKECOMMANDINFO, na prática ele geralmente aponta para uma estrutura CMINVOKECOMMANDINFOEX. Essa estrutura é uma versão estendida do CMINVOKECOMMANDINFO e tem vários membros adicionais que tornam possível passar cadeias de caracteres Unicode.

Verifique o membro cbSize do pici para determinar qual estrutura foi passada. Se for uma estrutura CMINVOKECOMMANDINFOEX e o membro fMask tiver o sinalizador CMIC_MASK_UNICODE definido, converta pici para CMINVOKECOMMANDINFOEX. Isso permite que seu aplicativo use as informações Unicode contidas nos últimos cinco membros da estrutura.

O membro lpVerb ou lpVerbW da estrutura é usado para identificar o comando a ser executado. Os comandos são identificados de uma das duas maneiras a seguir:

  • Pela cadeia de caracteres verbais do comando
  • Pelo deslocamento do identificador do comando

Para distinguir entre esses dois casos, verifique a palavra de ordem alta de lpVerb para o caso ANSI ou lpVerbW para o caso Unicode. Se a palavra de ordem alta for diferente de zero, lpVerb ou lpVerbW manterá uma cadeia de caracteres verbal. Se a palavra de ordem alta for zero, o deslocamento do comando estará na palavra de ordem baixa de lpVerb.

O exemplo a seguir mostra uma implementação simples de IContextMenu::InvokeCommand corresponde aos exemplos IContextMenu::QueryContextMenu e IContextMenu::GetCommandString fornecidos antes e depois desta seção. O método primeiro determina qual estrutura está sendo passada. Em seguida, ele determina se o comando é identificado por seu deslocamento ou seu verbo. Se lpVerb ou lpVerbW contiver um verbo ou deslocamento válido, o método exibirá uma caixa de mensagem.

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;
}

Método IContextMenu::QueryContextMenu

O Shell chama IContextMenu::QueryContextMenu para habilitar o manipulador de menu de atalho para adicionar seus itens de menu ao menu. Ele passa o identificador HMENU no parâmetro hmenu. O parâmetro indexMenu é definido como o índice a ser usado para o primeiro item de menu a ser adicionado.

Todos os itens de menu adicionados pelo manipulador devem ter identificadores que estejam entre os valores nos parâmetros idCmdFirst e idCmdLast. Normalmente, o primeiro identificador de comando é definido como idCmdFirst, que é incrementado em um (1) para cada comando adicional. Essa prática ajuda a evitar exceder idCmdLast e maximiza o número de identificadores disponíveis caso o Shell chame mais de um manipulador.

O deslocamento de comando de um identificador de item é a diferença entre o identificador e o valor em idCmdFirst. Armazene o deslocamento de cada item que seu manipulador adiciona ao menu de atalho porque o Shell pode usá-lo para identificar o item se ele chamar subsequentemente IContextMenu::GetCommandString ou IContextMenu::InvokeCommand.

Você também deve atribuir um verbo a cada comando adicionado. Um verbo é uma cadeia de caracteres que pode ser usada em vez do deslocamento para identificar o comando quando IContextMenu::InvokeCommand é chamado. Ele também é usado por funções como ShellExecuteEx para executar comandos de menu de atalho.

Há três sinalizadores que podem ser passados através do parâmetro uFlags que são relevantes para manipuladores de menu de atalho. Eles são descritos na tabela a seguir.

Sinalizador Descrição
CMF_DEFAULTONLY O usuário selecionou o comando padrão, geralmente clicando duas vezes no objeto. IContextMenu::QueryContextMenu deve retornar o controle para o Shell sem modificar o menu.
CMF_NODEFAULT Nenhum item no menu deve ser o item padrão. O método deve adicionar seus comandos ao menu.
CMF_NORMAL O menu de atalho será exibido normalmente. O método deve adicionar seus comandos ao menu.

 

Use InsertMenu ou InsertMenuItem para adicionar itens de menu à lista. Em seguida, retorne um valor HRESULT com a severidade definida como SEVERITY_SUCCESS. Defina o valor do código como o deslocamento do maior identificador de comando que foi atribuído, mais um (1). Por exemplo, suponha que idCmdFirst esteja definido como 5 e você adicione três itens ao menu com identificadores de comando de 5, 7 e 8. O valor de retorno deve ser MAKE_HRESULT(SEVERITY_SUCCESS, 0, 8 - 5 + 1).

O exemplo a seguir mostra uma implementação simples de IContextMenu::QueryContextMenu que insere um único comando. O deslocamento do identificador para o comando é IDM_DISPLAY, que é definido como zero. As variáveis m_pszVerb e m_pwszVerb são variáveis privadas usadas para armazenar a cadeia de caracteres de verbo independente de idioma associada nos formatos ANSI e Unicode.

#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));
}

Para outras tarefas de implementação de verbo, confira Criar Manipuladores de Menu de Contexto.

Menus de Atalho (Contexto) e Manipuladores de Menu de Atalho

Associações de Verbos e Arquivos

Escolher um Verbo Estático ou Dinâmico para o Menu de Atalho

Melhores Práticas para Manipuladores de Menu de Atalho e Vários Verbos de Seleção

Como Criar Manipuladores do Menu de Atalho

Referência do Menu de Atalho