2009-10-17 7 views

Respuesta

16

He hecho una solución rápida para usted. añadir estas unidades a la sección "Aplicaciones":

... ShlObj, ActiveX, ComObj 

y aquí está su procedimiento, acabo de añadir nuevo parámetro "HND" para llevar la manija del TWinControl que va a utilizar para visualizar el menú de contexto.

procedure ShowSysPopup(aFile: string; x, y: integer; HND: HWND); 
var 
    Root: IShellFolder; 
    ShellParentFolder: IShellFolder; 
    chEaten,dwAttributes: ULONG; 
    FilePIDL,ParentFolderPIDL: PItemIDList; 
    CM: IContextMenu; 
    Menu: HMenu; 
    Command: LongBool; 
    ICM2: IContextMenu2; 

    ICI: TCMInvokeCommandInfo; 
    ICmd: integer; 
    P: TPoint; 
Begin 
    OleCheck(SHGetDesktopFolder(Root));//Get the Desktop IShellFolder interface 

    OleCheck(Root.ParseDisplayName(HND, nil, 
    PWideChar(WideString(ExtractFilePath(aFile))), 
    chEaten, ParentFolderPIDL, dwAttributes)); // Get the PItemIDList of the parent folder 

    OleCheck(Root.BindToObject(ParentFolderPIDL, nil, IShellFolder, 
    ShellParentFolder)); // Get the IShellFolder Interface of the Parent Folder 

    OleCheck(ShellParentFolder.ParseDisplayName(HND, nil, 
    PWideChar(WideString(ExtractFileName(aFile))), 
    chEaten, FilePIDL, dwAttributes)); // Get the relative PItemIDList of the File 

    ShellParentFolder.GetUIObjectOf(HND, 1, FilePIDL, IID_IContextMenu, nil, CM); // get the IContextMenu Interace for the file 

    if CM = nil then Exit; 
    P.X := X; 
    P.Y := Y; 

    Windows.ClientToScreen(HND, P); 

    Menu := CreatePopupMenu; 

    try 
    CM.QueryContextMenu(Menu, 0, 1, $7FFF, CMF_EXPLORE or CMF_CANRENAME); 
    CM.QueryInterface(IID_IContextMenu2, ICM2); //To handle submenus. 
    try 
     Command := TrackPopupMenu(Menu, TPM_LEFTALIGN or TPM_LEFTBUTTON or TPM_RIGHTBUTTON or 
     TPM_RETURNCMD, p.X, p.Y, 0, HND, nil); 
    finally 
     ICM2 := nil; 
    end; 

    if Command then 
    begin 
     ICmd := LongInt(Command) - 1; 
     FillChar(ICI, SizeOf(ICI), #0); 
     with ICI do 
     begin 
     cbSize := SizeOf(ICI); 
     hWND := 0; 
     lpVerb := MakeIntResourceA(ICmd); 
     nShow := SW_SHOWNORMAL; 
     end; 
     CM.InvokeCommand(ICI); 
    end; 
    finally 
    DestroyMenu(Menu) 
    end; 
End; 

modificar/añadir la inicialización, sección de finalización como esto

initialization 
    OleInitialize(nil); 
finalization 
    OleUninitialize; 

y aquí cómo se puede utilizar este procedimiento:

procedure TForm2.Button1Click(Sender: TObject); 
begin 
    ShowSysPopup(Edit1.Text,Edit1.Left,Edit1.Top, Handle); 
end; 

espero que esto va a funcionar para usted.

Saludos,

Editar: si desea mostrar el menú contextual para seleccionar más de un archivo this article in my blog

+0

Esto parece un poco incompleto, los mensajes de IContextMenu2 no se manejan, es decir, no se llama a HandleMenuMsg en respuesta a los mensajes del menú. Como resultado, algunos submenús (como el 'Abrir con') no se completarán. [Aquí] (http://stackoverflow.com/a/5287265) es un ejemplo de lo que estoy hablando. –

+0

Además, sin una clase que implemente la interfaz IShellCommandVerb, su parámetro booleano 'Handled' y la interfaz en sí no sirven para nada. Como puede ver en su código, está consultando * nil * si es compatible con la interfaz, por supuesto, nunca se le da la interfaz, simplemente elimine ese lote de código innecesario y la declaración de tipo innecesario. –

+0

Me tomé la libertad de hacerlo yo mismo. Esta afirmación 'if Admite (nil, IShellCommandVerb, SCV) then' realmente se destacaba. Por favor, también corrija el código en su blog. Sin embargo, mi primer comentario sigue siendo válido. –

2

¿Estás seguro de que eso es lo que quieres hacer? Porque si lo hace, efectivamente va a tener que reproducir todo el código en el Shell de Windows y todo su comportamiento e interacciones con una gran cantidad de código.

El menú contextual está básicamente construido por "extensiones de shell". Estas son DLL COM registradas en el sistema. Cuando se invoca el menú contextual, el shell sigue un conjunto de reglas que determinan dónde debe verse (en el registro) para las DLL de extensión.

I found this to be a useful guide to these rules.

Pero encontrar la extensión DLL no es ni la mitad de la historia. Para cada DLL, el intérprete de comandos crea una instancia de los objetos COM registrados por esa DLL y realiza llamadas a los objetos a los que responden las DLL configurando o invocando comandos de menú.

El shell en sí no crea el menú, ni la información necesaria para construir el menú está disponible para ser consultada o leída directamente desde cualquier lugar: el menú está construido completamente de forma dinámica por las extensiones del shell.

El shell transfiere un identificador al menú de cada extensión, junto con cierta información que le indica a la extensión qué ID de comando debe usar para cualquier elemento que agregue a ese menú. La extensión puede agregar casi lo que quiera al identificador de menú que se le da, incluidos los submenús, etc., y bien puede agregar diferentes elementos dependiendo de las propiedades de la selección de archivo actual, no solo de las extensiones de archivo (por ejemplo, el cliente Tortoise SVN agrega diferentes elementos de menú según lo que sea relevante para el estado SVN actual de esos archivos).

Así que si usted quiere construir un menú así, como ya he dicho, tendrá que replicar todo el framework de extensión del shell (o al menos aquellas partes del mismo que inicializan el menú, suponiendo que por alguna razón no luego quiere o necesita invocar los comandos de menú) en su propio código.

Tal vez le ayude si explica por qué desea hacer esto y qué está tratando de lograr. Puede haber una forma más fácil de hacerlo.

+0

parece que puede ser una manera de hacerlo sin replicar todo ese código shell después de todo . Encontré esto pero es un ejemplo .NET. http://www.andrewvos.com/?p=420 – Deltics

0

Aunque estoy de acuerdo con Deltics en que es mucho trabajo, la información requerida para la mayoría (si no todos) de los elementos está disponible gratuitamente en el registro. La guía enumerada en la respuesta de Deltics se ve bien y le dará la mayoría de los elementos. Se puede buscar mucho desde entradas básicas en el registro, mientras que otros necesitan llamadas a objetos COM.

Cuestiones relacionadas