Crear Menu Contextual Windows

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

0 Miembros y 1 Visitante están viendo este tema.

Serapis

Saqué un poco de tiempo anoche para hacer un sencillo ejemplo, pero se me hizo tarde para postearlo aquí, y ponerlo sin comentarios no me parecía adecuado...

Me falta crear un fichero reg, que sirva de apoyo al ejemplo, para hacer el registro de las claves que se van a necesitar. Este lo pondré a la noche cuando regrese del curro...




Para el ejemplo vamos a considerar que se tratara de un programa que puede comprimir y descomprimir (que suelen tener muchos parámetros disponibles y por ello más complejos de lo habitual)... debe servir para entender ampliamente las formas de proceder (que no son fijas, ni inalterables, solo instructivas). Y supuestamente... dicho programa de ejemplo generaría ficheros con extensión *.comp

Al comienzo hay que considerar que acciones vamos a necesitar y qué parámetros precisa cada una.
Si tenemos ya el código que hace cada acción, los parámetros pueden ser los mismos que toman las funciones, y solo resta ver de escoger la letra/s más propicia para cada acción. Si son pocas acciones las que vamos a registrar, no hace falta comerse mucho la cabeza pero sin muchas, habrá que pararse en ello, más tiempo y meditarlo bien....

Algunas aclaraciones previas para no liarnos:
Los parámetros que aceptamos, no distinguimos mayúsculas de minúsculas pero eso al gusto, si el programa hubiera de proveer tropecientos parámetros, quizás haya que repensarlo, aunque es fácil que los usuarios no reparen en la distinción de tal circunstancia... es preferible evitar parecidos confusos.

Cuando solo lleva un parámetro o dos, no hace falta mucho 'guiso' con ellos, pero cuando van a ser varios, es preferible que tengan un formato que podamos entender y manipular fácilmente.

Como en las líneas de comando, está a la orden del día que se incluyan rutas, debes parar a considerar que ciertos caracteres no son válidos para las rutas, precisamente por que se utilizan para acciones sobre las rutas... por ejemplo nunca podrás poner un '*' al nombre de un fichero o carpeta, porque el asterisco es un 'comodín' para nombres de ficheros... esos caracteres prohibidos en las rutas, son precisamente los más aptos y los que nos han de servir para realizar nuestro formato. Los que programan en 'C', tienen el gran incordio de las dobles barras '\\' como separadores de rutas, precisamente por ser un escollo... harmeos uso del carácter ":" que como también foramn parte de las rutas, y por elegir hacer 'splits', igualmente nos vemos forzados s poner 2... podríamos haber elegido otro modelo para el formato, pero así se aprenden más cosas por el camino.

Y ahora al grano, cada parámetro irá antecedido de la barra ' /' y detrás irá la letra o letras que dan nombre al parámetro y que será lo que el programa reconozca. Cuando un parámetro deba llevar adjunto además de un valor, irá en la forma:
/parametro::valor
Ejemplos:
/ruta::C:/program files/compresor/ejemplo.txt
/T::3
/S::<<<--->>>


Luego en el programa se escruta la línea de comandos recibida...
- Iniciamente se hace un split, generando así un array con todos los parámetros y ya libres de la barra, porque cortamos justamente por ella (fijarse que lleva un espacio delante, para impedir que se corte por la barra de las rutas). Por tanto se exige, que cada parámetro vaya antecedido de " /"...
- Los parámetros son flexibles, no se exige que haya un orden concreto en la entrada de los parámetros, excepto para el parámetro que marca la orden-acción a ejecutar, que debe ser el primer parámetro.
De no ser asi, complica el asunto (innecesariamente pués antes que nada, habría que recorrer todos los parámetros para buscar la orden y retirarla, para luego investigar que parámetro acompañan a dicha orden).

- El primer parámetro se exige que sea la acción-orden que se reclama llevar a cabo. Cada tipo de orden llevará asociado sus propios parámetros, e ignorará los que no le competen (no marcará error si aparece un parámetro que no entiende, una vez quede clara la orden a ejecutar).
Al diseñar los parámetros (el string dedicado a cada uno), sin embargo los específicos de una orden no deberían interferir con los de otra,  es decir, los usados para una orden o significan lo mismo para otra orden o mejor usar otra 'letra-palabra', para no confundir al usuario del programa ni producirle amargas experiencias...
Es decir si /t significa Tipo, para una acción conviene que signifiqwue Tipo para todas las acciones, hacer que signifique 'Todo' para otra acción facilita que un posible usuario cometa errores y no se los aprenda.

Como decía, el programa de ejemplo consideramos que se tratara de un programa que puede comprimir y descomprimir, estas son las órdenes admitidas (evidentemente las que uno quiera tratar), yo he elegido estas de ejemplo y nótese que aunque en el registro se pondrá solo una letra, si se invoca por ejemplo desde un bat o desde una ventana de scripts (cmd, powershell, etc...) puede pasarse cualquiera de los valores señalados para la acción:

/h, /?, /Help, /Ayuda          
Propósito: mostrar info de ayuda
Si aparece, se ignora el resto de parámetros que hubiere...

La ayuda se invoca también si aparece un parámetro como orden, que no se reconoce...(pero solo si aparece como ORDEN)
...o si faltan parámetros obligatorios para una orden específica. En esete caso se invoca justo desde donde se escruta la orden.

/i, /Info                      
Propósito: Reclama información sobre un fichero comprimido.
Razonablemente exige que la ruta recibida sea un fichero de tipo ".comp"
Parámetros adicionales para esta orden:
          /r::ruta /Ruta::ruta
                     Obligatorio, Especifica la ruta de entrada, debe ser un  fichero de tipo '.comp'...
                     Ejemplo: /r::c:/Fotos/primavera 2020/primavera.comp
          /k, /Comment, /Comentario  
                     Opcional, Señala que se muestre el comentario del archivo si lo tiene.

     
/t, /Test, /v, /Verificar, /Verify  
Propósito: Reclama verificar si el archivo está bien o resulta dañado.
Puede solicitarse distintos niveles de verificación.
Parámetros adicionales para esta orden:
          /r::ruta /Ruta::ruta        
                     Obligatorio, Especifica la ruta de entrada, debe ser un  fichero de tipo '.comp'...
                     Ejemplo: /r::c:/Fotos/primavera 2020/primavera.comp
          /n::numero /Nivel::numero    
                     Opcional, señala el nivel de verificación que se lleva a cabo. Por defecto, solo evalúa la cabecera.

/l, /Listar                    
Propósito: Reclama listar el contenido de un fichero comp.
La ruta de entrada es obligatoria, el separador y la ruta de destino opcionales (si no se acompaña de ruta de destino, se vuelca en la pantalla, si la trae se vuelca al fichero).
          /r::ruta /Ruta::ruta          
                     Obligatorio, Especifica la ruta de entrada, debe ser un  fichero de tipo '.comp'...
                     Ejemplo: /r::c:/Fotos/invierno 2020/invierno.comp
          /d::ruta, /Destino::ruta      
                     Opcional, Especifica una ruta de destino donde volcar el listado. No debe existir.
                     Ejemplo: /r::c:/Fotos/invierno 2020/invierno.txt
          /s::string, /separador::string  
                     Opcional, Separador usado para generar el listado, si se omite se usa 'CR+LF'


/e, /Extraer, /Extract    
Propósito: Reclama la extracción de todo o parte del contenido.
          /r::ruta /Ruta::ruta          
                     Obligatorio, Especifica la ruta de entrada, debe ser un  fichero de tipo '.comp'...
                     Ejemplo: /r::c:/Fotos/invierno 2020/invierno.comp
          /d::carpeta, /Destino::carpeta
                     Obligatorio, Especifica una Carpeta de destino donde extraer el contenido. No debe existir (no sobrescribe).
                     Ejemplo: /r::c:/Fotos/invierno 2020/
          /p::patron, /Pattern::patron, /Patron::patron
                     Opcional, especifica un patrón que señala que se va a extraer.
                     Ejemplo: /p::*.jpg     (apunta a todas las imágenes jpg)
                     Ejemplo: /p::*vacaciones*.jpg   /apunta las imágenes jpg, que contengan 'vacaciones' en el nombre)
                     Ejemplo: /p::-20   (extrae las 20 últimos ficheros, si hay menos pués todos).

/a, /Add, /Añadir        
Proposito: Reclama comprimir y añadir ficheros a un archivo ya existente.
Podría llevar más parámetros para indicar otras opciones, pero es solo un ejemplo...
          /r::ruta /Ruta::ruta          
                     Obligatorio, Especifica la ruta de entrada, debe ser un  fichero de tipo '.comp'...
                     Ejemplo: /r::c:/Fotos/invierno 2020/invierno.comp
          /f::ruta /File::ruta, /Fichero::ruta
                     Obligatorio, Especifica la ruta del fichero a comprimir, debe existir...
                     Ejemplo: /r::c:/Fotos/invierno 2020/vacaciones en Marte 1254.jpg
          /t::numero, /Type::numero, /Tipo::numero    
                     Opcional, Determina que tipo de compresión quiere usarse... (un valor 0, no comprime solo archiva)

/c, /Compress, /Comprimir      
Propósito: Reclama crear un nuevo fichero comprimido, partiendo de la ruta recibida.
Puede ser un fichero o una carpeta. Si es una carpeta, comprime (supuestamente) todos los ficheros en la raíz de dicha carpeta (por defecto), y un parámetro adicional señala si comprime recursivamente.
Es común que un archivo comprimido pueda incorporar un comentario, pero no es normal que dicho comentario esté contenido sobre el registro (sería el mismo para todos los archivos). Pero si podría crearse otra acción para investigar si un archivo *.comp no tiene comentario y en tal caso abrir una ventanita para añadirle uno... pero.... es solo un ejemplo.
          /r::ruta, /Ruta::ruta          
                     Obligatorio. Especifica la ruta de entrada, donde está el fichero/s que se van a comprimir (puede ser un fichero o una carpeta).
                     Ejemplo: /r::c:/Fotos/invierno 2020/
                     Ejemplo: /r::c:/Fotos/invierno 2020/vacaciones en Marte 1254.jpg
          /t::x, /Type::x, /Tipo::x    
                     Opcional, Determina que tipo de compresión quiere usarse...
          /n,  /Nest, /Recursivo, /Anidado
                     Opcional, Si es una carpeta, indica si también se comprimirá todo el contenido de las subcarpetas que contenga.

Y listo... ese será todo lo que el programa habrá de reconocer... 7 órdenes-acciones distintas, cada una con sus propios parámetros, en el registro habran de introducirse todas esas accciones... muchas irán solamente sobre ficheros *.comp, y otras sobre cualquier fichero y carpeta...  



Ahora pasamos a la parte del código... yo había elegido windows forms, pero como al final no se hará con la interfaz, lo cambié a una aplicación de consola y como bien dice Elektro, no estoy muy puesto en WPF.
WPF lo veo más bien como la evolución dentro de las empresas de 'superespecialización', donde al final habrá programadores especializados solo en shell, solo en menús, solo en interfaces, solo en red, solo en seguridad, solo en... yo soy contrario a ese ideal empresarial, creo que un programador debe ser todoterreno, completo, no localizado en una porción minúscula dentro de un región, dentro de... y debe saber de casi todo, dominar al menos varios temas (todos es imposible), y tener amplios y profundos conocimientos en otros varios temas más, aunque también habrá otros temas que a uno no le parezcan nada atractivos y le queden lejanos... es razonable.

También te aclaro, que el espacio de nombre My (en Visual Basic), ofrece mayor funcionalidad de cara a operar con la línea de comandos que recibe el programa, sin embargo... cuanto a menor nivel de objetos se dé el ejemplo, más fácil será de trasladar a cualquier otro lenguaje, especialmente si no se tiene gran conocimiento de los objetos específicos de un lenguaje... así que predomina el uso del string... y los pocos métodos que uso son autoentendibles aunque varíen ligeramente en otro lenguaje.

He elegido una aplicación de consola, toda vez que no vamos a hacer nada con una interfaz, si se plasma el código en una aplicación windows forms, el código sobre Main, debe trasladarse al Sub New del form1, y con lo mismo las llamadas a 'console.writeline' pueden ser derivados hacia 'messagebox.show()'... y listo...
Por lo demás mientras se prueba mejor en modo debug, y meter la línea de comandos sobre "opciones de inicio, en la ficha "depurar" de la ventana de "Propiedades del proyecto" (el punto donde se localice puede variar según la versión de VS y lenguaje específico que uno tenga, pero vamos bucenado sobre el IDE, debe uno acabar por encontrarlo. Adjuntaré imágenes de todos modos...


Código (vbnet) [Seleccionar]

Sub Main()
       ' Si no hay línea de comandos, y fuera un programa con interfaz gráfica, debería abrir la aplicación por la ventana por defecto...
       If (Command.Length > 0) Then
           Call ProcesarLineaDeComandos()
           ' si fuera una aplicación de windows forms,
           'End  (y retirar el END de más abajo).
       End If
       End
En sub


En Main, lo dejamos claro y simple... si la línea d ecomandos ha recibido algo, se manda procesar y al término da por finalizada la sesión.

Ahora el código en dicha función que es la que evalúa todo el trabajo de proceso de la línea de comandos (pero no ejecuta ninguna orden ahí, cada orden se deriva a la función específica...
Básicamente esta es la función de la que te debes empapar bien... y tratar de comprenderla al 100%.
Date cuenta que se podría optimizar mucho, pero posiblemente a riesgo de quedar más oscuro para los que no dominen VB... así creo que incluso alguien que apenas sepa nada de Vb, sería capaz de entenderlo todo o casi todo...
Código (vbnet) [Seleccionar]

Private Sub ProcesarLineaDeComandos()
       Dim sepCommand(0) As String        
       Dim cmd() As String
       Dim SubParam() As String
       Dim k As Integer, n As Integer = 0
       Dim Param As String, Valor As String = ""
       Dim faltaParam As String = "No se puede procesar el comando. Falta uno o más parámetros obligatorios "

       Dim rutaOrg As String = "", rutaDst As String = ""
       Dim separador As String = vbCrLf, Comentario As String = "", patron As String = ""
       Dim nivel As Integer = 0, tipo As Integer = 0
       Dim tComentario As Boolean = False, recursivo As Boolean = False

       sepCommand(0) = " /"
       If Command.StartsWith(" ") = False Then
           Param = " " & Command()
       Else
           Param = Command()
       End If
       cmd = Param.Split(sepCommand, StringSplitOptions.RemoveEmptyEntries)
       sepCommand(0) = "::"

       ' Puede limitarse los parámetros a usar un único carácter en vez de todo un texto, remplazando
       '  las líneas: If (Param.Contains(sepCommand)) Then
       '  por la siguiente If (Param.Length > 1) Then

       Param = cmd(0).ToUpper
       Select Case Param  ' la orden, se exige que sea el primer parámetro, y no lleva valor detrás.
           Case "I", "INFO"
               For k = 1 To cmd.GetUpperBound(0)
                   Param = cmd(k).ToUpper

                   If (Param.Contains("::")) Then
                       SubParam = Param.Split(sepCommand, StringSplitOptions.RemoveEmptyEntries)
                       If (SubParam.Length > 1) Then
                           Param = SubParam(0) : Valor = SubParam(1)
                       Else
                           Param = "-"
                       End If
                   End If

                   Select Case Param
                       Case "R", "RUTA"
                           rutaOrg = Valor
                       Case "K", "COMMENT", "COMENTARIO"
                           tComentario = True
                   End Select
               Next

               ' Esta orden, contiene todo lo necesario?
               If (rutaOrg.Length > 0) Then
                   Call Informar(rutaOrg, tComentario)
               Else
                   Call ShowAyuda(faltaParam & " /Ruta, es obligatorio para /Info")
               End If
           Case "L", "LISTAR", "LIST"
               For k = 1 To cmd.GetUpperBound(0)
                   Param = cmd(k).ToUpper

                   If (Param.Contains("::")) Then
                       SubParam = Param.Split(sepCommand, StringSplitOptions.RemoveEmptyEntries)
                       If (SubParam.Length > 1) Then
                           Param = SubParam(0) : Valor = SubParam(1)
                       Else
                           Param = "-"
                       End If
                   End If

                   Select Case Param
                       Case "R", "RUTA"
                           rutaOrg = Valor
                       Case "D", "DESTINO"
                           rutaDst = Valor
                       Case "S", "SEPARADOR"
                           separador = Valor
                   End Select
                   Valor = ""
               Next

               ' Esta orden, contiene todo lo necesario?
               If (rutaOrg.Length > 0) Then
                   Call ListarContenido(rutaOrg, separador, rutaDst)
               Else
                   Call ShowAyuda(faltaParam & " /Ruta, es obligatorio para /Listar")
               End If

           Case "T", "V", "TEST", "VERIFY", "VERIFICAR"
               For k = 1 To cmd.GetUpperBound(0)
                   Param = cmd(k).ToUpper

                   If (Param.Contains("::")) Then
                       SubParam = Param.Split(sepCommand, StringSplitOptions.RemoveEmptyEntries)
                       If (SubParam.Length > 1) Then
                           Param = SubParam(0) : Valor = SubParam(1)
                       Else
                           Param = "-"
                       End If
                   End If

                   Select Case Param
                       Case Is = "R", "RUTA"
                           rutaOrg = Valor
                       Case "N", "NIVEL"
                           nivel = Integer.Parse(Valor)
                   End Select
               Next

               ' Esta orden, contiene todo lo necesario?
               If (rutaOrg.Length > 0) Then
                   Call Verificar(rutaOrg, nivel)
               Else
                   Call ShowAyuda(faltaParam & " /Ruta, es obligatorio para /Verificar")
               End If

           Case "E", "EXTRAER", "EXTRACT"
               For k = 1 To cmd.GetUpperBound(0)
                   Param = cmd(k).ToUpper

                   If (Param.Contains("::")) Then
                       SubParam = Param.Split(sepCommand, StringSplitOptions.RemoveEmptyEntries)
                       If (SubParam.Length > 1) Then
                           Param = SubParam(0) : Valor = SubParam(1)
                       Else
                           Param = "-"
                       End If
                   End If

                   Select Case Param
                       Case "R", "RUTA"
                           rutaOrg = Valor
                       Case "D", "DESTINO"
                           rutaDst = Valor
                       Case "P", "PATTERN", "PATRON"
                           patron = Valor
                   End Select
               Next

               ' Esta orden, contiene todo lo necesario?
               If ((rutaOrg.Length > 0) And (rutaDst.Length > 0)) Then
                   Call Extraer(rutaOrg, rutaDst, patron)
               Else
                   Call ShowAyuda(faltaParam & " /Ruta o /Destino (ó ambas) son obligatorias para /Extraer uno o más ficheros")
               End If

           Case "C", "COMPRIMIR", "COMPRESS"
               For k = 1 To cmd.GetUpperBound(0)
                   Param = cmd(k).ToUpper

                   If (Param.Contains("::")) Then
                       SubParam = Param.Split(sepCommand, StringSplitOptions.RemoveEmptyEntries)
                       If (SubParam.Length > 1) Then
                           Param = SubParam(0) : Valor = SubParam(1)
                       Else
                           Param = "-"
                       End If
                   End If

                   Select Case Param
                       Case "R", "RUTA"
                           rutaOrg = Valor
                       Case "T", "Tipo", "TYPE"
                           tipo = Valor
                       Case "N", "RECURSIVO", "ANIDADO", "NEST"
                           recursivo = True
                   End Select
               Next

               ' Esta orden, contiene todo lo necesario?
               If (rutaOrg.Length > 0) Then
                   Call Comprimir(rutaOrg, tipo, recursivo)
               Else
                   Call ShowAyuda(faltaParam & " /Ruta, es obligatorio para /Comprimir")
               End If

           Case "A", "ADD", "AÑADIR"
               For k = 1 To cmd.GetUpperBound(0)
                   Param = cmd(k).ToUpper

                   If (Param.Contains("::")) Then
                       SubParam = Param.Split(sepCommand, StringSplitOptions.RemoveEmptyEntries)
                       If (SubParam.Length > 1) Then
                           Param = SubParam(0) : Valor = SubParam(1)
                       Else
                           Param = "-"
                       End If
                   End If

                   Select Case Param
                       Case "R", "RUTA"
                           rutaOrg = Valor
                       Case "F", "FILE", "FICHERO"
                           rutaDst = Valor
                       Case "T", "Tipo", "TYPE"
                           tipo = Valor
                   End Select
               Next

               ' Esta orden, contiene todo lo necesario?
               If ((rutaOrg.Length > 0) And (rutaDst.Length > 0)) Then
                   Call Añadir(rutaOrg, rutaDst, tipo)
               Else
                   Call ShowAyuda(faltaParam & " /Ruta o /Fichero (ó ambas) son obligatorias para /Añadir un nuevo fichero al archivo comprimido.")
               End If

           Case "H", "?", "HELP", "AYUDA"
               Call ShowAyuda("")

           Case Else ' una orden con la que el programa no tiene 'compromiso' (quizás si en una versión más actual).
               Call ShowAyuda("No se reconoce el comando recibido (para la versión actual), se muestra la forma correcta de invocar el programa...")
       End Select
   End Sub


Como se puede ver cada acción realiza básicamente lo mismo... chequea cada parámetro aparecido en un bucle donde se pone como trampa cada parámetro que dicha acción admite, si aparece toma el valor que contenga si aparece un parámetro 'extraño' qued aignorado...
Luego se verifica que los parámetros obligatorios para la orden solicitada existan todos, si no se aborta y se lanza el 'manual' de la ayuda...
Si todo va bien, se invoca la función que ha de ejecutar la acción, junto con todos los parámetros.
OJO: Para parámetros opcionales, siempre debe inicializarse a un valor por defecto, de modo que si tal parámetro no comparece, sea el valor por defecto el que recibe la función.
A veces la aparición de un parámetro inutiliza el valor de otro, es decir hay parámetros con prioridad... en el ejemplo solo se dan 2 casos, uno con la ruta para la acción 'Comprimir', que permite añadir un único fichero o todos los que tenga una carpeta...
El otro caso se da  en listar, en teoría se volcará el listado por consola, pero si aparece un parámetro de ruta de destino para un fichero, se vuelva la salida a dicho fichero...
A veces se dan casos, donde un parámetro que recibe un valor siguen siendo válido el parámetro pero otro parámetro con más prioridad invalida su valor... es un caso más complejo, y esos casos se deben tratar mejor ya dentro de la función que ha de ejecutar la acción.

Solo queda añadir las funciones (de pega, la  única a medio completar es la de ayuda), para que haciendo un copy-paste, y modificando lo que proceda (si se adapta a otro lenguaje), pueda ejecutarse hasta el crítico momento de entrar a la función que acabaría realizando la ejecución de la acción solicitada.
Código (vbnet) [Seleccionar]

#Region "Procesado de Ordenes..."
   ''' <summary>
   ''' Orden ayuda (o cuando hay error en los parámetros de entradas).
   ''' </summary>
   ''' <param name="TextoExtra"></param>
   ''' <remarks></remarks>
   Private Sub ShowAyuda(ByRef TextoExtra As String)
       If (TextoExtra.Length > 0) Then
           Console.WriteLine(TextoExtra & vbCrLf)
           Console.WriteLine(Strings.StrDup(80, "="))
           Console.WriteLine("")
       End If
       Console.WriteLine("Uso:" & vbTab & "compresor.exe /orden <resto de parametros para dicha orden>")
       Console.WriteLine(Strings.StrDup(80, "="))
       Console.WriteLine("")
       Console.WriteLine("Ordenes disponibles: Añadir, Ayuda, Extraer, Comprimir, Info, Listar, Verificar, ")
       Console.WriteLine("   Comprimir: Crea un nuevo fichero comprimido, partiendo de la ruta recibida. Fichero o carpeta.")
       Console.WriteLine("     Parametros: /c /r:ruta [/t:x] [/n]")
       Console.WriteLine("     /c" & vbTab & "Orden para comprimir (tambien puede indicarse /Compress o /Comprimir)")
       Console.WriteLine("     /r:ruta" & vbTab & "Obligatorio. Especifica la ruta de entrada, donde esta el fichero/s que se van a comprimir (puede ser un fichero o una carpeta).")
       Console.WriteLine("     /t:numero" & vbTab & "Opcional. Determina que tipo de compresion quiere usarse... (un valor 0, no comprime solo archiva)")
       Console.WriteLine("     /n" & vbTab & "Opcional. Si es una carpeta, indica si tambien se comprimira todo el contenido de las subcarpetas que contenga.")
       Console.WriteLine("")
       Console.WriteLine("   Listar: Enumera el contenido de un fichero comp. Si no se indica una ruta de destino, se vuelca en la pantalla.")
       Console.WriteLine("     Parametros: /l /r:ruta [/d:ruta] [/s:string]")
       Console.WriteLine("     /l" & vbTab & "Orden para listar, también se acepta /List y /Listar")
       Console.WriteLine("     /r:ruta" & vbTab & "Obligatorio. Especifica la ruta de entrada, debe ser un  fichero de tipo '.comp'...")
       Console.WriteLine("     /d:ruta" & vbTab & "Opcional. Especifica una ruta de destino donde volcar el listado. No debe existir.")
       Console.WriteLine("     /s:separador" & vbTab & "Opcional. Separador usado para generar el listado, si se omite se usa 'CR+LF'.")
       Console.WriteLine("")
       Console.WriteLine("   Ayuda: Muestra detalles del uso de la línea de comandos del programa.")
       Console.WriteLine("     Parametros: /h")
       Console.WriteLine("     /h" & vbTab & "Orden para mostrar este mensaje de ayuda (también se acepta /? /Help y /Ayuda)")
       Console.WriteLine("")
       Console.WriteLine("etc... para otras ordenes.")
   End Sub

   ''' <summary>
   ''' Orden para ofrecer información sencilla sobre un archivo comprimido.
   ''' </summary>
   ''' <param name="Ruta"></param>
   ''' <param name="Comentario"></param>
   ''' <remarks></remarks>
   Private Sub Informar(ByVal Ruta As String, ByVal Comentario As Boolean)
       Dim fsI As IO.FileStream

       If GetFileStreamSiEsFormatoValido(Ruta, fsI) = True Then
           ' leer cabecera, tomar ciertos datos informativos
           ' y mostrarlos en alguna ventanita...
           ' versión del programa con que fue creada el archivo, cuantos ficheros contiene
           '  tamaño del archivo y tamaño que tendría el contenido descomprimido...etc...
           If Comentario = True Then
               ' si el archivo contiene un comentario sobre el mismo, tomar para mostrarlo...
           End If

           fsI.Close() : fsI = Nothing
       End If
   End Sub

   ''' <summary>
   ''' Orden listar:
   ''' </summary>
   ''' <param name="Ruta"></param>
   ''' <param name="Separador"></param>
   ''' <param name="Destino"></param>
   ''' <remarks></remarks>
   Private Sub ListarContenido(ByRef Ruta As String, ByRef Separador As String, Optional ByRef Destino As String = "")
       Dim fsI As IO.FileStream, fsO As IO.FileStream
       Dim lista() As String ' o cualquier otro objeto que facilite el añadido... según diseño del programa.

       If GetFileStreamSiEsFormatoValido(Ruta, fsI) = True Then
           ' leer la cabecera.
           ' mover el puntero de lectura al punto donde consta la lista de ficheros contenidos
           ' Ir tomando la lista de ficheros que contiene (si es una lista plana, si no ir saltando entre el contenido con el par direccion + size).
           ' recordar añadir el separador tras cada nombre.
           If (Destino.Length > 0) Then
               If IO.File.Exists(Destino) = False Then
                   ' crear fichero y volcar el contenido.
                   ' en este caso es preferible ir escribiendo (append), directamente al fichero mejor que añadirlo a un array para después volcarlo a fichero...

                   '.Close() : fsO = Nothing
               Else
                   ' mensaje: error, el fichero de destino ya existe, la lista debe crearse en un fichero nuevo...
               End If
           Else ' volcarla por pantalla
               ReDim lista(0 To 5)
               Console.WriteLine("Éste es el contenido del archivo: " & vbCrLf & (lista.ToString)) ', "Contenido del archivo:")
           End If

           fsI = Nothing
       End If
   End Sub

   ''' <summary>
   ''' Orden para verificar
   ''' </summary>
   ''' <param name="Ruta"></param>
   ''' <param name="Nivel"></param>
   ''' <remarks></remarks>
   Private Sub Verificar(ByVal Ruta As String, Optional ByVal Nivel As Integer = 0)
       Dim fsI As IO.FileStream

       If GetFileStreamSiEsFormatoValido(Ruta, fsI) = True Then
           ' (revisión mínima) leer cabecera y verificar que es correcta. esto siempre
           If RevisionMinima(fsI) And Nivel > 0 Then
               ' si nivel=1,(revisión simple) revisar por ejemplo que los índices de comienzo de extracción corresponden con la dirección guardada y tamaño del comprimido.
               ' Si nivel=2 (revisión media) podría señalar revisión completa, en tal caso (por ejemplo), calcular hash, para todo el archivo (excepto la cabecera) y ver que coincide
               '   con el hash guardado en la cabecera.
               ' Si nivel>2 (revisión completa: a todo lo previo, toca ir extrayendo de uno en uno cada fichero comprimido y calcular su hash y comprobar que coincide con el alamcenado en la estructura ligada a dicho fichero)
           End If

           ' informar de modo similar al chkdisk... por ejemplo...
           ' ir informando sobre la marcha, para las revisiones 2 y mayor que 2...
           ' informar al final con un resumen... todo ok, o falló tal cosa...
       End If
   End Sub

   ''' <summary>
   ''' Orden para extraer contenido, todo o parcial.
   ''' </summary>
   ''' <param name="Ruta"></param>
   ''' <param name="Destino"></param>
   ''' <param name="Patron"></param>
   ''' <remarks></remarks>
   Private Sub Extraer(ByVal Ruta As String, ByVal Destino As String, ByVal Patron As String)
       Dim fsI As IO.FileStream

       ' Si no existe la carpeta de destino, ni siquiera nos molestamos en abrir el archivo.
       ' opcionalmente podría crearse la carpeta (solo si la ruta padre para ella existe)
       '   pero como es un ejemplo, basta así...
       If IO.Directory.Exists(Destino) Then
           If GetFileStreamSiEsFormatoValido(Ruta, fsI) = True Then
               ' Evaluar el patrón:
               ' Si es "" implica que se extrae todo.
               ' Si es en la forma *.extension" ó ".extension" , se extraen todos los ficheros que coincidan con dicha extensión...
               ' Si es en la forma *algo* se extraen todos los ficheros que contengan ese 'algo'...
               ' si es en la forma 'Xnumero-', se extraen los primeros x ficheros del comeinzo del archivo.
               ' si es en la forma '-Xnumero', se extraen los últimos x ficheros del final del archivo.
               ' si es en la forma 'Ynumero-Xnumero', se extren los x ficheros a partir de yº fichero comprimido.
               ' si es en la forma 'nombre.extension' se extrae el fichero cuyo nombre coincide (si existe).
               ' también podrían indicarse patrones por fecha..."mm/dd/AAAA' por ejemplo, para extrer los ficheros cuya fecha de compresión/creacion/modificacion/etc... coincidan con la indicada...
               '  incluyendo fechas parciales como indicar solo el mes o año, o un rango entre fechas...
               ' etc... cosa de imaginación.

               ' informar al final de la cantidad de ficheros extraídos... y abrir la carpeta donde se extrajo (resulta útil).
           End If
       End If
   End Sub

   ''' <summary>
   ''' Orden para comprimir un fichero y añadirlo al archivo comprimido.
   ''' </summary>
   ''' <param name="Ruta"></param>
   ''' <param name="Fichero"></param>
   ''' <remarks></remarks>
   Private Sub Añadir(ByVal Ruta As String, ByVal Fichero As String, ByVal Tipo As Integer)
       ' verifica que el fichero comprimido existe (se supone que sí) y el de origen
       ' luego Más de lo mismo, comprime el fichero, añadiéndolo al final del archivo,
       ' y actualiza la cabecera, cantidad de ficheros, y hash de la cabecera.
       ' etc..
       '...
   End Sub

   ''' <summary>
   ''' Orden para crear un nuevo archivo comprimido (no debe existir)
   ''' </summary>
   ''' <param name="Ruta"></param>
   ''' <param name="Tipo"></param>
   ''' <param name="Recursivo"></param>
   ''' <remarks></remarks>
   Private Sub Comprimir(ByVal Ruta As String, ByVal Tipo As Integer, Optional ByVal Recursivo As Boolean = False)
       Dim nombre As String
       Dim fsO As IO.FileStream
       Dim fi As IO.FileInfo, di As IO.DirectoryInfo
       Dim puntero As Int64
       Dim numfiles As Integer

       ' no sabemos a priori si origen es la ruta a un fichero o a una carpeta
       ' si es una carpeta, se comprimirá todo su contenido.
       If IO.Directory.Exists(Ruta) Then
           di = FileIO.FileSystem.GetDirectoryInfo(Ruta)
           nombre = (Ruta & "\" & di.Name & ".comp")
           fsO = IO.File.Create(nombre)

           puntero = ComprimirAnidado(fsO, Ruta, numfiles, Tipo, Recursivo)
       ElseIf FileIO.FileSystem.FileExists(Ruta) Then
           fi = FileIO.FileSystem.GetFileInfo(Ruta)
           nombre = Ruta.Replace(fi.Extension.ToUpper, ".comp")
           If IO.File.Exists(nombre) = False Then
               fsO = IO.File.Create(nombre)

               puntero = ComprimirFichero(fsO, Tipo, Ruta)
               numfiles = 1
           Else
               ' error ya existe el archivo de salida, debe crearse uno nuevo... no puede existir.
               Exit Sub
           End If
       End If

       If puntero = -1 Then
           ' mensaje: ha fallado la compresión para el fichero 'nombre'
           ' se puede solicitar ignorar y continuar, o bien abortar,
           ' como es un ejemplo se aborta, pero se informa...
           'fsO.Close()
           ' eliminar archivo.
       Else
           ' mensaje: archivo creado... total de 'numfiles' comprimidos...
       End If

       fsO = Nothing
   End Sub
#End Region


Y finalmente otras pequeñas funciones (tamvbién de 'papel-cartón') que aparecen invocadas en el código, para que no cante error...
Código (vbnet) [Seleccionar]

#Region "Las funciones que realizan todo el trabajo (ejecutan las órdenes)"

   Private Function ComprimirAnidado(ByRef Fs As IO.FileStream, ByRef Carpeta As String, ByRef NumFiles As Integer, ByVal Tipo As Integer, ByRef Recursivo As Boolean) As Int64
       Dim fi As IO.FileInfo, di As IO.DirectoryInfo, d As IO.DirectoryInfo
       Dim files() As IO.FileInfo, folders() As IO.DirectoryInfo
       Dim puntero As Int64

       di = FileIO.FileSystem.GetDirectoryInfo(Carpeta)

       files = di.GetFiles
       For Each fi In files
           puntero = ComprimirFichero(Fs, Tipo, fi.FullName)
           If (puntero = -1) Then
               Return -1
           Else
               ' puntero se usa como dirección, punto de inicio de la compresión del siguiente fichero
               '  para la estructura del mismo
               NumFiles += 1
           End If
       Next

       If (Recursivo = True) Then
           folders = di.GetDirectories
           For Each d In folders
               puntero = ComprimirAnidado(Fs, d.FullName, NumFiles, Tipo, Recursivo)
               If puntero = -1 Then
                   Return -1
               End If
           Next
       End If

       Return Fs.Position
   End Function

   Private Function ComprimirFichero(ByRef Fs As IO.FileStream, ByVal Tipo As Integer, ByRef Fichero As String) As Int64
       Dim fsI As IO.FileStream

       fsI = IO.File.OpenRead(Fichero)
       ' según el tipo de compresión debería derivarse a la función específica...

       '...crear un búffer de lectura, y sobre un bucle ir leyendo y comprimiendo y volcando al destino.
       ' al tiempo ir calculando un hash para sobre el buffer.
       ' escribir los datos de cabecera de info del fichero (tamaño original, hash, fechas, etc...).
       ' actualizar en destino

       ' si falla devolver -1.

       Return Fs.Position
   End Function

   Private Function GetFileStreamSiEsFormatoValido(ByRef Ruta As String, ByRef fichero As IO.FileStream) As Boolean
       If IO.File.Exists(Ruta) Then
           Try
               fichero = IO.File.OpenRead(Ruta) ' Verifica primero si existe el fichero.
               '   lo(abre), lee la cabecera para verificar que es del tipo esperado
               '   y quizás un breve chequeo de que no está corrupto...
               '   Si todo conforme devuelve el filestream y
               Return True
               '   si no conforme
               '      fichero.Close() : fichero = Nothing
               '      mensaje: el fichero 'ruta' no es del formato que éste programa está capacitado para tratar.
               '      o mensaje: el fichero 'ruta', es del tipo esperado, pero parece estar dañada la cabecera.
               '      o mensaje: el fichero 'ruta' es del tipo esperado, pero la versión que señala no puede ser atendida por la versión actual del programa, por favor actualice a una versión más actual."
               '      return false
           Catch ex As Exception
               Console.WriteLine("Ha ocurrido un error no controlado:" & vbCrLf & ex.Message)
               Return False
           End Try
       Else 'si no existe:
           '    mensaje: el fichero 'ruta' no parece existir (se ha movido, eliminado, renombrado?), o quizás no tenga permisos suficientes
           Return False
       End If
   End Function

   ' Trata de ver que como mínimo la cabecera no está corrupta
   '  (ya se sabe previamente que es del tipo adecuado y puede leerse)
   Private Function RevisionMinima(ByRef FS As IO.FileStream) As Boolean
       ' leer la cabecera y verificar que esta bien...
       ' en general primero se computa un hash a la cabecera y luego se revisa con el hash contenido  en la misma cabecera. a estos efectos, el propio hash no debe formar parte de ese cómputo, por lo que suele ponerse al final de la cabecera.
       Return True
   End Function
#End Region


Y listo, debiera ser suficiente (si no soy muy optimista), para que te sirva de aprendizaje e inspiración...
Me queda eso si, poner alguna imagen y el contenido de un fichero reg, que guardara todas las acciones en el registro... aún así, para probarlo puede hacerse sin shell, haciendo uso de scripts... algún bat-cmd, por ejemplo. Aunuqe para eso entonce shay que compilarlo.
Si desde debug, se meten las opciones de línea de comando se puede probar "paso a paso" (en vbnet es la tecla F11, no recuerdo ahora mismo cual es la de C#).

rigorvzla

Guao NEBIRE que explicacion tan extensa , solida y concisa, muchisimas gracias , esto es tema de sentarse a leer con detalle linea a linea y mas aun analizar cada linea de codigo, de momento pasare de implementarlo de una vez al proyecto mio ya que me llevara tiempo estudiar aprender y entender todo lo explicado, muchisimas gracias a medida de  que valla haciendlo las pruebas respectivas te ire preguntando para la mayor comprension  del tema,  MUCHISIMAS GRACIAS !! .

Dejare claro que uso C# WPF , basicamente por que es el primero y unico lenguaje de programacion que  me presentaron y hasta la fecha me ha gustado mucho, podria decir que es lo mejor que conozco y unico. Versatil y "Facil"... (para no ser programador de profesion).

Cinco estrellas a este super tutorial , le dedicare tiempo necesario y estare informandote de acuerdo a mis avances en el tema, ya previamente habia logrado con ejemplos previos de elektro crear una aplicacion simple con parametros donde mostraba mensajes, solo que se presentaron los problemas como que se abria nuevamente la aplicacion, cosa que no es correcto, entre otras cosas.

Pero ya con esa guia todo duda mia deberia quedar solventado.

Eleкtro

#22
Cita de: rigorvzla en 28 Agosto 2018, 03:01 AMya previamente habia logrado con ejemplos previos de elektro crear una aplicacion simple con parametros donde mostraba mensajes, solo que se presentaron los problemas como que se abria nuevamente la aplicacion, cosa que no es correcto, entre otras cosas.

Primero presentaste un problema, y se te ofrecieron dos soluciones (manipulación del registro de Windows, o desarrollo de una shell-extensión), luego presentaste un problema distinto y adicional que no habias comentado al principio: mantener una única instancia de tu app, y se te ofrecieron varias soluciones por mi parte con ejemplos completos y funcionales en el último post de la segunda página de este thread, que no los hayas querido o sabido aplicar es cosa tuya.

Lo comento, por que casi pareces insinuar que los problemas que te surgen sean culpa mía. Si desde un principio te expresaras con la suficiente claridad y comentando punto por punto todas y cada una de las cosas que quieres lograr hacer, todo sería más facil.




Ya que estamos, aquí te dejaré unos últimos ejemplos relacionados para mostrarte una alternativa de como lo puedes hacer. El primero demuestra una forma dinámica mediante clases de tipos genéricos, elegante y reutilizable para representar (definir) argumentos y también parsearlos en una aplicación:


...se podría decir que es una solución guiada para administrar la creación, el uso y el análisis de argumentos recibidos en la aplicación.

El segundo y último ejemplo demuestra una forma igualmente dinámica y elegante (mediante Xml) y además yo diría que incluso divertida al poder jugar con la colorización de forma dinámica, para representar la entrada de ayuda command-line mostrada por una aplicación:


Saludos.








rigorvzla

Elektro nada que ver con que tu generas problemas, todo lo contrario agradezco muchisimo la solucion que me diste tambien, ambas me seran de gran ayuda.

CitarLo comento, por que casi pareces insinuar que los problemas que te surgen sean culpa mía. Si desde un principio te expresaras con la suficiente claridad y comentando punto por punto todas y cada una de las cosas que quieres lograr hacer, todo sería más facil.

Se me es complicado explicarme con la suficiente claridad lo acepto no solo me pasa por aqui, pero aclaro que NO es culpa de ustedes lo que me surje a mi por falta de explicacion clara.

De igual manera agradezco el trabajo de ambos buen dia, me pondre a esto al tener la disponiblidad (factor tiempo es muy cruel y corto).

Serapis

#24
Bueno, al final he echo el ficherito reg para añadir todas las acciones al registro...

Debajo del copiapega, algunos comentarios...

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

# Lo derivamos hacia una clave donde extendernos más...
[HKEY_CLASSES_ROOT\.comp]
@="compresorSuper"

[HKEY_CLASSES_ROOT\compresorSuper]
"BrowserFlags"=dword:00000008
@="Compresor descompresor de ficheros y carpetas"
"EditFlags"=dword:00000000
"AlwaysShowExt"=""

# El icono del programa se asocia a los *.comp...
[HKEY_CLASSES_ROOT\compresorSuper\DefaultIcon]
@="c:\\program files\\compresorSuper\\compresorSuper.exe,1"

[HKEY_CLASSES_ROOT\compresorSuper\shell]

[HKEY_CLASSES_ROOT\compresorSuper\shell\Open]

# Esta acción es la típica del doble click, a un fichero *.comp
# El simple OPEN, al tratarse de una aplicación de consola lo derivamos hacia la acción: INFO
#  Si fuera una interfaz, se abriría la aplicación mostrando una ventana con el contenido y un extenso menú...
[HKEY_CLASSES_ROOT\compresorSuper\shell\Open\command]
@="c:\\program files\\compresorSuper\\compresorSuper.exe /I /R::%1"


# Acción para informar
# --------------------------------
[HKEY_CLASSES_ROOT\compresorSuper\shell\Informar]
@="Comp:&Informar"

# Acción para informar con petición de mostrar el comentario...
[HKEY_CLASSES_ROOT\compresorSuper\shell\Informar\command]
@="c:\\program files\\compresorSuper\\compresorSuper.exe /I /R::%1 /K"


# Acción para Verificar (TEST)
# --------------------------------
[HKEY_CLASSES_ROOT\compresorSuper\shell\Verificar]
@="Comp:&Verificar"

# Acción para verificar con petición de hasta el nivel 2 (revisión media)
[HKEY_CLASSES_ROOT\compresorSuper\shell\Verificar\command]
@="c:\\program files\\compresorSuper\\compresorSuper.exe /T /R::%1 /N::2"


# Acción para Listar
# --------------------------------
[HKEY_CLASSES_ROOT\compresorSuper\shell\ListarToScreen]
@="Comp:&Listar en pantalla"

# Acción para Listar, el separador se deja por defecto (CR+LF), por lo que dicho parámetro se omite.
[HKEY_CLASSES_ROOT\compresorSuper\shell\ListarToScreen\command]
@="c:\\program files\\compresorSuper\\compresorSuper.exe /L /R::%1"


# Acción para Listar (a FICHERO)
# --------------------------------
[HKEY_CLASSES_ROOT\compresorSuper\shell\ListarToFile]
@="Comp:Li&star en fichero"

# Acción para Listar, a fichero... en la raíz de la unidad C.
#  Algo más elaborado debería admitir el parámetro '/d' sin valor, para señalar que la carpeta fuere la misma de origen de %1
[HKEY_CLASSES_ROOT\compresorSuper\shell\ListarToFile\command]
@="c:\\program files\\compresorSuper\\compresorSuper.exe /L /R::%1 /D::C:\"


# Acción para Extraer todo
# --------------------------------
[HKEY_CLASSES_ROOT\compresorSuper\shell\ExtraerTodo]
@="Comp:&Extraer todo"

# Acción para Extraer todo, en la unidad C (que no es muy interesante).
#  Algo más elaborado debería admitir el parámetro '/d' sin valor, para señalar que la carpeta fuere la misma de origen de %1
#    o al menos preguntar por la ruta de extracción, trtas recibir el comando...
[HKEY_CLASSES_ROOT\compresorSuper\shell\ExtraerTodo\command]
@="c:\\program files\\compresorSuper\\compresorSuper.exe /E /R::%1 /D::C:\"


# Acción para Extraer imágenes jpg
# --------------------------------
[HKEY_CLASSES_ROOT\compresorSuper\shell\ExtraerImgJpg]
@="Comp:&Extraer imágenes jpg"

# Acción para Extraer solo las imágenes JPG (que hubiere dentro del archivo comprimido), en la unidad C (que no es muy interesante).
#  Algo más elaborado debería admitir el parámetro '/d' sin valor, para señalar que la carpeta fuere la misma de origen de %1
#    o al menos preguntar por la ruta de extracción, trtas recibir el comando...
# Esto debiera dar idea que el mismo comando puede tener diferentes variaciones en función de los valores que se coloquen en los parámetros que admite.
[HKEY_CLASSES_ROOT\compresorSuper\shell\ExtraerImgJpg\command]
@="c:\\program files\\compresorSuper\\compresorSuper.exe /E /R::%1 /D::C:\ /P::*.jpg"

# Ayuda con la línea de comandos
# --------------------------------
[HKEY_CLASSES_ROOT\compresorSuper\shell\Ayuda]
@="Comp:&Help (ayuda CommandLine)"

# No precisa más comentario...
[HKEY_CLASSES_ROOT\compresorSuper\shell\ExtraerImgJpg\command]
@="c:\\program files\\compresorSuper\\compresorSuper.exe /H"



# ====================================================================================
# TODAS LAS ACCIONES PREVIAS EMERGEN SOLO PARA ARCHIVOS DE TIPO *.COMP
# AHORA TOCA LA ÚNICA ACCIÓN QUE SE APLICA AL RESTO DE FICHEROS Y CARPETAS (COMPRIMIR)
# ====================================================================================


# Acción para Comprimir (fichero)
# --------------------------------
[HKEY_CLASSES_ROOT\*\shell\ComprimirComp]
@="Comp:&Comprimir"

# Comprime el fichero... (con el algoritmo por defecto: tipo 1)
[HKEY_CLASSES_ROOT\*\shell\ComprimirComp\command]
@="c:\\program files\\compresorSuper\\compresorSuper.exe /C /R::%1 /T::1"


# Acción para Comprimir (fichero)
# --------------------------------
[HKEY_CLASSES_ROOT\*\shell\ComprimirCompZip]
@="Comp:Comprimir (&Zip)"

# Comprime el fichero... (con el algoritmo ZIP (tipo 2))
[HKEY_CLASSES_ROOT\*\shell\ComprimirCompZip\command]
@="c:\\program files\\compresorSuper\\compresorSuper.exe /C /R::%1 /T::2"


# Acción para Comprimir (fichero)
# --------------------------------
[HKEY_CLASSES_ROOT\*\shell\ComprimirCompPaq]
@="Comp:E&mpaquetar (no comprime)"

# Solo empaqueta el fichero... (tipo 0))
[HKEY_CLASSES_ROOT\*\shell\ComprimirCompPaq\command]
@="c:\\program files\\compresorSuper\\compresorSuper.exe /C /R::%1 /T::0"



# Acción para Comprimir (ficheros en carpeta)
# --------------------------------
[HKEY_CLASSES_ROOT\Directory\shell\ComprimirCompZip]
@="Comp:&Comprimir (ficheros)"

# Acción para comprimir los ficheros en la raíz de la carpeta en ZIP (Tipo 2)
[HKEY_CLASSES_ROOT\Directory\shell\ComprimirCompZip\command]
@="c:\\program files\\compresorSuper\\compresorSuper.exe /C /R::%1 /T::2"


# Acción para Comprimir (recursivamente en carpeta)
# --------------------------------
[HKEY_CLASSES_ROOT\Directory\shell\ComprimirCompZipNest]
@="Comp:&Comprimir (recursivo)"

# Acción para comprimir el contenido completo de una carpeta en ZIP (Tipo 2)
[HKEY_CLASSES_ROOT\Directory\shell\ComprimirCompZipNest\command]
@="c:\\program files\\compresorSuper\\compresorSuper.exe /C /R::%1 /T::2 /N"


0 - NOTA: No he probado el ficherito, espero que si contiene algún error seas capaz de subsanarlo, si no es así, informa, pero primero inténtalo por tí mismo...

1 - Lo primero, todo el texto de esta cita, lo copias y lo pegas en un fichero nuevo, y lo guardas con extensión .reg, luego haces los cambios que se indican a continuación y cuando estén todos los cambios acomodados, guardas de nuevo el contenido y haz doble click sobre él, el sistema reconoce este tipo de fichero y guardará todas las claves en el registro...

2 - Su contenido es una asociación con la exrtensión ".comp" (que se supone no existe en tu registro, si existiera, mira de cambiar sobre el texto del fichero por otra que no exista (quizás comp2, o lo que se te ocurra).

3 - Los archivos que crees (aunque estén vacios) con extensión .comp, llevarán el mismo icono de tu aplicación... Y cuando pinches sobre dicho ficheros, en el menú contextual aparecerá un chorro de acciones para ellos.
Para facilitar la búsqueda en el menú contextual, a todos los he precedido con un texto en la forma: 'comp: texto de acción', donde texto de acción es el texto que describe la acción... así aparecerán todos juntos y será fácil de ver....

4 - Cambios a realizar al fichero .reg:
Como se ve se da por sentado que el programa se llama CompresorSuper.exe, por tanto haz un remplazo para cambiar cada aparición de compresorSuper por el que hubiere de tener tu programa o bien deja este y crea una aplicación de prueba con la transcripción del código del mensaje previo, y así los cambios son mínimos al menos para probarlo... y fíjate que se deja una ruta en program files\nombre del programa\...
Es decir una vez que se meta esto al registro, debe existir un ejecutable en la ruta que se haya indicado... así que cambia la ruta a la que tu tengas...
5 - Para hacer uso (sin errores), de dichas acciones una vez registradas (es decir fuera ya de debug), debes haber compilado el programa y diponer el ejecutable en la ruta que se la indicado al registro...

6 - Guarda finalmente el fichero .reg y con doble click se introducirá al registro (seguramente te pida permiso para ello)...

7 - Ahora si examinas detenidamente el ficherito, verás que las acciones introducidas al registro, son las acciones que pusimos en el código más arriba, que son las que decdicmos que nuestro programa dispusiera de esta forma, y a nuestro antojo (observo que me he comido la acción "añadir", pero bueno el resto está y como son ejemplo de prueba pués vale). He añadido comentarios al ficherito y separado convenientemente cada acción de la siguiente, para mayor claridad...
Verás también que cada acción registrada tiene en concreto los parámetros designados para cada acción en el código, es decir se atiene al 'diseño' que elegimos... y por último verás que incluso hay acciones que tienen más de un 'registro', ya que se ha puesto una variación de la misma (a base de alterar los parámetros, por ejemplo 'comprimir y empaquetar y comprimir en Zip, etc...).

8 - Resument de registro: Hay 3 tipos de cosas registradas...
1º La asociación con una extensión. Fíjate como la extensión .comp se deriva hacia "compresorSuper", es una forma rápida de poder alterar-derivar una extensión hacia otra aplicación sin 'destruir' lo registrado por una aplicación previa. Así cuando abras el registro, no verás bajo la extensión .comp (bajo la clave HKEY\Classes\.comp más que una línea, por tanto busca luego esa clave en el registro (misma sección, es decir bajo: HKEY\Classes\compresorSuper ).

2º Se han resgistrado varias acciones para esa extensión, por tanto vinculadas al programa "compresorSuper.exe", es decir son accones que sólo emergerán en el menú contextual, cuando sea un fichero de tipo .comp ....por eso para probar que aparecen tras hacer el registro, deberás crear ficheros con esa extensión (crea un fichero de texto vacío (o escribe algo si quieres, no importa) y cambia su extensión... en ese punto, el registro no tiene ninguna respopnsabilidad sobre si un fichero es o no del tipo cuya extensión contiene, esa responsabilidad compete al programa asociado a la extensión (esto debe quedarte claro como el agua, para no exigir al registro cometidos que no le competen).

3º Se ha registrado para cualquier otro tipo de fichero, la acción comprimir y a las carpetas 2 acciones comprimir. es decir esas acciones solo cabe llevarlas a cabo a distintos ficheros, las previas solo a los de tipo .comp
Si te fijas bien, he dejado tachado (en el párrafo anterior) la palabra 'otro'... porque lamentablemente el registro es incapaz de entender que querríamos registrar acciones para todos los ficheros excepto para 1 (o más) determinados... por lo que el programa debería ser capaz de detectar cuando para esas acciones (en este caso cuando la acción es comprimir, únicamente), que el único fichero que no va a admitir esa acción, son precisamente los de tipo .comp (porque ya está comprimido), si se pretendiera comprimir una segunda vez, se debería cambiar la extensión. en resumen si a la acción comprimir le llega un fichero de tipo .comp, podría o debería rechazarlo... si se desea comprimir o empaquetar, el contenido de una carpeta,  si sería admisible que entre ese contenido hubiera ficheros .comp.
Imagina winrar para comprimir una carpeta y que deje fuera ficheros .rar que hubiere en esa carpeta, no es admisible, en cambio si verás que no deja 'recomprimir' un archivo .rar (si, si le 'engañas' cambiándole la extensión...).

...y bueno, no creo necesario más notas aclaratorias respecto del fichero reg... aunque no quita que algo se me pase...





9 - Cuando hagas copia del código del mensaje de más arriba, realmente puedes omitir, todas las llamadas a las funciones de más abajo, así no tienes porqué copiar, pegar y traducir a C (todas esa funciones), para probar el ejemplo (basta con), las líneas de llamada a dichas funciones pueden ser remplazadas por una salida a la consola, tal que así (añadimos 1 línea y por cada acción 2 líneas más y comentamos otra):

Código (vbnet) [Seleccionar]

       console.writeline("Orden recibida: " & param  ' <<<<---------------------------
       Select Case Param
          ' ...
          ' ...
           Case "L", "LISTAR", "LIST"
               For k = 1 To cmd.GetUpperBound(0)
                   Param = cmd(k).ToUpper

                   If (Param.Contains("::")) Then
                       SubParam = Param.Split(sepCommand, StringSplitOptions.RemoveEmptyEntries)
                       If (SubParam.Length > 1) Then
                           Param = SubParam(0) : Valor = SubParam(1)
                       Else
                           Param = "-"
                       End If
                   End If

                   console.writeline(vbtab & "Parametro recibido: " & param  & vbtab & "Valor: " & valor ' <<<<---------------------------
                   Select Case Param
                       Case "R", "RUTA"
                           rutaOrg = Valor                            
                       Case "D", "DESTINO"
                           rutaDst = Valor
                       Case "S", "SEPARADOR"
                           separador = Valor
                   End Select
                   Valor = ""
               Next    

               ' Esta orden, contiene todo lo necesario?
               If (rutaOrg.Length > 0) Then
                   ' Call ListarContenido(rutaOrg, separador, rutaDst)
                     console.writeline("Aquí iría la ejecución d ela acción ' <<<<---------------------------
               Else
                   Call ShowAyuda(faltaParam & " /Ruta, es obligatorio para /Listar")
               End If          





Con respecto a lo que te señalaba Elektro, te aclaro que de cara a entender la línea de comandos, el código puesto más arriba conjuntamente con este fichero reg de ejemplo, es lo mejor que puede haber... porque así ves que se hace, cómo y porqué... sin embargo, aunque sea lo mejor para entender, no es lo mejor de cara al programador. Es decir, para cada programa tendrías que rescribir toda la operatoria, cada vez... si recurres a un objeto que empaquete todo esa operatoria y sea genérico (apto para cualquier programa), como la solución que mismamente Elektro te aporta en otro enlace, es mucho más óptimo cara a la 'progamación ágil'...

Aquí se emplean unas 200 líneas de código, mientras que usando un objeto, apenas serían unas 20... básicamente creas una instancia del objeto, al que se pasa el valor de 'commands' (o quizás el objeto sea capaz de interceptarlo por sí mismo, este punto es irrelevante), el objeto lo masca... y tu luego solo tienes un bloque select case donde evalúas el primer argumento (que al caso es la orden solicitada), y para cada caso, una llamada a la función donde se colocan el resto de argumentos, o bien se pasan todos como un paramArray, o como una colección, etc... según el diseño del objeto. Lo que efectivamente para esas 7 acciones distintas, se resumiría en poco más o menos que 20 líneas de código y además con bastante claridad y fáciles de mantener cuando surja la necesidad.

En resumen, diferencia el código puesto para entender y asimilar conceptos (que suele ser subóptimo, pués su cometido no es ese) y código óptimo para el rendimiento.



Olvidé adjuntar un par de imágenes, por lo demás obviables, pero por si alguien las necesita...

La siguiente imagen muestra, en 2 la línea de comando recibida, y en 1 los parámetros ya 'descompuestos' en el array...



La siguiente imagen muestra (en pasos demarcados), donde localizar el punto (en el IDe de VB), para ingresar la línea de comando cuando estamos en modo DEBUG...

rigorvzla

wooow , 21/10 puntos jejeje , gracias tengo material para leer analizar comprender para un buen rato muchas , MUCHISIMAS gracias  dudo que alla faltado algo, ya ahora me queda esperar los dias libres que vienen de esta semana y a leer con calma, gracias nuevamente por la ayuda prestada a ti y a Elektro feliz dia!! :D

rigorvzla

#26
Antes de crear un nuevo tema probare por aqui, quiero saludar a todos nuevamente , ya que hace ya tiempo que publique este post y me fue de mucha ayuda.

Eh comprendido un tanto el manejo del registro desde C#, pero no eh logrado comprender lo siguiente, a pesar de estar en la ruta CLASS\ROOT donde estan todas las extensiones de los archivos, algunas extensiones carecen de la carpeta "shell", apesar de crearla y agregarle el comando que quiero , no se muestra en el menu contextual y no entiendo por que, por ejemplo quiero hacerlo con la extensión .mkv y solo me muestra las carpetas (OpenWithProgIds, ShellEx) y haga lo que haga creando manualmente la carpeta "shell" no funciona.

Si me pueden explicar el porque y que deberia hacer para lograrlo seria de mucha ayuda para mi.

NOTA: tengo instalado el reproductor VLC y este, crea en el registro una llave llamada VLC.mkv la cual SI tiene la carpeta "shell" , incluso le agregue mi menu y comando y se mostro, pero el asunto es que si el usuario NO tiene el VLC instalado, no funcionara, entocnes pense, como puedo registrar mi programa con las extensiones q quiero, de la misma manera que lo hace el VLC.

Espero sus respuestas y gracias de antemano.

MODIFICACION DE POST!
Probe la aplicacion en un sistema de windows limpio, y resulto interesante la prueba ya que al agregar la extensión ".7z" resulta que si me la toma, no me registra el icono de mi aplicacion en el archivo de dica extensión, pero si me aparece la accion en el menu contextual de dicho archivo, incluso lleva a cabo la accion cuando hago click.

La nueva pregunta es. Porque en el equipo donde estoy desarrollando la app y tengo instalado 7zip NO me registra la extensión y en cambio en la pc de prueba que esta limpia s(solo windows) SI me la registra, como puedo hacer para que me la visualize en un equipo que tenga instalado un programa que registre la misma extensión?

RayR

Creo que estás confundiendo algunas cosas, como registrar un tipo de archivo y registrar una aplicación que maneje ese tipo. No tengo tiempo para ver todo lo que te han escrito en este hilo, pero a forma de recordatorio o resumen, esto es lo mínimo necesario para agregar un programa a la lista "Abrir con" de un tipo de archivo.

Primero, no deberías modificar HKEY_CLASSES_ROOT. Leerla está bien, pero escribirla no se recomienda en los Windows modernos. Sólo deberías modificar HKEY_CURRENT_USER, para cambios que sólo apliquen al usuario actual; o HKEY_LOCAL_MACHINE, para los que apliquen de forma global. Windows automáticamente combina ambas para formar HKEY_CLASSES_ROOT, que se mantiene por motivos de compatibilidad. En este ejemplo supondré que quieres que los cambios apliquen a todos los usuarios.

Primero crea un ProgID por cada extensión que quieras manejar. Por ejemplo, suponiendo que tu programa se llama MyPlayer, para manejar los .mkv:

Código (ini) [Seleccionar]
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\MyPlayer.mkv\shell\open\command]
@="\"c:\\program files\\MyPlayer\\myplayer.exe\" \"%1\""


Luego, agregarlo a la extensión:

Código (ini) [Seleccionar]
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.mkv\OpenWithProgids]
"MyPlayer.mkv"=hex(0):


Para agregar otra acción (verbo es el término oficial) como "Agregar a lista de reproducción", repites lo mismo del primer paso, pero en lugar de open, el verbo que quieras, por ejemplo: [HKEY_LOCAL_MACHINE\SOFTWARE\Classes\MyPlayer.mkv\shell\addtoplaylist\command]

Esto ya podría funcionar, pero si tienes más de una aplicación añadiendo verbos para ese mismo tipo de archivos desde el registro o se cambia la aplicación predeterminada para esa extensión, puede haber problemas, y quizás es lo que te pasa con 7-Zip. Para evitar eso puedes recurrir a la clave SystemFileAssociations, así:

Código (ini) [Seleccionar]
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\SystemFileAssociations\.mkv\shell\addtoplaylist]
@="Agregar a lista de reproduccion"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\SystemFileAssociations\.mkv\shell\addtoplaylist\command]
@="\"c:\\program files\\MyPlayer\\myplayer.exe\" -argumento \"%1\""

rigorvzla

muchs gracias!!! en efecto estoy modificando de manera directa la clas root, eso me sirve pero no me convece, por que si existen otros programas q asocian la misma extensión, hay problemas, entoces hare los pasos que me has dejado a lo ultimo a ver como me va, MCUHISIMAS GRACIAS!!! 

RayR

Debo aclarar que al decir que puede dar problemas no me refiero a que se vaya a producir un error ni nada por el estilo. Realmente no hay nada incorrecto en usar ProgIDs, y de hecho deberías hacerlo, pero si hay más de una aplicación registrada para manejar un tipo de archivo, es posible que los verbos de la tuya no se muestren si se dan ciertas condiciones, cosa que no sucede con SystemFileAssociations.