2010-09-23 30 views
11

En el Explorador de Windows, hace clic derecho en un archivo, aparece un menú contextual que contiene elementos incorporados como 'Enviar a ...' y/o acciones de terceros como 'archivo zip con Winzip'. Mi pregunta es:¿Cómo acceder a los elementos del menú contextual del shell de Windows?

  • ¿Cómo obtener la lista completa de los elementos de menú disponibles para un archivo específico?
  • Para cada elemento del menú, ¿cómo obtener la leyenda?
  • ¿Cómo invocar una acción de elemento de menú específico para un archivo de disco específico?

Gracias de antemano!

[EDITAR]: Mientras que otra información es absolutamente útil, la solución de Delphi será muy apreciada.

+0

¿quieres crear propios elementos del menú contextual o manipular elementos de menú existentes? – Liton

+0

@Liton, no creando mis propios elementos de menú de contexto de shell, sino para manipular elementos de menú existentes incorporados o de 3ª parte. –

Respuesta

9

La clave para obtener el menú contextual de Shell es utilizar la interfaz IContextMenu.

revise este gran artículo Shell context menu support para más detalles.

ACTUALIZACIÓN

de ejemplos de Delphi se puede ver la unidad JclShell del JEDI JCL (compruebe el funcionamiento DisplayContextMenu) y la unidad de ShellCtrls incluido en la carpeta de ejemplos de Delphi.

+0

¡Gracias por el enlace, lo comprobaré! –

+0

¡Hola RRUZ, el funciton DisplayContextMenu del paquete JCL hace exactamente lo que quiero! ¡Gracias! –

7

Respuesta corta

Prueba el ShellBrowser Components de JAM Software. Tienen un componente que te permitirá mostrar el menú contextual del Explorer con tus propios comandos mezclados desde un TPopupMenu.


Respuesta larga

Conseguir el menú del explorador, la consulta de todas sus propiedades, y los acoge en su propio menú es posible, pero que realmente debe ser cómoda la lectura/escritura de bajo nivel de código de Win32 y un conocimiento práctico de C ayudará. También tendrá que tener cuidado con algunos problemas (que se detallan a continuación). Recomiendo leer la serie How to host an IContextMenu de Raymond Chen para obtener muchos detalles técnicos.

El enfoque más fácil es una consulta de la interfaz de IContextMenu, entonces el HMENU, a continuación, utilizar TrackPopupMenu para dejar que Windows muestre el menú, a continuación, llamar InvokeCommand al final.

Algunos de los códigos siguientes no han sido probados o han sido modificados con respecto a lo que estamos utilizando, por lo tanto, proceda bajo su propia responsabilidad.

Así es como se obtiene la IContextMenu, para un grupo de archivos dentro de una carpeta de base:

function GetExplorerMenu(AHandle: HWND; const APath: string; 
    AFilenames: TStrings): IContextMenu; 
var 
    Desktop, Parent: IShellFolder; 
    FolderPidl: PItemIDList; 
    FilePidls: array of PItemIDList; 
    PathW: WideString; 
    i: Integer; 
begin 
    // Retrieve the Desktop's IShellFolder interface 
    OleCheck(SHGetDesktopFolder(Desktop)); 
    // Retrieve the parent folder's PItemIDList and then it's IShellFolder interface 
    PathW := WideString(IncludeTrailingPathDelimiter(APath)); 
    OleCheck(Desktop.ParseDisplayName(AHandle, nil, PWideChar(PathW), 
    Cardinal(nil^), FolderPidl, Cardinal(nil^))); 
    try 
    OleCheck(Desktop.BindToObject(FolderPidl, nil, IID_IShellFolder, Parent)); 
    finally 
    SHFree(FolderPidl); 
    end; 
    // Retrieve PIDLs for each file, relative the the parent folder 
    SetLength(FilePidls, AFilenames.Count); 
    try 
    FillChar(FilePidls[0], SizeOf(PItemIDList) * AFilenames.Count, 0); 
    for i := 0 to AFilenames.Count-1 do begin 
     PathW := WideString(AFilenames[i]); 
     OleCheck(Parent.ParseDisplayName(AHandle, nil, PWideChar(PathW), 
     Cardinal(nil^), FilePidls[i], Cardinal(nil^))); 
    end; 
    // Get the context menu for the files from the parent's IShellFolder 
    OleCheck(Parent.GetUIObjectOf(AHandle, AFilenames.Count, FilePidls[0], 
     IID_IContextMenu, nil, Result)); 
    finally 
    for i := 0 to Length(FilePidls) - 1 do 
     SHFree(FilePidls[i]); 
    end; 
end; 

para obtener los elementos de menú real que necesita para llamar IContextMenu.QueryContextMenu. Puede destruir el HMENU devuelto usando DestroyMenu.

function GetExplorerHMenu(const AContextMenu: IContextMenu): HMENU; 
const 
    MENUID_FIRST = 1; 
    MENUID_LAST = $7FFF; 
var 
    OldMode: UINT; 
begin 
    OldMode := SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOOPENFILEERRORBOX); 
    try 
    Result := CreatePopupMenu; 
    AContextMenu.QueryContextMenu(Result, 0, MENUID_FIRST, MENUID_LAST, CMF_NORMAL); 
    finally 
    SetErrorMode(OldMode); 
    end; 
end; 

Así es como en realidad se llama el comando que el usuario ha seleccionado en el menú:

procedure InvokeCommand(const AContextMenu: IContextMenu; AVerb: PChar); 
const 
    CMIC_MASK_SHIFT_DOWN = $10000000; 
    CMIC_MASK_CONTROL_DOWN = $20000000; 
var 
    CI: TCMInvokeCommandInfoEx; 
begin 
    FillChar(CI, SizeOf(TCMInvokeCommandInfoEx), 0); 
    CI.cbSize := SizeOf(TCMInvokeCommandInfo); 
    CI.hwnd := GetOwnerHandle(Owner); 
    CI.lpVerb := AVerb; 
    CI.nShow := SW_SHOWNORMAL; 
    // Ignore return value for InvokeCommand. Some shell extensions return errors 
    // from it even if the command worked. 
    try 
    AContextMenu.InvokeCommand(PCMInvokeCommandInfo(@CI)^) 
    except on E: Exception do 
    MessageDlg(Owner, E.Message, mtError, [mbOk], 0); 
    end; 
end; 

procedure InvokeCommand(const AContextMenu: IContextMenu; ACommandID: UINT); 
begin 
    InvokeCommand(AContextMenu, MakeIntResource(Word(ACommandID))); 
end; 

Ahora puede utilizar la función GetMenuItemInfo para obtener el título, mapa de bits, etc., sino una mucho más fácil El enfoque es llamar al TrackPopupMenu y dejar que Windows muestre el menú emergente. Eso sería algo como esto:

procedure ShowExplorerMenu(AForm: TForm; AMousePos: TPoint; 
    const APath: string; AFilenames: TStrings;); 
var 
    ShellMenu: IContextMenu; 
    Menu: HMENU; 
    MenuID: LongInt; 
begin 
    ShellMenu := GetExplorerMenu(AForm.Handle, APath, AFilenames); 
    Menu := GetExplorerHMenu(ShellMenu); 
    try 
    MenuID := TrackPopupMenu(Menu, TPM_LEFTALIGN or TPM_TOPALIGN or TPM_RETURNCMD, 
     AMousePos.X, AMousePos.Y, 0, AForm.Handle, nil); 
    InvokeCommand(ShellMenu, MenuID - MENUID_FIRST); 
    finally 
    DestroyMenu(Menu); 
    end; 
end; 

Si realmente se desea extraer los elementos del menú/subtítulos y añadirlos a su propio menú emergente (usamos la barra 2000 y hacemos exactamente eso), aquí están los otros grandes problemas se encontrará con:

  • El "Enviar a" del menú, y cualesquiera otras que se construyen bajo demanda no funcionarán a menos que resuelva los mensajes y pasarlos a las interfaces IContextMenu2/IContextMenu3.
  • Los mapas de bits del menú están en un par de formatos diferentes. Delphi no maneja los colores altos de Vista sin persuasión, y los más antiguos se combinan en el color de fondo con un XOR.
  • Algunos elementos del menú son dibujados por el propietario, por lo que debe capturar los mensajes de pintura y hacer que pinten en su propio lienzo.
  • Las cadenas de sugerencias no funcionarán a menos que las consulte manualmente.
  • Necesitará administrar la vida útil de IContextMenu y HMENU y solo liberarlos una vez que se haya cerrado el menú emergente.
+0

Hola Craig, gracias por la información técnica detallada, ¡son muy útiles! Pero tengo que aceptar la respuesta de RRUZ porque me señaló la función DisplayContextMenu de JCL, que hace exactamente lo que quiero con una sola línea ... –

0

Aquí hay un ejemplo de cómo la lógica del sistema operativo detrás del elemento de menú contextual "Enviar a ... destinatario de correo" puede utilizarse desde una aplicación Delphi para abrir el cliente de correo predeterminado, mostrando un nuevo correo con el pasaron archivos (seleccionados) unidos:

How can I simulate ‘Send To…’ with Delphi?

Cuestiones relacionadas