Menú

Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mostrar Mensajes Menú

Mensajes - Serapis

#2191
Las filas ocupan el espacio entre una fila y la siguiente.
Las columnas ocupan el espacio entre un carácter y el siguiente...

Y digo esto, porqué no a cuál te refieres exactamente, yo veo bien las filas.

En cualuqier caso es algo que depende de la fuente utilizada, así que puedes probar con diferentes fuentes para el  objeto printer:

Código (vb) [Seleccionar]
printer.font = "verdana"

Y también puedes jugar con el tamaño:
Código (vb) [Seleccionar]
printer.font.size = 12

y por último y más interesante, si estás satisfecho con la fuente y tal... simplemente puedes indicarle que la siguiente línea la imprima algo más arriba...

Esto es crea una función tal que así CambiarFuente, que invocaremos cada vez que queramos cambiar la fuente o su tamaño, y que nos devolverá el alto de la fuente (simepre actualizado), para luego usar una función 'Saltar... puesta más abajo...

...yo te pongo código, esto antedicho y algo más que te explico más abajo, tu lo pruebas en otro proyecto aparte, cambiar lo que proceda, lo pruebas de nuevo y entonces que quedes satisfecho, lo integras en tu proyecto)...

Añade un botón y un listbox como se muestra en la imagen capturada ...

Código (vb) [Seleccionar]

Dim AltoLinea As Single  ' en píxeles


Private Sub Command1_Click()
   Call IntentaCambiarFuenteEInforma("Verdana")        ' 13
   Call IntentaCambiarFuenteEInforma("Arial")          ' 14
   Call IntentaCambiarFuenteEInforma("8514oem")        ' 20
   ' Esta dará error, porque su nombre es 'Monospac' sobra la 'e'... para probar...
   Call IntentaCambiarFuenteEInforma("Monospace821 Bt", 22) ' monoespaciada
   Call IntentaCambiarFuenteEInforma("Agatha")         ' 21
   
   Call IntentaCambiarFuenteEInforma("Fixedsys")       ' 20
   Call IntentaCambiarFuenteEInforma("Impact")         ' 22
   Call IntentaCambiarFuenteEInforma("Lucida Console") ' 18 monoespaciada
   Call IntentaCambiarFuenteEInforma("Math B", 32)     ' 13--> 49
   Call IntentaCambiarFuenteEInforma("Mini Pics Lil Edibles") ' 43 esta tiene comida, que tal un símbolo en cada artículo ?
   
   With Me
       .CurrentX = 3200
       .CurrentY = 45
       .ForeColor = vbRed
       Print ("%*lq8Dev") ' hamburguesa, pizza, espaguetis?, perrito caliente, helado, fruta, pollo, bebida
   End With
End Sub

Private Sub IntentaCambiarFuenteEInforma(Optional ByRef Nombrefuente As String, Optional size)
   Dim fuenteprevia As String, fuenteReasign As String, fuenteFinal As String
   Dim sizePrevio As Single, sizeActual As Single
   Dim cmb As String
   
   fuenteprevia = Me.FontName: sizePrevio = Me.FontSize
   
   If Me.CambiarFuente(Nombrefuente, size) Then
       'MsgBox "Nueva fuente. Alto de una línea con la fuente '" & Nombrefuente & "':  " & CStr(AltoLinea)
       fuenteFinal = Nombrefuente
       cmb = "Cambiada"
   Else
       fuenteFinal = Me.FontName
       cmb = "Forzada"
       'MsgBox "Alto de una línea con la fuente reasignada '" & CStr(Me.FontName) & "':  " & CStr(AltoLinea)
   End If
   sizeActual = Me.FontSize
   
   
   linea = Concatena(cmb, fuenteprevia, CStr(sizePrevio), Nombrefuente, fuenteFinal, CStr(sizeActual))
   List1.AddItem (linea)
End Sub

Private Function Concatena(a As String, b As String, c As String, d As String, e As String, f As String) As String
   Dim c1 As String, f1 As String
   
   a = FijarNumchars(a, 10)
   b = FijarNumchars(b, 17)
   c = FijarNumchars(c, 10, vbRightJustify)
   d = FijarNumchars(d, 17)
   e = FijarNumchars(e, 17)
   f = FijarNumchars(f, 10, vbRightJustify)
   
   Concatena = Join(Array(a, b, c, d, e, f), " | ")
End Function

' Devuelve texto truncado al número de caracteres indicado (corta o añade espacios).
Private Function FijarNumchars(ByRef Valor As String, ByVal Numchars As Byte, Optional Lado As AlignmentConstants = vbLeftJustify) As String
   Dim size As Byte, par As Byte
   
   
   size = Len(Valor)
   If (size > Numchars) Then
       FijarNumchars = Left$(Valor, Numchars)
   ElseIf (size < Numchars) Then ' Hay q ue añadir espacios en alguna o más partes...
       Select Case Lado
           Case vbLeftJustify
               FijarNumchars = Valor & (Space$(Numchars - size))
           Case vbRightJustify
               FijarNumchars = (Space$(Numchars - size)) & Valor
           Case Else ' centrado
               par = (size And 1) ' si es par= 0, si no suma 1
               size = ((Numchars - size) \ 2)
               
               FijarNumchars = (Space$(size) & Valor & Space$(size + par))
       End Select
   Else
       FijarNumchars = Valor
   End If
End Function

Private Sub Form_Load()
   Dim linea As String
   
   Call CambiarFuente
   
   linea = Concatena("Cambio", "Actual", "Alto", "Solicitada", "Final", "Alto")
   With List1
       .Font = Me.Font
       .Width = Me.TextWidth(linea & "  ")
       Me.Width = .Width + (.Left * 2) + 60  ' suma 4 píxeles del margen de la ventana...
       
       .AddItem (linea)
       Call .AddItem(String(100, "-"))
   End With
End Sub


' Intenta cambiar la fuente... en caso de hacerlo devuelve TRUE.
'   aunque no se produzca ningún cambio, SIEMPRE calcula el alto para la fuente actual.
' OJO: Cambiar la fuente puede cambiar el alto, ya que cada fuente tiene su propia altura específica.
Function CambiarFuente(Optional ByRef Nombrefuente As String, Optional size) As Boolean
   Static basta As Boolean
   Dim intentoFallido As Boolean
   On Error Resume Next  ' por si no existe la fuente...

   ' Cambia la fuente
   If (Len(Nombrefuente) > 0) Then
       Me.Font = Nombrefuente
       
       If (Me.FontName = Nombrefuente) Then
           CambiarFuente = True ' la fuente cambió.
       Else
           intentoFallido = True ' ha habido un intento de cambiod e fuente fallido...
           ' pero un fallo en esto, implica que asigna una fuente del parent
           MsgBox "Nueva fuente: " & Me.FontName & vbCrLf & "Reasignada por intento fallido de : " & Nombrefuente & vbCrLf & "Ha habido un intento de cambio de fuente fallido... quizás no esté instalada en tu sistema (o el nombre de la fuente esté mal redactado)."
           ' OJO: La reasignación de fuente, cambia la fuente, pero deja el tamaño previo que hubiere (truncado al más próximo que permite la nueva fuente).
       End If
   End If
   
   ' Size se deja como variant, para preguntar si se recibió valor para dicho parámetro.
   If IsMissing(size) = False Then
       ' si hubo error al tratar de cambiar de fuente, no se deja cambiar el tamaño...
       If (intentoFallido = False) Then
           ' No necesariamente se cambiará al valor reclamado, cada fuente tiene sus tamaños, así que se ajusta al más 'similar'.
           If (Me.FontSize <> size) Then
               Me.Font.size = size
               MsgBox "tamaño de fuente cambiado a: " & CStr(Me.FontSize)
           End If
       End If
   End If

   ' Y obtiene el alto de cada línea (en realidad basta con un carácter, pués se basa en la fuente)
   '   En realidad si solo es una línea (de alto) una cadena vacía es suficiente...
   AltoLinea = (Me.TextHeight("Whisky para todos.") / Screen.TwipsPerPixelY)
   
   
   If (basta = False) Then ' es suficiente con mostrarlo una vez...
       ' OJO: Si el texto se compone de varias líneas (porque lleva CRLF), devuelve el alto acumulado entre todas ellas.
       ' ejemplo:
       Dim x As Single
       x = ((Me.TextHeight("Whisky" & vbCrLf & " para" & vbCrLf & " todos.") / 3) / Screen.TwipsPerPixelY)
       MsgBox "¿el alto de una línea es el mismo que el de tres si dividimos su alto entre tres?" & vbCrLf & vbTab & CStr(x = AltoLinea)
       
       basta = True
   End If
End Function


Bien, hay varias cosas que decir del código previo, pero antes vamos a lo que te urge...
El código anterior nos ofrece el alto de línea... (en el ejemplo es para el form, pero cambiando en la función deseada (CambiarFuente), Me. por Printer. donde procede, llegamos a los mismo, no tengo impresora conectada a este equipo)...



Código (vb) [Seleccionar]

' Función: Reposiciona el puntero de escritura.
' Parámetros:
'    AvanceUnitario: es la cantidad de píxels (u otro tipo de medida, esto debes consensuarlo con el "ScaleMode" que tengas), que sube o bajas sobre el alto de una fila. Un valor 0 señala que es el alto que ofrece la fuente.
'    AvanceLineas: es para cuando debas saltar más de una línea (típicamente porque dejas alguna en blanco), pero por defecto tiene valor de 1 linea.
'    IncHorizontal: valor de Incremento horizontal.
'    Absoluto: Señala si el valor es absoluto o relativo (que se suma (o se resta si es menor que 0) a la posiciópn horizontal actual).
Private Sub SaltarLinea(ByVal AvanceUnitario As Byte, Optional AvanceLineas As Byte = 1, Optional ByVal IncHorizontal As Integer = 0, Optional ByVal Absoluto As Boolean = True)
   Dim x As Integer
   
   Printer.CurrentY = Printer.CurrentY + ((AltoLinea + AvanceUnitario) * AvanceLineas)
   
   ' Puede que queramos saltar de línea arriba abajo, pero seguir en la posición horizontal, o cambiarla...
   If (IncHorizontal <> 0) Then
       x = IncHorizontal
       If (Absoluto = True) Then
           Printer.CurrentX = IncHorizontal
       Else
           Printer.CurrentX = (Printer.CurrentX + IncHorizontal)
       End If
   Else
       If (Absoluto = True) Then Printer.CurrentX = 0
   End If
End Sub
' Para saltar 3 líneas, con un alto de 2 píxeles más de alto entre cada línea)
SaltarLinea(2, 3)

' Para saltar a la siguiente línea con 1 píxel más arriba de lo que tiene cada línea:
SaltarLinea(-1, 1)

'  Recuerda Si ya saltaste la línea ...
' SaltarLinea( x, AvanceLineas: =0 )
SaltarLinea(-3 , 0) ' de la línea actual sube 3 pixels.


Recuerda que  'print' tiene la siguiente sintaxis:
print {Spc(n) | Tab(n)} expression charpos

- Spc(n): es opcional y quiere decir que pudes indicar x espacios (columnas)
- Tab(n): es opcional y quiere decir que puedes imprimir en la columna x de dicha línea (incluso más atrás de la actual, donde apunte currentX, es decir es un valor absoluto).
En realidad Tab es lo adecuado, cuando tienes varias columnas, pués conociendo el punto de comienzo de cada ciolumna, puedes escribirlas en cualquier orden... ya que controla el punto de escritura en la línea con el 'tabulador' ...
- expression: se refiere al texto que vas a escribir.
- charpos: son opciones que indican adónde apuntar tras escribir la 'expressión'. Si se omite (por defecto es así), se salta de línea, si se pone "," (una coma), equivale a añadir vbtab al texto. Si se pone ";",  simplemente avanza a la posición dle siguiente carácter... es decir es lo adecuado si quieres usar la función "saltarLinea", indicando cuantas líneas y el resto de parámetros, etc......






Y una vez aclarado tu problema...
Por lo demás, veo que las columnas no coinciden... tienes columnas de artículo, precio, cantidad total...
Tu primer trabajo es determinar la posición donde termina cada una de las columnas, o mejor darles formato y señalar cuantas columnas ocupa cada una...
Es decir, la columna artículo, se imprime alineada a la izquierda (se hace así con el texto), pero las otras columnas son numéricas y los números se alinean a la derecha...
Sabiendo la delimitación de un campo (punto de comienzo y fin), como te he indicado más arriba puedes usar Tab(x), para empezar a imprimir ahí...

...en el código he puesto también ejemplo para alinear texto sobre columnas... que espero te sirvan de ayuda para mejorar la presentación, como se ve en la imagen.



cuestiones menores... puedes usar negrita para algún campo dentro de la etiquetas de factura.
Incluso la veleidad de que si seleccionas la fuente "Mini Pics Lil Edibles", que contiene símbolos de comida, podría antecerse el texto de la columna del artículo con un carácter...  para diferenciar por ejemplo comida de bebidas, postre, café, licores, etc...



p.d.:
No uses instrucciones de esta forma:
Código (vb) [Seleccionar]

 Printer.Print "               ¡¡¡GRACIAS POR SU PREFERENCIA!!!"


Usa mejor la función como la que te he pasado....
Código (vb) [Seleccionar]

dim Gracias as string

gracias = "¡¡¡GRACIAS POR SU PREFERENCIA!!!"
printer.print FijarNumchars(gracias, printer.width, vbCenter)

Recuerda siempre el ScaleMode, para ejemplos de código, sin probar uno no va a estar poniendo cada cosa meticulosamente , eso corresponde al interesado... Si printer.width, no te ofrece valores traduce desde  printer.papersize, y si no establece un valor manualmente al cargar la aplicación sabido el ancho del papel...
#2192
Cita de: Orubatosu en 30 Agosto 2018, 17:45 PM
...Pretender definir el significado de las palabras es algo que los aprendices de dictadores, muy al estilo 1984 quieren hacer...
No hace falta que sean dictadores (o solo aprendices), lo vemos casi a diario en muchos políticos (con altas dosis de tontería mental)...

Se me viene a la cabeza esas solemnes idioteces del tipo: "Miembros y miembras..." que el RAE recoge (en la acepción pertinente como "individuo que pertenece a una comunidad...", pero claro, topamos luego con: "indivíduo o individua"... y si miembro, en su concepto inicial refiere a una parte del todo, indivíduo, es más de lo mismo: "que no se puede dividir" (sin dejar de ser lo que es, obviamente), y que refiere al "ser de una u otra especie"...
#2193
...y si a pesar de todo quieres usarlo, por que te gusta, antes de instalarlo, como mínimo envíalo a  https://www.virustotal.com/es/
#2194
Java / Re: Estructura repititiva while
30 Agosto 2018, 18:02 PM
Probablemente el problema radique en el carácter separador de decimales...

Usa locale (con Scanner), para averiguar que idioma está colocado (seguramente el español), usa uselocale, para cambiarlo.

También con getDecimalSeparator,  que te señala qué carácter estás usando actualmente como separador decimal, puedes saber rápidamente si es ese el problema...



Para cuestiones simples con el lenguaje, antes revisa la documentación del lenguaje...
https://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html#nextFloat()
#2195
Si es el tuyo lo solictas a la operadora. Si no es tuyo, no hay forma de tener acceso más que a lo que quede en el historial de llamadas desde la última vez que fue borrado.

Mala cosa es que se guardara copia oculta (que no debiera ser así sin conocimiento del dueño de la línea), pero uno nunca acaba de sorprenderse de los desastres que empresas de solvente envergadura tienen por ahí... Al parecer tienen más chapuceros en sus filas, que los que nunca imaginamos, incluso entre la cúpula de directivos... (de tanto en cuanto, sale uno a la luz pública), así que no conviene garantizar un no rotundo
#2196
Yo creo que la definición que da el RAE no se sale de exagerada por ningún lado. Incluso es discreta.

Y respecto de la igualdad de sexos, pués lo siento por quien piense lo contrario, pero solo es conforme en cuanto a la ley (todos somos iguales antes la ley), pero en el resto somos muy distintos... no solo ya entre sexo o raza y edad, si no entre vecinos y hasta hermanos. Y si no fuera así, pués seríamos todos clones idénticos.
Y quien no lo entienda, pués ya tiene ahí en eso mismo, un ejemplo de distinción...

Las redes sociales, lo que tiene es que hace de altavoz, sin filtro alguno... por eso, incluso hasta si afirmas que "las cucarachas son bichos feos", no faltará algún grupúsculo que te venga a atacar por semejante tontería... Las redes sociales, son la voz de los indignados (así en general), más no de los que aportan soluciones. He ahí la M135D4 de políticos que tenemos, uno tras otro... en cada país (no solo en éste o aquél)... si fueran algo más que indignados, no se eligiría a nadie que no fuera apto, aunque un país se quedara sin gobierno...
#2197
Bah... no hay ningún problema...

Ahora que Inmaduro (su apellido no le hace justicia), ha quitado 5 ceros, puede quitarle la semana que viene otros 3 y luego imprimir chorrocientos milllones más de "macro-blívares-soberanos-revolucuionarios-oloquechachisuenemejor-queigualvalelomismo"... ah no, que ahora el lumbrera acaba de inventar el ahooro vendiendo lingotes de oro (minilingotes, digo gotas de oro)... ...total para qué, para que tengas tus pequeños ahorros en casa y a la vuelta de unos meses aparezcan delincuentes especializados en secuestro y robar tus 'lingotes' ???. Soluciones no producirá, pero delincuentes... a mansalva. A futuro, todos esos delincuentes serán una carga para el país que durará alrededor de 2 generaciones...

Si viviéramos 2 siglos atrás, creo que nadie (en su tierra) dudaría en llevarlo a la horca.
#2198
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...
#2199
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#).
#2200
CitarHola nebire, disculpa el medio quiero agradecerte por tu explicacion que fue muy solida y precisa, mas no me solvento lo siguiente, espero ser lo mas claro posible y disculpa si no, necesito que una vez creado el "boton" llamemoslo asi , en el menu contextual, en vez de llamar a un archivo .exe, llame un metodo del mismo proyecto.

Como puedo lograr tal cosa? en caso de no ser por este medio, como deberia ser, quiero que al apretar ese boton creado en el menu contextual por ejemplo me ejecute un mensaje X, tantas vecees yo lo aprete.

no se como hacerlo no se como argumentar mi programa exe para pasar ahi el metodo.

Disculpa mi ignorancia y espero puedas entenderme mas claramente.
Ya dije antes que haría un sencillo programa de ejmplo, en el que puedas basarte.

Creo que tu fallo (ahora), está en que, parece que no comprendes qué es y como funciona la 'linea de comandos' que recibe un programa y al caso, el ejemplo que te pondré espero que te aclares las dudas.

Desde fuera de un programa, no puede llamarse a un método de un programa, si no es a través del propio programa, sea por línea de comandos, sea por un punto de entrada al método. Esto último solo está disponible en librerías, porque un ejecutable no expone 'funciones/métodos públicos (esa es precisamente la idea de las librerías, exponer funciones que puedan ser invocadas desde fuera, y su diseño, responde justamente a eso)... y por tanto (para los ejecutables (que no son servidores)) solo hay un único punto de acceso al programa. ...pero precisamente es en esa entrada donde se debe evaluar si se ha recibido una orden (parámetro), para invocar un método específico. No me extiendo más, pués ya iré explicando cuando ponga el ejemplo, seccionando el código en varias partes donde comento...

Perdona si dije que lo pondría a la tarde y no ha sido así, pero al final, uno no es dueño de su tiempo libre... surgen cosas y al final uno tiene horarios intempestivos, a lo largo de la noche (si no me entra sueño) o mañana... vamos tan pronto saque una horita libre (y ganas de ponerme con elllo), lo hago... Si tienes muchas prisas, entonces intenta investigar por tu cuenta.