Personalización de un menú contextual mediante verbos dinámicos
Los controladores de menú contextual también se conocen como controladores de verbo. Un controlador de menú contextual es un tipo de controlador de tipo de archivo.
Este tema se organiza de la siguiente manera:
- Acerca de los verbos estáticos y dinámicos
- Cómo funcionan los controladores de menú contextual con verbos dinámicos
- Evitar colisiones debido a nombres de verbo no calificados
- Registro de un controlador de menú contextual con un verbo dinámico
- Implementación de la interfaz IContextMenu
- Temas relacionados
Acerca de los verbos estáticos y dinámicos
Le recomendamos encarecidamente que implemente un menú contextual mediante uno de los métodos de verbo estático. Se recomienda seguir las instrucciones proporcionadas en la sección "Personalización de un menú contextual mediante verbos estáticos" de Creación de controladores de menú contextual. Para obtener el comportamiento dinámico de los verbos estáticos en Windows 7 y versiones posteriores, consulte "Obtener comportamiento dinámico para verbos estáticos" en Creación de controladores de menú contextual. Para obtener más información sobre la implementación de verbos estáticos y los verbos dinámicos que se deben evitar, consulte Elección de un verbo estático o dinámico para el menú contextual.
Si debe extender el menú contextual para un tipo de archivo registrando un verbo dinámico para el tipo de archivo, siga las instrucciones que se proporcionan más adelante en este tema.
Nota:
Hay consideraciones especiales para Windows de 64 bits al registrar controladores que funcionan en el contexto de aplicaciones de 32 bits: cuando se invocan verbos Shell en el contexto de una aplicación de 32 bits, el subsistema WOW64 redirige el acceso del sistema de archivos a algunas rutas de acceso. Si el controlador .exe se almacena en una de esas rutas de acceso, no es accesible en este contexto. Por lo tanto, como solución alternativa, almacene el .exe en una ruta de acceso que no se redirija o almacene una versión de código auxiliar del .exe que inicie la versión real.
Cómo funcionan los controladores de menú contextual con verbos dinámicos
Además de IUnknown, los controladores de menú contextual exportan las siguientes interfaces adicionales para controlar la mensajería necesaria para implementar los elementos de menú dibujados por el propietario:
- IShellExtInit (obligatorio)
- IContextMenu (obligatorio)
- IContextMenu2 (opcional)
- IContextMenu3 (opcional)
Para obtener más información sobre los elementos de menú dibujados por el propietario, consulte la sección Creación de elementos de menú dibujados por el propietario en Uso de menús.
El Shell usa la interfaz IShellExtInit para inicializar el controlador. Cuando el Shell llama a IShellExtInit::Initialize, pasa un objeto de datos con el nombre del objeto y un puntero a una lista de identificadores de elemento (PIDL) de la carpeta que contiene el archivo. El parámetro hkeyProgID es la ubicación del registro en la que se registra el identificador del menú contextual. El método IShellExtInit::Initialize debe extraer el nombre de archivo del objeto de datos y almacenar el nombre y el puntero de la carpeta en una lista de identificadores de elemento (PIDL) para su uso posterior. Para obtener más información sobre la inicialización del controlador, consulte Implementación de IShellExtInit.
Cuando los verbos se presentan en un menú contextual, primero se detectan, después se presentan al usuario y, por último, se invocan. En la lista siguiente se describen estos tres pasos con más detalle:
- El Shell llama a IContextMenu::QueryContextMenu, que devuelve un conjunto de verbos que se pueden basar en el estado de los elementos o el sistema.
- El sistema pasa un identificador HMENU que el método puede usar para agregar elementos al menú contextual.
- Si el usuario hace clic en uno de los elementos del controlador, el Shell llama a IContextMenu::InvokeCommand. Después, el controlador puede ejecutar el comando adecuado.
Evitar colisiones debido a nombres de verbo no calificados
Dado que los verbos están registrados por tipo, se puede usar el mismo nombre de verbo para verbos en distintos elementos. Al hacerlo, las aplicaciones pueden hacer referencia a verbos comunes independientemente del tipo de elemento. Aunque esta funcionalidad es útil, el uso de nombres no calificados puede dar lugar a colisiones con varios fabricantes de software independientes (ISV) que eligen el mismo nombre de verbo. Para evitar esto, siempre use un prefijo en el verbo con el nombre del ISV, como se indica a continuación:
ISV_Name.verb
Use siempre un ProgID específico de la aplicación. La adopción de la convención de asignación de la extensión de nombre de archivo a un ProgID proporcionado por el ISV evita posibles colisiones. Sin embargo, dado que algunos tipos de elementos no usan esta asignación, hay una necesidad de usar nombres únicos del fabricante. Al agregar un verbo a un ProgID existente que ya tenga ese verbo registrado, primero debe quitar la clave del registro del verbo anterior antes de agregar su propio verbo. Debe hacerlo para evitar combinar la información de verbo de los dos verbos. Si no lo hace, se produce un comportamiento imprevisible.
Registro de un controlador de menú contextual con un verbo dinámico
Los controladores de menú contextual están asociados a un tipo de archivo o a una carpeta. En el caso de los tipos de archivo, el controlador se registra en la siguiente subclave.
HKEY_CLASSES_ROOT
Program ID
shellex
ContextMenuHandlers
Para asociar un controlador de menú contextual con un tipo de archivo o una carpeta, cree primero una subclave en la subclave ContextMenuHandlers. Asigne un nombre a la subclave para el controlador y establezca el valor predeterminado de la subclave en la forma de cadena del GUID del identificador de clase del controlador (CLSID).
A continuación, para asociar un controlador de menú contextual con diferentes tipos de carpetas, registre el controlador de la misma manera que lo haría para un tipo de archivo, pero en la subclave FolderType como se muestra en el ejemplo siguiente.
HKEY_CLASSES_ROOT
FolderType
shellex
ContextMenuHandlers
Para obtener más información sobre los tipos de carpeta para los que puede registrar controladores, consulte Registro de controladores de extensión de Shell.
Si un tipo de archivo tiene un menú contextual asociado, al hacer doble clic en un objeto normalmente se inicia el comando predeterminado y no se llama al método IContextMenu::QueryContextMenu del controlador. Para especificar que se debe llamar al método IContextMenu::QueryContextMenu del controlador cuando se hace doble clic en un objeto, cree una subclave bajo la subclave CLSID del controlador, como se muestra aquí.
HKEY_CLASSES_ROOT
CLSID
{00000000-1111-2222-3333-444444444444}
shellex
MayChangeDefaultMenu
Cuando se hace doble clic en un objeto asociado al controlador, se llama a IContextMenu::QueryContextMenu con la marca CMF_DEFAULTONLY establecida en el parámetro uFlags.
Los controladores de menú contextual deben establecer la subclave MayChangeDefaultMenu solo si es posible que necesiten cambiar el verbo predeterminado del menú contextual. Establecer esta subclave obliga al sistema a cargar la DLL del controlador cuando se hace doble clic en un elemento asociado. Si el controlador no cambia el verbo predeterminado, no debe establecer esta subclave porque, al hacerlo, el sistema carga la DLL innecesariamente.
En el ejemplo siguiente se muestran las entradas del registro que habilitan un controlador de menú contextual para un tipo de archivo .myp. La subclave CLSID del controlador incluye una subclave MayChangeDefaultMenu para garantizar que se llame al controlador cuando el usuario haga doble clic en un 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}
Implementación de la interfaz IContextMenu
IContextMenu es el método más eficaz, pero también el más complicado de implementar. Se recomienda encarecidamente implementar un verbo mediante uno de los métodos de verbo estático. Para obtener más información, consulte Elegir un verbo estático o dinámico para el menú contextual. IContextMenu tiene tres métodos, GetCommandString, InvokeCommandy QueryContextMenu, que se describen aquí en detalle.
Método IContextMenu::GetCommandString
El método IContextMenu::GetCommandString del controlador se usa para devolver el nombre canónico de un verbo. Este método es opcional. En Windows XP y versiones anteriores de Windows, cuando el Explorador de Windows tiene una barra de estado, este método se usa para recuperar el texto de ayuda que se muestra en la barra de estado de un elemento de menú.
El parámetro idCmd contiene el desplazamiento del identificador del comando que se definió cuando se llamó a IContextMenu::QueryContextMenu. Si se solicita una cadena de ayuda, uFlags se establecerá en GCS_HELPTEXTW. Copie la cadena de ayuda en el búfer pszName y conviértala a PWSTR. La cadena de verbo se solicita estableciendo uFlags en GCS_VERBW. Copie la cadena adecuada en pszName, igual que con la cadena de ayuda. Los controladores de menú contextual no usan las marcas GCS_VALIDATEA y GCS_VALIDATEW.
En el ejemplo siguiente se muestra una implementación sencilla de IContextMenu::GetCommandString que corresponde al ejemplo IContextMenu::QueryContextMenu proporcionado en la sección IContextMenu::QueryContextMenu Method de este tema. Dado que el controlador agrega solo un elemento de menú, solo hay un conjunto de cadenas que se puede devolver. El método comprueba si idCmd es válido y, si es así, devuelve la cadena solicitada.
La función StringCchCopy se usa para copiar la cadena solicitada en pszName para asegurarse de que la cadena copiada no supere el tamaño del búfer especificado por cchName. En este ejemplo solo se implementa la compatibilidad con los valores Unicode de uFlags, ya que solo esos valores se han usado en el Explorador de Windows desde 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
Se llama a este método cuando un usuario hace clic en un elemento de menú para indicar al controlador que ejecute el comando asociado. El parámetro pici apunta a una estructura que contiene la información necesaria.
Aunque pici se declara en Shlobj.h como una estructura CMINVOKECOMMANDINFO, en la práctica suele apuntar a una estructura CMINVOKECOMMANDINFOEX. Esta estructura es una versión extendida de CMINVOKECOMMANDINFO y tiene varios miembros adicionales que permiten pasar cadenas Unicode.
Compruebe el miembro cbSize de pici para determinar en qué estructura se pasó. Si es una estructura CMINVOKECOMMANDINFOEX y el miembro fMask tiene establecida la marca CMIC_MASK_UNICODE, convierta pici en CMINVOKECOMMANDINFOEX. Esto permite a la aplicación usar la información Unicode contenida en los últimos cinco miembros de la estructura.
El miembro lpVerb o lpVerbW de la estructura se usa para identificar el comando que se va a ejecutar. Los comandos se identifican de una de las dos maneras siguientes:
- Por la cadena de verbo del comando
- Mediante el desplazamiento del identificador del comando
Para distinguir entre estos dos casos, compruebe la palabra de orden superior de lpVerb para el caso ANSI o lpVerbW para el caso Unicode. Si la palabra de orden superior es distinta de cero, lpVerb o lpVerbW contiene una cadena de verbo. Si la palabra de orden superior es cero, el desplazamiento del comando se encuentra en la palabra de orden inferior de lpVerb.
En el ejemplo siguiente se muestra una implementación sencilla de IContextMenu::InvokeCommand que corresponde a los ejemplos IContextMenu::QueryContextMenu y IContextMenu::GetCommandString proporcionados antes y después de esta sección. El método determina primero en qué estructura se pasa. A continuación, determina si el comando se identifica mediante su desplazamiento o su verbo. Si lpVerb o lpVerbW contiene un verbo o desplazamiento válidos, el método muestra un cuadro de mensaje.
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
El Shell llama a IContextMenu::QueryContextMenu para permitir que el controlador de menú contextual agregue sus elementos de menú al menú. Pasa el controlador HMENU en el parámetro hmenu. El parámetro indexMenu se establece en el índice que se va a usar para el primer elemento de menú que se va a agregar.
Los elementos de menú agregados por el controlador deben tener identificadores que se encuentren entre los valores de los parámetros idCmdFirst e idCmdLast. Normalmente, el primer identificador de comando se establece en idCmdFirst, que se incrementa en uno (1) para cada comando adicional. Esta práctica le ayuda a evitar superar idCmdLast y maximiza el número de identificadores disponibles en caso de que el Shell llame a más de un controlador.
El desplazamiento de comandos de un identificador de elemento es la diferencia entre el identificador y el valor de idCmdFirst. Almacene el desplazamiento de cada elemento que el controlador agrega al menú contextual porque el Shell podría usarlo para identificar el elemento si posteriormente llama a IContextMenu::GetCommandString o IContextMenu::InvokeCommand.
También debe asignar un verbo a cada comando que agregue. Un verbo es una cadena que se puede usar en lugar del desplazamiento para identificar el comando cuando se llama a IContextMenu::InvokeCommand. También lo usan funciones como ShellExecuteEx para ejecutar comandos de menú contextual.
Hay tres marcas que se pueden pasar a través del parámetro uFlags que son relevantes para los controladores de menú contextual. Se describen en la tabla siguiente.
Marca | Descripción |
---|---|
CMF_DEFAULTONLY | El usuario ha seleccionado el comando predeterminado, normalmente haciendo doble clic en el objeto. IContextMenu::QueryContextMenu debe devolver el control al Shell sin modificar el menú. |
CMF_NODEFAULT | Ningún elemento del menú debe ser el elemento predeterminado. El método debe agregar sus comandos al menú. |
CMF_NORMAL | El menú contextual se mostrará normalmente. El método debe agregar sus comandos al menú. |
Use InsertMenu o InsertMenuItem para agregar elementos de menú a la lista. A continuación, devuelve un valor HRESULT con la gravedad establecida en SEVERITY_SUCCESS. Establezca el valor de código en el desplazamiento del identificador de comando más grande que se asignó, más uno (1). Por ejemplo, suponga que idCmdFirst está establecido en 5 y agrega tres elementos al menú con identificadores de comando de 5, 7 y 8. Se debería devolver el valor MAKE_HRESULT(SEVERITY_SUCCESS, 0, 8 - 5 + 1)
.
En el ejemplo siguiente se muestra una implementación sencilla de IContextMenu::QueryContextMenu que inserta un único comando. El desplazamiento del identificador del comando es IDM_DISPLAY, que se establece en cero. Las variables m_pszVerb y m_pwszVerb son variables privadas que se usan para almacenar la cadena de verbo independiente del lenguaje asociada en formatos ANSI y 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 ver otras tareas de implementación de verbos, consulte Creación de controladores de menú contextual.
Temas relacionados