Crear Menu Contextual Windows

Iniciado por rigorvzla, 22 Agosto 2018, 01:33 AM

0 Miembros y 4 Visitantes están viendo este tema.

rigorvzla

Hola amigos, tengo una inquietud, quiero con la apertura de mi aplicacion agregar opciones al menu contextual de windows, lo cual investigando encontre este ejemplo:

  public partial class MainWindow : Window
    {
        private const string MenuName = "Folder\\shell\\Asistente Virtual AIRH";
        public const string Command = "Folder\\shell\\Asistente Virtual AIRH\\command";

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            cargaContextual();
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            cierreContextual();
        }


        private void cierreContextual()
        {
            try
            {
                RegistryKey reg = Registry.ClassesRoot.OpenSubKey(Command);
                if (reg != null)
                {
                    reg.Close();
                    Registry.ClassesRoot.DeleteSubKey(Command);
                }
                reg = Registry.ClassesRoot.OpenSubKey(MenuName);
                if (reg != null)
                {
                    reg.Close();
                    Registry.ClassesRoot.DeleteSubKey(MenuName);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, ex.ToString());
            }
            finally
            {
            }
        }
       
        private void ejecutrable()
        {
            MessageBox.Show("Hola");
        }
        private void cargaContextual()
        {
            RegistryKey regmenu = null;
            RegistryKey regcmd = null;
            try
            {
                regmenu = Registry.ClassesRoot.CreateSubKey(MenuName);
                if (regmenu != null)
                    regmenu.SetValue("", "AV-AIRH");
                regcmd = Registry.ClassesRoot.CreateSubKey(Command);
                if (regcmd != null)
                    regcmd.SetValue("", @"E:\Descargas\a.exe");

            }
            catch (Exception ex)
            {
                MessageBox.Show(this, ex.ToString());
            }
            finally
            {
                if (regmenu != null)
                    regmenu.Close();
                if (regcmd != null)
                    regcmd.Close();
            }
        }
    }
}


tiene unas pequeñas modificaciones y es funcional, el problema radica en lo siguiente, solo funciona en el menu contextual de carpetas NO archivos , tampoco me deja muy claro, como modificar la accion que se establece al hacer click en el , de momento solo abre el archivo "a.exe" pero quisiera que se ejecutara un metodo especifico que yo establezca, quiero tambien en ves de crear un menu se creen varios , estos varios en un submenu, si alguien me puede guiar se lo agradeceria mucho para poder sacarle partido a ese codigo ejemplo que consegui.

Y como nota final, el poder agregar una imagen a esa opcion de menu contextual, como lo hace el MegaAsync claro ejemplo de lo que quiero hacer.

Serapis

- Necesitas conocer un poco aunque sea por encima la estructura del registro...
Por ejemplo, si tinees esto:
Command = "Folder\\shell\\Asistente Virtual AIRH\\command";
y esto:
Registry.ClassesRoot
Básicamente le estás diciendo que la ruta en el registro es:
CitarHKEY_Classes_Root\Folder\...
Donde folder refiere acciones sobre carpetas. Luego para hacerlo sobre ficheros, debes primero saber si es para cualquier fichero o para una (o varias) extensión específica.
Si es una extensión específica, entonces define command, a la ruta específica.
Command = ".exe\\shell\\Asistente Virtual AIRH\\command";
en este ejemplo afecta solo a aquellos ficheros con extensión ".exe". Si deseas que se aplique a cualquier fichero, debes usar el asterisco...

- La acción es lo que tu quieras hacer, pero si es una extensión que no es tuya, no modifiques el registro, antes bien, registra tu propia acción, en general cada extensión específica suele tener asociado unas acciones como "Open" y "Print", no importa el nombre que le dés, si importa el texto que muestres en el menú...
Al final en "command", lo que estás definiendo es el comando que ha de recibir una aplicación, por ejemplo:
"c:\program files\mi programa\chachiprograma.exe /A:a /B /R:%1 /C:20"
Si observas bien, en comand al final indicas la ruta de la aplicación que recibirá el comando... el fichero que se entrega a dicha aplicación está representado por %1, y "/A:a /B /R: /C:20" son parámetros adicionales (de ejemplo) ... más específico el ejemplo: "/R:" indicaría al programa que este parámetro contiene la ruta (tras los dos puntos)...Las barras "/", son para hacer un split en la cadena de comandos recibida y escrutar que parámetros son invocados... los ":", es para hacer un split entre un parámetro y el valor que pueda acompañar a dicho parámetro..., así los parámetros que el afinal el programa debería interpretar serían A, B, C y R (y  a,b, c y r,  si quieres que definan la misma acción)... cada uno representaría lo que uno quisiera que represente ese parámetro (salvo que de ejemplo ya hemos señalado que R sería el que señale la ruta recibida)...

- Sobre más acciones, pués repetir todo el proceso.
Por ejemplo si el programa fuera un compresor-descompresor, la acción anterior podría ser la acción para comprimir, y entonces ese texto sería el que entraríamos en el código:

MenuName = "*\\shell\\Compresor";
Command = "*\\shell\\Compresor\\command";

regmenu = Registry.ClassesRoot.CreateSubKey(MenuName);
                if (regmenu != null)
                    regmenu.SetValue("", "&Comprimir");

regcmd = Registry.ClassesRoot.CreateSubKey(Command);
                if (regcmd != null)
                    regcmd.SetValue("", @"c:\program files\mi programa\compresor.exe /A:a /B /R:%1 /C:20");

y para otra acción, como he dicho es repetir cambiando lo que proceda, supongamos descomprimir. Al descomprimir, no lo haríamos sobre cualquier fichero, si no sobre el tipo que genera nuestro compresor... supongamos la extensión .comp
Supongamos que el parámetro A, fuera una abreviatura de 'accion' y x fuera eXtraer


MenuName = ".comp\\shell\\Compresor";
Command = ".comp\\shell\\Compresor\\command";

regmenu = Registry.ClassesRoot.CreateSubKey(MenuName);
                if (regmenu != null)
                    regmenu.SetValue("", "E&xtraer");

regcmd = Registry.ClassesRoot.CreateSubKey(Command);
                if (regcmd != null)
                    regcmd.SetValue("", @"c:\program files\mi programa\compresor.exe /A:x /R:%1 /Q");

Mira como las rutas ahora se refieren solo a los ficheros con extensión ".comp" el texto ahora en vez de "Comprimir", aparecerá en el menú contextual "Extraer", el cual solo aparecerá si el fichero seleccionado es de tipo ".comp"
Finalmente observa como ha cambiado los parámetros, la "A" de acción ahora tiene una "x", asociada a Extraer, (la "a" de más arriba podría asociarse a "añadir"), el parámetro "Q", podría indicar que se pregunte al usuario si extraer todo o dejar que el usuario decida que quiere extraer..., sin dicho parámetro, por ejemplo podría ser un indicador para: "Extraer Todo"

- Respecto de que aparezcan como submenú... no sé si haya cambiado en win10, pero antes al menos, la única forma de introducir como submenú, exigía escribir una librería, implementando determinadas interfaces... no me he informado si esto ha cambiado (no me ha surgido la necesidad). Así que en teoría no, pero tampoco garantizo que no sea posible actualmente, queda a tu esfuerzo acceder al MSDN e investigar un poco... que no quema.
https://msdn.microsoft.com/es-es/library/microsoft.win32.registrykey(v=vs.110).aspx

NOTA: No he probado el código que has puesto, he supuesto que es correcto, y tan solo he realizado cambios en las partes que corresponden, para que se pasa dónde y cómo. Reformulando todo en funciones, sería mucho menos código que copiar, pegar y hacer cambios... una única función debería valerte para registrar diferentes acciones, pasando la ruta del registro, el verbo y el comando a ejecutar. 

Y recuerda que para ejecutar código sobre el registro se debe contar con los permisos apropiados... podrías recibir una excepción de tipo "Security...", o "UnauthorizedAccess...", así que ejecuta las acciones dentro de un try... catch.

Eleкtro

#2
Cita de: rigorvzla en 22 Agosto 2018, 01:33 AMagregar opciones al menu contextual de windows

Hay dos formas de llevarlo a cabo, la primera manera, que es la más sofisticada y versatil sería mediante el desarrollo de una extensión de la shell (o shell-extension) del menú contextual, lo cual requiere el uso de C++, o de wrappers de C++ para .NET como sería la librería SharpShell, y la segunda manera, que es mucho más simple de llevar a cabo pero también mucho menos sofisticada sería añadiendo ciertas claves en el registro de Windows. Cabe mencionar que una shell-extension basicamente funciona mediante la registración de interfaces y adición de ciertas claves en el registro de Windows, pero es un procedimiento mucho más sofisticado que nada tiene que ver con la forma más simple de añadir las claves al registro de Windows.

Ambas metodologías implican ventajas y desventajas, tú te has decidido por la segunda metodología, y está bien, ya que a menos que necesites controlar la carga de multiples archivos en una única y misma instancia de tu programa entonces no deberías considerar el desarrollo de una shell-extensión para el menú contextual.




Cita de: rigorvzla en 22 Agosto 2018, 01:33 AMel problema radica en lo siguiente, solo funciona en el menu contextual de carpetas NO archivos

Es evidente, puesto que estás manipulando la clave "HKEY_CLASSES_ROOT\Folder", y como su nombre indica por si mismo, "Folder" significa carpeta...

Aparte de eso, lo estás haciendo incorrectamente, debes manipular la clave "HKEY_CLASSES_ROOT\Directory" en su lugar, puesto que una carpeta (o folder) por definición también puede ser una carpeta virtual, mientras que un directorio (o directory) no lo es.

Para añadir comandos al menú contextual de archivos, de todos los tipos de archivo, puedes manipular la clave: "HKEY_CLASSES_ROOT\*"

Para añadir comandos al menú contextual de un tipo de archivo específico, hay diferentes metodologías para hacerlo, pero basicamente debes encontrar (o crear) la asociación de la extensión del archivo en el registro de Windows y añadir allí las claves para crear el nuevo comando del menú contextual, por ejemplo digamos que quieres que el menú sea visible solamente al hacer click derecho sobre archivos de texto plano (extensión .txt), entonces la clave sería, por lo general, "HKEY_CLASSES_ROOT\txtfile".




Cita de: rigorvzla en 22 Agosto 2018, 01:33 AMquiero tambien en ves de crear un menu se creen varios , estos varios en un submenu

En otras circunstancias podrías seguir estas indicaciones:

Pero en tu caso en particular me parece que necesitarás desarrollar una shell-extension para permitir la adición de menues en cascada. SharpShell es sencillo de usar, y el autor provee tutoriales y muestras de código en C#, así que no deberías tener problema alguno documentandote y practicando hasta que lo consigas.




Cita de: rigorvzla en 22 Agosto 2018, 01:33 AMpoder agregar una imagen a esa opcion de menu contextual

Simplemente creando la entrada "Icon" de tipo string (REG_SZ) donde especificarás la ruta absoluta del icono a mostrar, seguido de una coma y el índice del icono.

Aquí abajo te muestro a modo de script de Regedit, como sería la estructura de un menú contextual para todos los tipos de archivo, con dos comandos y con iconos.

Código (ini) [Seleccionar]
Windows Registry Editor Version 5.00

; ProgramName
[HKEY_CLASSES_ROOT\*\shell\ProgramName]
"MUIVerb"="ProgramName"
"SubCommands"="ProgramName.RunProgram;ProgramName.LoadFile"
"Icon"="\"C:\\Program Files (x86)\\ProgramName\\ProgramName.exe\",0"
"Position"="Middle"

; ProgramName.RunProgram
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\ProgramName.RunProgram]
@="Open ProgramName"
"Icon"="\"C:\\Program Files (x86)\\ProgramName\\ProgramName.exe\",0"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\ProgramName.RunProgram\command]
@="\"C:\\Program Files (x86)\\ProgramName\\ProgramName.exe\""

; ProgramName.LoadFile
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\ProgramName.LoadFile]
@="Load File In ProgramName"
"Icon"="\"C:\\Program Files (x86)\\ProgramName\\ProgramName.exe\",0"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\ProgramName.LoadFile\command]
@="\"C:\\Program Files (x86)\\ProgramName\\ProgramName.exe\" \"%1\""


Tan solo tienes que reproducirlo en C#, lo cual imagino que ya sabrás hacer habiendo mostrado el código de manipulación del registro que has mostrado en tu post...

Saludos.








rigorvzla

Nebire , muchisimas gracias, se nota que se te da muy MUY bien esto de modificacion de registro, que explicacion tan genial muchas gracias.

Elektro, como siempre de atento muchisimas gracias por tu repuesta, ambas me has sido de gran GRAN ayuda y la partida para lograr lo que tengo en mente, espero cualquier duda a aposterior, puedan aclararla, feliz dia al regresar de mis labores me ponddre de inmediato.

rigorvzla

#4
Muy bien, eh logrado ya entender en parte la ubicacion del regustro y como crear el boton para poder hacer click , mas no logro como hacer para que ejecute un metodo, me explico mejor, si coloco en  la ruta del archivo un "archivo.exe" lo ejecuta sin mas, pero si coloco otra extension "archivo.pdf" no lo hace (esto es una curiosidad de saber el porque), el problema que si amerito resolver es , que al hacer click ahi , me ejecute un (mensaje de hola) un metodo, (Comprimir() ; ) pero al colocarlo me da un error de object el cual no entiendo aun .

void Comprimir()
{
lineas de compresion //ejemplo, basicamente el metodo que yo quiera
}

Espero puedan entender lo que quiero explicar, ya al tener eso me quedara la creacion de submenus y la colocacion de la imagen que ya me fue explicado con una buena teoria , debere ahora aplicar la practica jeje...

Serapis

#5
A ver... creo que no acabas de entenderlo del todo.

Estás en el registro en la rama Classes... esa rama en específico, de lo que versa es de definir que PROGRAMAS, manejan qué extensiones y qué acciones contiene cada extensión de fichero (así de modo muy resumido, pero espero que suficientemente claro).

Es decir, al indicarle una ruta hacia un .exe, lo que le estamos diciendo, es que es ese programa (el de la ruta) quien será el encargado de hacer las acciones (verbo) que correspondan al fichero/s recibido ( %1).  Además también te he explicado que el programa puede recibir parámetros adicionales predefinidos, más allá de solo la ruta.

...por tanto, en la ruta no puede haber un *.pdf, porque ese fichero no es un ejecutable, ese fichero es un fichero de datos.

Por defecto, la acción "Open", lo que hace es abrir con el programa asociado (.exe), una determinada extensión...
Por ejemplo los ficheros ".pdf" se abrirán (si no se ha cambiado la asociación), con el programa "acrobat reader" de Adobe...
Si tu programa va a remplazar la acción de abrir un pdf, tu programa.exe podría sobrescribir la acción "Open", en cambio si vas a realizar una acción muy distinta de abrirlo (imagina que pretendes buscar si dentro del pdf, existe determinado texto, pero no muestras el contenido al usuario, solo te limitas a decir " 'éste pdf', contiene x veces el texto buscado", entonces si no vas a mostrar el contenido del pdf, la acción no debiera sobrescribir "Open", si no añadir una acción "Buscar x texto", y uno de los parámetros podría ser el texto que se busca...

En resumen: la ruta el ajecutable, lo que le dice es qué programa hará el trabajo deseado, por tanto, sí o sí, tiene que ser un ejecutable (.exe, .bat, .dll pero las librerías tienen otro proceso de registro diferente (shellex de "shell EXtension")) pero la ruta del fichero seleccionado que recibe el shell se recoje en el parámetro "%1", y éste se considera un dato... vamos que por algo es un parámetro, incluso aunque el fichero sea una extensión .exe...

Un ejemplo, un programa que calcula un hash (da igual que tipo de hash) a un fichero recibido... el programa que calcula el hash debe ser un ejecutablem, si o sí... pero el fichero al que calcula el hash, puede ser cualquiera, incluso un .exe, un pdf, etc... así la línea de comandos que el shell debe enviar al programa y el programa al que se debe enviar, viene declarado en la línea de "command"
"c:\program files\Hash\Hash.exe %1 /T:md5"
Así explicando la línea... existe un programa en la ruta: c:\program files\Hash\
que se llama: Hash.exe
y que al parecer es capaz de calcular diferentes tipos de hashes, si se invoca esa línea en concreto, estaría reclamando calcular el MD5 (se supone esa es la idea del parámetro T:md5
Y lo calcula al fichero (sea el que sea), que figura en la ruta: %1, que es el fichero que el usuario ha pinchado, para abrir el menú contextual.

Y ese simple ejemplo, es trtasladable a cualquier otra acción.

Si tú no tienes nada que hacer con ningún fichero, entonces no tiene sentido que guardes en el registro acciones sobre ficheros... si tu por ejemplo simplemente quieres que un programa diga hola... debes crear ese programa, y que el programa cuando arranque diga "Hola", en el registro, tu acción podría ser "Saludar" (y ese podría ser el texto a mostrar en el menú contextual, y la línea de comando podría ser tal que así:
"c:\program files\Miprogramasaludador\Saludos.exe"
como no hace nada sobre ningún fichero, ni siquiera es preciso ponerle el %1. El usuario simplemente usará un fichero cualquiera (si registras la acción bajo HKEY...classes\*\... ), para que aparezca el menú contextual y muestre ahí la opción "Saludar"
Si no existe el fichero Saludos.exe el registro intentará ver si hay otro programa adicional asociado, si tu programa luego no hace lo que dice la acción "Print", pero no imprime nada, es problema de tu programa o estás engañando al usuario ó si no hay impresora el programa que trata de imprimir, verificará que no hay n¡instalada una impresora o que no está lista y avisará o gebnerará un error.
Si la ruta ni siquiera deriva a un ejecutable... pués cantará el error que te canta. Eso es un error de no saber que estás haciendo, y que espera el registro que introduzcas.

El registro después de todo, es simplemente una base de datos, con una jerarquía específica y la shell completa, es esa misma base de datos con librerías que permiten manipularla... cuenta con algunas librerías para poder manipular la base de datos, y una interfaz de usuario (el emnú contextual en sí mismo), pero por sí mismo, el registro no ejecuta nada, simplemente almacena datos (como cualquier otras base de datos), y el sistema lo que hace es permitir al programador guardar datos, leer datos, borrar datos, modificar datos, pero bajo una funcionalidad apiñada en algunas librerías en vez de manipularla a través de SQL, u otro sistema y al usuario le permite seleccionar objetos mostrar opciones y enviar la opción pulsada...

...en definitiva... ¿entiendes que es tu programa, el que al final tiene que hacer la acción que quieras llevar a cabo, y que el menú contextual, sólo es un vehículo/intermediario/facilitador/vínculo para dirigir una acción del usuario (auspiciando una simple interfaz de usuario en modo de lista de menús) hacia tu programa?.

Dicho de otro modo (aunque es lo mismo, por tercera o quinta vez). El menú contextual, solo provee al usuario de opciones entre las que elegir, que se toman del registro en función del tipo de fichero seleccionado y las acciones registradas para ese tipo de fichero (incluso virtual (enlaces simbólicos), como explicaba Elektro al diferenciar "Folder" de "Directory") y las redirige al programa/librería que tiene asociado la acción elegida por el usuario.... y es ese programa quién hará lo que tenga que hacer,
por ejemplo un programa de imagen registrará extensiones de imagen y pondrá verbos como Open, Print, Edit, Send To, etc... para visualizar, imprimir, alterar la imagen enviarla por correro, etc...


rigorvzla

Entendido a mil, ya comprendo ahora que lo que quiero hacer no es por este medio, ya entendi que el hecho de crear un menu es para vincular una apliccion externa ya exe y asi poder manipularlo , MAS NO hacer referencia a algun metodo ya creado en el mismo programa.

Partiendo de esto, existe alguna manera para crear lo que quiero en caso de ser asi o conocer un metodo, podrias decirme de donde deberia partir para empezar a investigar y documentarme, teniendo en cuenta el SharpShell que me recomendi elektro el cual empezare a ver ya mismo , conocerlo y ver que provecho aprender de ello.

Gracias por tu explicacion clara y concisa , muchas gracias.

Serapis

...ya, pero es que me queda claro que no haces bien, en cambio no tanto que es lo que realmente pretendes hacer...

Si te explicas con claridad diciendo que tratas de hacer (sin vincularlo al registro, durante la explicación), ya veré yo si lo que reclamas se puede hacer con el registro (o si hay algo más directo y efectivo), y en cuyo caso te señalaría cómo...

rigorvzla

Entendido te explico q quiero hacer, yo quiero que al ejecutar mi aplicacion , se cree en el menu contextual de windows una opcion que ejecute cierta accion (metodo del mismo proyecto) .

Digamos que quiero que se cree un "boton" en el menu contextual en archivos especificos y  en carpetas, y yo poder asignar la accion de ese "boton" creado en el menu contextual de windows.

Imagina el menu contextual de WinRar que tiene diferentes opciones en el menu contextual de windows, bueno quiero crear eso... Aplicado a mi programa tal cual un icono y diferentes acciones que tiene el mismo winrar.

Eleкtro

#9
Cita de: rigorvzla en 24 Agosto 2018, 12:57 PM
Espero puedan entender lo que quiero explicar, ya al tener eso me quedara la creacion de submenus y la colocacion de la imagen que ya me fue explicado con una buena teoria , debere ahora aplicar la practica jeje...

Cita de: NEBIRE en 25 Agosto 2018, 03:19 AM
...ya, pero es que me queda claro que no haces bien, en cambio no tanto que es lo que realmente pretendes hacer...

Si te explicas con claridad diciendo que tratas de hacer (sin vincularlo al registro, durante la explicación), ya veré yo si lo que reclamas se puede hacer con el registro (o si hay algo más directo y efectivo), y en cuyo caso te señalaría cómo...

A mi tampoco me queda nada claro.




Cita de: rigorvzla en 24 Agosto 2018, 12:57 PMno logro como hacer para que ejecute un metodo, me explico mejor, si coloco en  la ruta del archivo un "archivo.exe" lo ejecuta sin mas, pero si coloco otra extension "archivo.pdf" no lo hace (esto es una curiosidad de saber el porque)

No comprendo que complicación le encuentras, tú en tu programa puedes elegir el método que quieras utilizar con la ruta del archivo/directorio que se envía por linea de comandos a tu programa.

Tampoco se si te refieres a eso, no te explicas demasiado bien. Si lo que quieres es controlar las acciones a llevar a cabo sobre el archivo/directorio seleccionado mediante el uso de una librería en vez de un programa executable, entonces necesitas desarrollar una shell-extensión y registrar la .dll generada en el sistema operativo.




Cita de: rigorvzla en 25 Agosto 2018, 01:42 AM
Entendido a mil, ya comprendo ahora que lo que quiero hacer no es por este medio, ya entendi que el hecho de crear un menu es para vincular una apliccion externa ya exe y asi poder manipularlo , MAS NO hacer referencia a algun metodo ya creado en el mismo programa.

Cita de: rigorvzla en 25 Agosto 2018, 04:41 AMquiero que al ejecutar mi aplicacion , se cree en el menu contextual de windows una opcion que ejecute cierta accion (metodo del mismo proyecto) .

Para eso es que existen los argumentos por linea de commandos...

Al crear un comando en el menú contextual para archivos o directorios, puedes especificar argumentos que enviarle a tu programa (aparte de la ruta del archivo/directorio selecciona, claro está). Por ejemplo:

Código (ini) [Seleccionar]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\ProgramName.LoadFile\command]
@="\"C:\\Program Files (x86)\\ProgramName\\ProgramName.exe\" -Argumento1 -Argumento2 \"%1\""


Logicamente debes controlar los argumentos en tu aplicación. Aparte de la densa información que puedes encontrar con Google en la MSDN / Microsoft docs.com, aquí tienes algo mas breve:


Saludos