Partager via


Personnalisation d’un menu contextuel à l’aide de verbes dynamiques

Les gestionnaires de menus contextuels sont également appelés gestionnaires de verbes. Un gestionnaire de menus contextuels est un type de gestionnaire de type de fichier.

Cette rubrique est organisée comme suit :

À propos des verbes statiques et dynamiques

Nous vous encourageons vivement à implémenter un menu contextuel à l’aide de l’une des méthodes de verbe statique. Nous vous recommandons de suivre les instructions fournies dans la section « Personnalisation d’un menu contextuel à l’aide de verbes statiques » de Création de gestionnaires de menus contextuels. Pour obtenir un comportement dynamique pour les verbes statiques dans Windows 7 et versions ultérieures, consultez « Obtention d’un comportement dynamique pour les verbes statiques » dans Création de gestionnaires de menus contextuels. Pour plus d’informations sur l’implémentation de verbes statiques et les verbes dynamiques à éviter, consultez Choisir entre un verbe statique et dynamique pour votre menu contextuel.

Si vous devez étendre le menu contextuel d’un type de fichier en inscrivant un verbe dynamique pour le type de fichier, suivez les instructions fournies plus loin dans cette rubrique.

Remarque

Il existe des considérations spéciales pour Windows 64 bits lors de l’inscription de gestionnaires qui fonctionnent dans le contexte d’applications 32 bits : lorsque des verbes Shell sont appelés dans le contexte d’une application 32 bits, le sous-système WOW64 redirige l’accès au système de fichiers vers certains chemins. Si votre gestionnaire .exe est stocké dans l’un de ces chemins, il n’est pas accessible dans ce contexte. Par conséquent, pour contourner ce problème, stockez votre fichier .exe dans un chemin d’accès qui n’est pas redirigé, ou stockez une version stub de votre fichier .exe qui lance la version réelle.

 

Fonctionnement des gestionnaires de menus contextuels avec des verbes dynamiques

Outre IUnknown, les gestionnaires de menus contextuels exportent les interfaces supplémentaires suivantes pour gérer les messages nécessaires pour implémenter des éléments de menu dessinés par le propriétaire :

Pour plus d’informations sur les éléments de menu dessinés par le propriétaire, consultez la section Création d’éléments de menu dessinés par le propriétaire dans Utilisation des menus.

L’interpréteur de commandes utilise l’interface IShellExtInit pour initialiser le gestionnaire. Lorsque l’interpréteur de commandes appelle IShellExtInit::Initialize, il transmet un objet de données avec le nom de l’objet et un pointeur vers une liste d’identificateurs d’élément (PIDL) du dossier qui contient le fichier. Le paramètre hkeyProgID est l’emplacement du Registre sous lequel le handle de menu contextuel est inscrit. La méthode IShellExtInit::Initialize doit extraire le nom de fichier de l’objet de données et stocker le nom et le pointeur du dossier vers une liste d’identificateurs d’élément (PIDL) pour une utilisation ultérieure. Pour plus d’informations sur l’initialisation du gestionnaire, consultez Implémentation d’IShellExtInit.

Lorsque des verbes sont présentés dans un menu contextuel, ils sont d’abord découverts, puis présentés à l’utilisateur et enfin appelés. La liste suivante décrit ces trois étapes plus en détail :

  1. L’interpréteur de commandes appelle IContextMenu::QueryContextMenu, qui retourne un ensemble de verbes qui peuvent être basés sur l’état des éléments ou du système.
  2. Le système transmet un handle HMENU que la méthode peut utiliser pour ajouter des éléments au menu contextuel.
  3. Si l’utilisateur clique sur l’un des éléments du gestionnaire, l’interpréteur de commandes appelle IContextMenu::InvokeCommand. Le gestionnaire peut ensuite exécuter la commande appropriée.

Éviter les collisions en raison de noms de verbes non qualifiés

Étant donné que les verbes sont inscrits par type, le même nom de verbe peut être utilisé pour les verbes sur différents éléments. Cela permet aux applications de faire référence à des verbes courants indépendamment du type d’élément. Bien que cette fonctionnalité soit utile, l’utilisation de noms non qualifiés peut entraîner des collisions avec plusieurs éditeurs de logiciels indépendants (ISV) qui choisissent le même nom de verbe. Pour éviter cela, préfixez toujours les verbes avec le nom de l’éditeur de logiciels indépendant comme suit :

ISV_Name.verb

Utilisez toujours un ProgID spécifique à l’application. L’adoption de la convention de mappage de l’extension de nom de fichier à un ProgID fourni par un éditeur de logiciels indépendant évite les collisions potentielles. Toutefois, étant donné que certains types d’éléments n’utilisent pas ce mappage, il est nécessaire de trouver des noms uniques au fournisseur. Lors de l’ajout d’un verbe à un ProgID existant qui peut déjà avoir ce verbe inscrit, vous devez d’abord supprimer la clé de Registre de l’ancien verbe avant d’ajouter votre propre verbe. Vous devez le faire pour éviter de fusionner les informations des deux verbes. L’échec de cette opération entraîne un comportement imprévisible.

Inscription d’un gestionnaire de menus contextuels avec un verbe dynamique

Les gestionnaires de menus contextuels sont associés à un type de fichier ou à un dossier. Pour les types de fichiers, le gestionnaire est inscrit sous la sous-clé suivante.

HKEY_CLASSES_ROOT
   Program ID
      shellex
         ContextMenuHandlers

Pour associer un gestionnaire de menus contextuels à un type de fichier ou à un dossier, créez d’abord une sous-clé sous la sous-clé ContextMenuHandlers. Nommez la sous-clé du gestionnaire et définissez la valeur par défaut de la sous-clé sur la forme de chaîne du GUID CLSID (Class Identifier) du gestionnaire.

Ensuite, pour associer un gestionnaire de menus contextuels à différents types de dossiers, inscrivez le gestionnaire de la même façon que vous le feriez pour un type de fichier, mais sous la sous-clé FolderType, comme illustré dans l’exemple suivant.

HKEY_CLASSES_ROOT
   FolderType
      shellex
         ContextMenuHandlers

Pour plus d’informations sur les types de dossiers pour lesquels vous pouvez inscrire des gestionnaires, consultez Inscription de gestionnaires d’extensions d’interpréteur de commandes.

Si un type de fichier est associé à un menu contextuel, le double-clic sur un objet lance normalement la commande par défaut et la méthode IContextMenu::QueryContextMenu n’est pas appelée. Pour spécifier que la méthode IContextMenu::QueryContextMenu du gestionnaire doit être appelée en cas de double clic sur un objet, créez une sous-clé sous la sous-clé CLSID du gestionnaire, comme illustré ici.

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

Lors d’un double clic sur un objet associé au gestionnaire, IContextMenu::QueryContextMenu est appelé avec l’indicateur CMF_DEFAULTONLY défini dans le paramètre uFlags.

Les gestionnaires de menus contextuels doivent définir la sous-clé MayChangeDefaultMenu uniquement s’ils peuvent avoir besoin de modifier le verbe par défaut du menu contextuel. La définition de cette sous-clé force le système à charger la DLL du gestionnaire lorsqu’un élément associé est double-cliqué. Si votre gestionnaire ne modifie pas le verbe par défaut, vous ne devez pas définir cette sous-clé, car cela entraîne le chargement inutile de votre DLL par le système.

L’exemple suivant illustre les entrées de Registre qui activent un gestionnaire de menus contextuels pour un type de fichier .myp. La sous-clé CLSID du gestionnaire inclut une sous-clé MayChangeDefaultMenu pour garantir que le gestionnaire est appelé lorsque l’utilisateur double-clique sur un objet associé.

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}

Implémentation de l’interface IContextMenu

IContextMenu est la méthode la plus puissante, mais aussi la plus complexe à implémenter. Nous vous recommandons vivement d’implémenter un verbe à l’aide de l’une des méthodes de verbe statique. Pour plus d’informations, consultez Choisir un verbe statique ou dynamique pour votre menu contextuel. IContextMenu a trois méthodes, GetCommandString, InvokeCommand et QueryContextMenu, qui sont décrites ici en détail.

Méthode IContextMenu::GetCommandString

La méthode IContextMenu::GetCommandString du gestionnaire est utilisée pour renvoyer le nom canonique d’un verbe. Cette méthode est facultative. Dans Windows XP et les versions antérieures de Windows, lorsque l’Explorateur Windows a une barre d’état, cette méthode est utilisée pour récupérer le texte d’aide affiché dans la barre d’état d’un élément de menu.

Le paramètre idCmd contient le décalage d’identificateur de la commande qui a été définie lorsque IContextMenu::QueryContextMenu a été appelé. Si une chaîne d’aide est demandée, uFlags est défini sur GCS_HELPTEXTW. Copiez la chaîne d’aide dans la mémoire tampon pszName, en la castant sur un PWSTR. La chaîne de verbe est demandée en définissant uFlags sur GCS_VERBW. Copiez la chaîne appropriée dans pszName, comme avec la chaîne d’aide. Les indicateurs GCS_VALIDATEA et GCS_VALIDATEW ne sont pas utilisés par les gestionnaires de menus contextuels.

L’exemple suivant montre une implémentation simple de IContextMenu::GetCommandString qui correspond à l’exemple IContextMenu::QueryContextMenu donné dans la section IContextMenu::QueryContextMenu de cette rubrique. Étant donné que le gestionnaire ajoute un seul élément de menu, il n’existe qu’un seul ensemble de chaînes qui peuvent être retourné. La méthode teste si idCmd est valide et, le cas échéant, retourne la chaîne demandée.

La fonction StringCchCopy est utilisée pour copier la chaîne demandée dans pszName pour vous assurer que la chaîne copiée ne dépasse pas la taille de la mémoire tampon spécifiée par cchName. Cet exemple implémente uniquement la prise en charge des valeurs Unicode d’uFlags, car ce sont les seules valeurs utilisées dans l’Explorateur Windows depuis 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éthode IContextMenu::InvokeCommand

Cette méthode est appelée lorsqu’un utilisateur clique sur un élément de menu pour indiquer au gestionnaire d’exécuter la commande associée. Le paramètre pici pointe vers une structure qui contient les informations requises.

Bien que pici soit déclaré dans Shlobj.h comme une structure CMINVOKECOMMANDINFO, dans la pratique, il pointe souvent vers une structure CMINVOKECOMMANDINFOEX. Cette structure est une version étendue de CMINVOKECOMMANDINFO et possède plusieurs membres supplémentaires qui permettent de transmettre des chaînes Unicode.

Vérifiez le membre cbSize de pici pour déterminer la structure transmise. S’il s’agit d’une structure CMINVOKECOMMANDINFOEX et que le membre fMask a le jeu d’indicateurs CMIC_MASK_UNICODE, castez pici sur CMINVOKECOMMANDINFOEX. Cela permet à votre application d’utiliser les informations Unicode contenues dans les cinq derniers membres de la structure.

Le membre lpVerb ou lpVerbW de la structure est utilisé pour identifier la commande à exécuter. Les commandes sont identifiées de l’une des deux manières suivantes :

  • Par la chaîne de verbe de la commande
  • Par le décalage d’identificateur de la commande

Pour distinguer ces deux cas, vérifiez le mot d’ordre élevé de lpVerb pour ANSI ou lpVerbW pour Unicode. Si le mot d’ordre élevé est différent de zéro, lpVerb ou lpVerbW contient une chaîne de verbe. Si le mot d’ordre élevé est égal à zéro, le décalage de commande est dans le mot de bas ordre de lpVerb.

L’exemple suivant montre une implémentation simple d’IContextMenu::InvokeCommand qui correspond aux exemples IContextMenu::QueryContextMenu et IContextMenu::GetCommandString donnés avant et après cette section. La méthode détermine d’abord la structure transmise. Elle détermine ensuite si la commande est identifiée par son décalage ou son verbe. Si lpVerb ou lpVerbW contient un verbe ou un décalage valide, la méthode affiche une boîte de message.

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::Méthode QueryContextMenu

L’interpréteur de commandes appelle IContextMenu::QueryContextMenu pour permettre au gestionnaire de menus contextuels d’ajouter ses éléments de menu au menu. Il transmet le handle HMENU dans le paramètre hmenu. Le paramètre indexMenu est défini sur l’index à utiliser pour le premier élément de menu à ajouter.

Tous les éléments de menu ajoutés par le gestionnaire doivent présenter des identificateurs compris entre les valeurs des paramètres idCmdFirst et idCmdLast. En règle générale, le premier identificateur de commande est défini sur idCmdFirst, qui est incrémenté d’un (1) pour chaque commande supplémentaire. Cette pratique vous permet d’éviter de dépasser idCmdLast et d’optimiser le nombre d’identificateurs disponibles au cas où l’interpréteur de commandes appelle plusieurs gestionnaires.

Le décalage de commande d’un identificateur d’élément est la différence entre l’identificateur et la valeur dans idCmdFirst. Stockez le décalage de chaque élément que votre gestionnaire ajoute au menu contextuel, car l’interpréteur de commandes peut l’utiliser pour identifier l’élément s’il appelle ensuite IContextMenu::GetCommandString ou IContextMenu::InvokeCommand.

Vous devez également attribuer un verbe à chaque commande que vous ajoutez. Un verbe est une chaîne qui peut être utilisée au lieu du décalage pour identifier la commande lorsque IContextMenu::InvokeCommand est appelé. Il est également utilisé par des fonctions telles que ShellExecuteEx pour exécuter des commandes de menu contextuel.

Il existe trois indicateurs qui peuvent être transmis via le paramètre uFlags qui sont pertinents pour les gestionnaires de menus contextuels. Ils sont décrits dans le tableau suivant.

Indicateur Description
CMF_DEFAULTONLY L’utilisateur a sélectionné la commande par défaut, généralement en double-cliquant sur l’objet. IContextMenu::QueryContextMenu doit retourner le contrôle à l’interpréteur de commandes sans modifier le menu.
CMF_NODEFAULT Aucun élément du menu ne doit être l’élément par défaut. La méthode doit ajouter ses commandes au menu.
CMF_NORMAL Le menu contextuel s’affiche normalement. La méthode doit ajouter ses commandes au menu.

 

Utilisez InsertMenu ou InsertMenuItem pour ajouter des éléments de menu à la liste. Retournez ensuite une valeur HRESULT avec la gravité définie sur SEVERITY_SUCCESS. Définissez la valeur du code sur le décalage du plus grand identificateur de commande attribué, plus un (1). Par exemple, supposons que IdCmdFirst est défini sur 5 et que vous ajoutez trois éléments au menu avec des identificateurs de commande de 5, 7 et 8. La valeur de retour doit être MAKE_HRESULT(SEVERITY_SUCCESS, 0, 8 - 5 + 1).

L’exemple suivant montre une implémentation simple d’IContextMenu::QueryContextMenu qui insère une seule commande. Le décalage d’identificateur de la commande est IDM_DISPLAY, qui est défini sur zéro. Les variables m_pszVerb et m_pwszVerb sont des variables privées utilisées pour stocker la chaîne de verbe indépendante du langage associée aux formats ANSI et 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));
}

Pour d’autres tâches d’implémentation de verbe, consultez Création de gestionnaires de menus contextuels.

Menus contextuels et gestionnaires de menus contextuels

Verbes et associations de fichiers

Choisir entre un verbe statique et dynamique pour votre menu contextuel

Meilleures pratiques pour les gestionnaires de menus contextuels et les verbes de sélection multiple

Création de gestionnaires de menu contextuel

Informations de référence sur le menu contextuel