Partilhar via


Personalizando 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 está organizado da seguinte forma:

Sobre verbos estáticos e dinâmicos

Recomendamos vivamente que implemente um menu de atalho usando um dos métodos verbais estáticos. Recomendamos que você siga as instruções fornecidas na seção "Personalizando um menu de atalho usando verbos estáticos" de Criando manipuladores de menu de contexto. Para obter o comportamento dinâmico para verbos estáticos no Windows 7 e posterior, consulte "Obtendo comportamento dinâmico para verbos estáticos" em Criando manipuladores de menu de contexto. Para obter detalhes sobre a implementação de verbos estáticos e quais verbos dinâmicos devem ser evitados, consulte Escolhendo 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 do sistema de arquivos para alguns caminhos. Se o manipulador de .exe estiver armazenado em um desses caminhos, ele não estará acessível neste contexto. Portanto, como uma solução alternativa, armazene seu .exe em um caminho que não seja redirecionado ou armazene uma versão stub do seu .exe que inicie a versão real.

 

Como os manipuladores de menu de atalho funcionam com verbos dinâmicos

Além de 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:

  • IShellExtInit (obrigatório)
  • IContextMenu (obrigatório)
  • IContextMenu2 (opcional)
  • IContextMenu3 (opcional)

Para obter mais informações sobre itens de menu desenhados pelo proprietário, consulte a seção Criando itens de menu Owner-Drawn em Usando menus.

O Shell usa o IShellExtInit interface 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 registo sob o qual o gestor de menus de atalho é registado. O método IShellExtInit::Initialize deve extrair o nome do ficheiro do objeto de dados e guardar o nome e o ponteiro da pasta numa lista de identificação de itens (PIDL) para uso posterior. Para obter mais informações sobre a inicialização do manipulador, consulte Implementing IShellExtInit.

Quando os verbos são apresentados em um menu de atalho, eles são primeiro descobertos, depois apresentados ao usuário e, finalmente, invocados. A lista a seguir descreve essas três etapas com 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 o identificador de 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. O manipulador pode então executar o comando apropriado.

Evitando 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 essa funcionalidade seja útil, o uso de nomes não qualificados pode resultar em colisões com vários fornecedores independentes de software (ISVs) que escolhem o mesmo nome de verbo. Para evitar isso, sempre prefixe verbos com o nome ISV da seguinte maneira:

ISV_Name.verb

Use sempre um ProgID específico do aplicativo. Ao adotar a convenção de mapear a extensão de nome de arquivo com um ProgID fornecido por ISV, evita-se possíveis colisões. No entanto, como alguns tipos de item não usam esse mapeamento, há uma necessidade de nomes exclusivos do fornecedor. Ao adicionar um verbo a um ProgID existente que já pode ter esse verbo registrado, você deve primeiro remover a chave do Registro para o verbo antigo antes de adicionar seu próprio verbo. Deve fazê-lo para evitar a fusão da informação verbal dos dois verbos. Não o fazer resulta num comportamento imprevisível.

Registrando um manipulador de menu de atalho com um verbo dinâmico

Os manipuladores de menu de atalho estão associados a um tipo de arquivo ou a uma pasta. Para tipos de arquivo, o manipulador é registrado na 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 como a forma de cadeia de caracteres do GUID do identificador de classe do manipulador (CLSID).

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

HKEY_CLASSES_ROOT
   FolderType
      shellex
         ContextMenuHandlers

Para obter mais informações sobre os tipos de pasta para os quais você pode registrar manipuladores, consulte Registrando manipuladores de extensão de shell.

Se um tipo de arquivo tiver um menu de atalho associado a ele, clicar duas vezes em um objeto normalmente iniciará o comando padrão e o método deIContextMenu::QueryContextMenu do manipulador não será chamado. Para especificar que o método de IContextMenu::QueryContextMenu do manipulador deve ser chamado quando um objeto é clica do 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 configuraçã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 sua 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 clica 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}

Implementando 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, consulte Escolhendo um verbo estático ou dinâmico para o menu de atalho. IContextMenu tem três métodos, GetCommandString, InvokeCommande QueryContextMenu, que são discutidos aqui em detalhes.

Método IContextMenu::GetCommandString

O manipulador IContextMenu::GetCommandString método é usado para retornar o nome canônico para um verbo. Este método é opcional. No Windows XP e versões anteriores do Windows, quando o Windows Explorer tem uma barra de status, esse método é usado para recuperar o texto de ajuda que é exibido na barra de status para 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 for solicitada uma cadeia de caracteres de ajuda, o uFlags será definido como GCS_HELPTEXTW. Copie a cadeia de caracteres de ajuda para o buffer pszName, convertendo-a em uma PWSTR. A string do verbo é solicitada definindo uFlags para GCS_VERBW. Copie a cadeia de caracteres apropriada para pszName, assim como acontece 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 IContextMenu::QueryContextMenu exemplo dado na seção IContextMenu::QueryContextMenu Method deste tópico. Como o manipulador adiciona apenas um item de menu, há apenas um conjunto de cadeias de caracteres que podem ser retornadas. 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 implementa apenas o suporte para os valores Unicode de uFlags, porque apenas esses foram 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 ela 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 cbSize membro de 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 da estrutura ou o membro lpVerbW da estrutura é usado para identificar o comando a ser executado. Os comandos são identificados de uma das seguintes maneiras:

  • Pela cadeia de caracteres do verbo 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 contém uma cadeia de caracteres verbais. Se a palavra de ordem superior for zero, o deslocamento do comando estará na palavra de ordem inferior de lpVerb.

O exemplo a seguir mostra uma implementação simples de IContextMenu::InvokeCommand que corresponde ao IContextMenu::QueryContextMenu e IContextMenu::GetCommandString exemplos fornecidos antes e depois desta seção. O método determina primeiro qual estrutura está a ser passada. Em seguida, determina se o comando é identificado pelo seu deslocamento ou pelo seu verbo. Se lpVerb ou lpVerbW contiver um verbo válido ou um offset, 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. 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 posteriormente 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.

Bandeira 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 (sem predefinição) 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 de HRESULT com a gravidade definida como SEVERITY_SUCCESS. Defina o valor do código como o deslocamento do maior identificador de comando atribuído, acrescentando um (1). Por exemplo, suponha que idCmdFirst está definido como 5 e você adiciona 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 verbais 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 verbos, consulte Criando manipuladores de menu de contexto.

Menus de atalho (contexto) e manipuladores de menu de atalho

Verbos e Associações de Arquivos

Escolher um verbo estático ou dinâmico para o seu menu de atalho

Práticas recomendadas para manipuladores de menu de atalho e verbos de seleção múltipla

Criando manipuladores de menu de atalho

de referência do menu de atalho