En un post anterior del blog expuse el empleo de una función API para reproducir un sonido (ver).
En esta ocasión comentaré una nueva función API que nos permitirá abrir cualquier tipo de archivo tan sólo informando de dónde se encuentra (cuál es su ruta), sin saber el nombre de la aplicación asociada; en respuesta a la pregunta planteada:
Antes de 'meternos en faena' me gustaría comentar, de manera muy superficial, qué es una API y cuán peligroso puede llegar a ser, si no se emplea adecuadamente... comenzar diciendo que antes de llamar a funciones de Microsoft Windows (API), debemos entender cómo las DLL de API de Windows tratan los argumentos y tipos de datos, ya que las llamadas incorrectas a funciones de Windows pueden causar errores de páginas no válidas u otro comportamiento imprevisto.
También podemos leer una introducción en Wikipedia
En nuestro ejercicio de hoy no existe peligro alguno, ya que sólo abriremos un fichero desde nuestra hoja de cálculo... para este trabajo emplearemos la función API ShellExecute.
Esta función API 'ShellExecute' dispone de seis parámetros, muchos de ellos opcionales:
1- hWnd: Identifica la ventana principal. Esta ventana recibe las cajas de mensajes que una aplicación produce (por ejemplo, para el informe de errores).
2- lpOperation: Especificamos la acción que queremos realizar al ejecutar nuestra función. Podrían ser entre otras las siguientes acciones:
4- lpParameters: Si hemos informado en el anterior 'lpFile' un archivo ejecutable (.exe), entonces 'lpParameters' será una cadena(String) que detalle los parámetros para que opere dicha aplicación. Si 'lpFile' fuera un archivo tipo documento, 'lpParameters' debería ser NULL (0& or vbNullString).
5- lpDirectory: Cadena que identifica la carpeta/directorio de trabajo por defecto.
6- nShowCmd : Cómo queremos ver la applicación cuando se ejecute y abra:
Añadiremos nuestro código en la Hoja de trabajo, asociándolo a un evento _BeforeDoubleClick:
Estamos listos para trabajar, bastará hacer doble clic sobre una celda cualquiera de la hoja para que la función busque y abra el fichero (sea cual sea) en la ruta especificada cuyo nombre corresponda con el valor de la celda 'clicada'...
En esta ocasión comentaré una nueva función API que nos permitirá abrir cualquier tipo de archivo tan sólo informando de dónde se encuentra (cuál es su ruta), sin saber el nombre de la aplicación asociada; en respuesta a la pregunta planteada:
...una macro que logre hipervincular los nombres de varias imágenes (p.e. a.jpg, b.jpg, c.jpg, etc) que están en una columna, con las imágenes, propiamente dichas, que están en la siguiente carpeta: "D:\BancoFotos\". Para logra que con un click en el nombre de la imagen abra dicha imagen... |
Antes de 'meternos en faena' me gustaría comentar, de manera muy superficial, qué es una API y cuán peligroso puede llegar a ser, si no se emplea adecuadamente... comenzar diciendo que antes de llamar a funciones de Microsoft Windows (API), debemos entender cómo las DLL de API de Windows tratan los argumentos y tipos de datos, ya que las llamadas incorrectas a funciones de Windows pueden causar errores de páginas no válidas u otro comportamiento imprevisto.
También podemos leer una introducción en Wikipedia
En nuestro ejercicio de hoy no existe peligro alguno, ya que sólo abriremos un fichero desde nuestra hoja de cálculo... para este trabajo emplearemos la función API ShellExecute.
Esta función API 'ShellExecute' dispone de seis parámetros, muchos de ellos opcionales:
1- hWnd: Identifica la ventana principal. Esta ventana recibe las cajas de mensajes que una aplicación produce (por ejemplo, para el informe de errores).
2- lpOperation: Especificamos la acción que queremos realizar al ejecutar nuestra función. Podrían ser entre otras las siguientes acciones:
- "Open" = nuestra función abrirá el archivo especificado por el parámetro siguiente 'lpFile'. El archivo podrá ser indistintamente un ejecutable (.exe) o cualquier otro tipo de documento (.jpg, .pdf, etc., etc.), incluso podría ser sencillamente una carpeta o directorio.
- "Print" = La función imprimiría el archivo indicado por 'lpFile', lógicamente el archivo deberá ser algún tipo de documento no ejecutable, ya que en caso de serlo, simplemente lo abriría... (si así estuviera indicado).
- "Explore" = La función 'exploraría' la carpeta/directorio indicada por 'lpFile'.
- "Play" = Para aquellos métodos que soportan una función Play, como archivos de sonido...
- "Properties" = Detallaría las propiedades del archivo.
- 0& = el argumento 'lpOperation' puede ser también NULL (vbNullString si declaramos la variable como String, o bien 0& si queda declarada de cualquier otra forma). En estos casos la función toma la acción por defecto, que es OPEN.
4- lpParameters: Si hemos informado en el anterior 'lpFile' un archivo ejecutable (.exe), entonces 'lpParameters' será una cadena(String) que detalle los parámetros para que opere dicha aplicación. Si 'lpFile' fuera un archivo tipo documento, 'lpParameters' debería ser NULL (0& or vbNullString).
5- lpDirectory: Cadena que identifica la carpeta/directorio de trabajo por defecto.
6- nShowCmd : Cómo queremos ver la applicación cuando se ejecute y abra:
- Valor
- 0 = oculta la ventana de activación y pasa a otra ventana.
- 1 = Activa y muestra una ventana. Si la ventana está Minimizada o Maximizada , Windows la restaura a su tamaño y posición original (igual que 9 ).
- 2 = Activa una ventana y la muestra en forma de icono .
- 3 = Activa una ventana y lo muestra como una ventana Maximizada.
- 4 = muestra una ventana en su tamaño más reciente y posición. La ventana que está actualmente permanece activa.
- 5 = Activa una ventana y lo muestra en su actual tamaño y la posición.
- 6 = Minimiza la ventana especificada y activa la ventana de nivel superior en la lista del sistema.
- 7 = Muestra una ventana como un icono. La ventana que está actualmente activa permanece activa.
- 8 = Muestra una ventana en su estado actual. La ventana que está actualmente activa permanece activa.
- 9 = Activa y muestra una ventana. Si la ventana está Minimizada o Maximizada, Windows restaura a su tamaño y posición original (lo mismo que 1).
Añadiremos nuestro código en la Hoja de trabajo, asociándolo a un evento _BeforeDoubleClick:
Private Declare Function ShellExecute _ Lib "shell32.dll" _ Alias "ShellExecuteA" ( _ ByVal hwnd As Long, _ ByVal lpOperation As String, _ ByVal lpFile As String, _ ByVal lpParameters As String, _ ByVal lpDirectory As String, _ ByVal nShowCmd As Long) _ As Long Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean) Dim strFile As String Dim strAction As String Dim lngErr As Long 'ruta del archivo. strFile = "E:\excelforo\Fotos\" & Target.Value 'las acciones pueden ser OPEN, NEW u otras; todo depende de qué necesitemos strAction = "OPEN" 'llamamos a la funicón API lngErr = ShellExecute(0, strAction, strFile, "", "", 0) End Sub
Estamos listos para trabajar, bastará hacer doble clic sobre una celda cualquiera de la hoja para que la función busque y abra el fichero (sea cual sea) en la ruta especificada cuyo nombre corresponda con el valor de la celda 'clicada'...
Hola muchas gracias por tu función, pero presento el siguiente problema:
ResponderEliminarcuando cambio: strAction = "OPEN" por strAction = "PRINT" en este último no me imprime el archivo, pero con strAction = "OPEN" si lo abre. puedes ayudarme?. Gracias.
Hola!
Eliminarsupongo que el fichero que quieres imprimir NO es ejecutable o algún otro tipo de fichero NO imprimible (comprimidos, etc)???
Slds
Hola Ismael, es cierto, no es ejecutable pero si es imprimible pues tiene extensión htm. Gracias.
EliminarHola,
Eliminartengo mis dudas que esta API detecte los html como 'imprimibles', en su documentación exige que sean documentos de texto (y al fin y al cabo, un html es un fichero de código con protocolos UTF, etc. etc...).
hace tiempo funcionaba otra instrucción.. no sé si aún siga activa, que específicamente sí permitía imprimir html:
rundll32.exe mshtml.dll,PrintHTML nombrearchivo
Espero te resulte.
Slds
Hola Ismael, y específicamente como seria el código de la función para esa instrucción (rundll32.exe mshtml.dll,PrintHTML nombrearchivo). Gracias.
EliminarPues no recuerdo muy bien..
Eliminares un código, que como te indicaba, es algo viejo.. y me parece era específico para máquinas de 32 bits.
Pero en principio, es tal cual, cambiando 'nombrearchivo' por el nombre de tu fichero.
Saludos
Hola Ismael, lo máximo tu código, me sirvió muy bien. Muchas gracias por compartir tu conocimiento y experiencia de manera gratuita.
Eliminar;-)
EliminarMuchas gracias!
Este comentario ha sido eliminado por el autor.
ResponderEliminarEstimado, me has salvado la mitad de un quebradero de cabeza que tengo hace meses, he aprendido excel sobre la marcha y muchos temas no manejo muy bien.
ResponderEliminartengo un informe con una macro que dispara a través de hipervinculos unos archivos .REP, estos a su vez exportan datos a unos TXT que es desde donde luego excel obtiene la información, el problema es que debo a cada instante aceptar la alerta de ejecución de hipervinculo, pero con el código que dejaste los archivos se ejecutan sin mas, la pregunta es como puedo agregar esto a mi macro?
Hola Kazuno,
EliminarNo sé exactamente a qué te refieres con tu pregunta ??
pero entiendo tendrás tu macro en un módulo estándar.. asi que incorpora al inicio del módulo la declaración de la función:
Private Declare Function ShellExecute ....
y el cuerpo del procedimiento Sub que aparece en lugar de tu instrucción para abrir los ficheros.
Un saludo
Gracias por la respuesta, disculpa si no me exprese bien, a ver si me aclaras un poco mas la idea (quede plop con lo de la declaración).
Eliminarpor ejemplo, una de las macro que uso es así (comento todas las lineas en mis macro, mañas mías)
Sub opa()
'no queremos que muestre la operacion
Application.ScreenUpdating = False
'nos dirigimos a los links, como la pestaña esta oculta, la mostramos
'seleccionamos los links y trabajamos en ellos, uno por vez
Range("X2").Select
Selection.Hyperlinks(1).Follow NewWindow:=False, AddHistory:=True
'tiempo de espera mientras se abre el reporte CMS
Application.Wait (Now + TimeValue("00:00:5"))
'selecciona y trabaja en la pestaña oculta
Sheets("CMS").Visible = True
Sheets("CMS").Select
Range("A1:AI25").Select
'limpiamos lo que ya este, en caso de error
Selection.ClearContents
....
en que parte debería agregar lo que mencionas? suponiendo que el LINK de la celda X2 es "C:/texto.txt"
Por lo que entiendo quieres abrir el fichero que se encuentra en la ubicación del hipervínculo...
Eliminarpodrías tomar la dirección de hipervínculo como variable, e incluirlo directamente en el procedimiento descrito en el post.
También podrías usar directamente el método .Open del objeto Workbook.
Espero haberte orientado esta vez.
Saludos
Buenos días.
ResponderEliminarTengo una macro que imprime con esta API, los archivos PDF´s, pero dado que la impresora predeterminada está configurada para impresión a una cara no puedo hacer que me imprima uno o varios de los archivos a doble cara. Es decir, necesito que el primer archivo lo imprima a una cara y el segundo a doble cara. Gracias
Buenos días Oscar,
Eliminaresta API controla la impresora y no la configuración, date cuenta que el control sobre otras aplicaciones (Adobe en este caso) desde Excel es limitado...
No creo posible tal cosa... salvo mejor opinion
Lo siento
un saludo
Buenas tardes, Ismael, podrías apoyarme para abrir un archivo en formato pdf, la situación es esta: capturó un número en un textbox y ese numero es el nombre del archivo en pdf, obviamente pues va a estar cambiando el nombre del archivo, soy principiante en esto y los archivos pdf están en una carpeta en C:\BD
EliminarHola Daniel,
Eliminara priori te serviría la macro expuesta en el post.. en todo caso cambiando la línea 18:
strFile = "E:\excelforo\Fotos\" & Target.Value
por tu particularidad:
strFile = "C:\BD\" & TextBox1.Value
OJO asegúrate cómo se llama tu control
Saludos
Hola buenas, gracias por la explicación, amigo una pregunta, es posible también cerrar una aplicación desde el excel, o sea, que así mismo como uno la puede abrir, la pueda cerrar, muchas gracias
ResponderEliminarHola Alexander,
Eliminarsí, claro es posible.. depende de la aplicación que quieras cerrar se suele emplear una u otra manera, por ejemplo para paquete Office se emplea el método:
.Quit
en otros casos .Terminate...
Espero te oriente
Saludos
Tengo el problema que cuando se abre el fichero .jpg con el visualizador de imagenes de windows en sistemas de 32bit lo hace bien pero en sistemas de 64bit puros como es el Windows Server 2013, no me funciona dándome el error en la linea Lib "shell32.dll".
ResponderEliminarPor favor me pueden ayudar.
Hola Jorge,
Eliminares correcto, ya que en el ejemplo estamos utilizando la librería correspondiente a 32 bits (justo en la línea que indicas).. aunque en teoría la de 32bits debería funcionar también en sistemas de 64 bits.
Prueba de todas formas comenzando por:
Private Declare PtrSafe Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (....
es decir, añadiendo PtrSafe antes de la palabra Function
con esto debería asegurarse el correcto funcionamiento
Saludos
Gracias amigo no me ha funcionado con .jpg y los he modificado en .pdf y de esta forma podemos funcionar con servidores windows server 2013.
ResponderEliminarEres un tio genial por contestarme tan rapido.
;-)
Eliminarme alegra haber podido ayudar
Saludos
Es grato saber que existe gente con un conocimiento tan basto como tu, dispuesto a compartirlo, mis más sinceras gracias.
ResponderEliminar;-)
Eliminarmuchas gracias...
Al compartir TODOS aprendemos
Un saludo
Hola Ismael
ResponderEliminarViendo el codigo de esta funcion API veo que la apertura del fichero correspondiente se apertura con el programa que por defecto abre la extension de dicho fichero (que aparece en la opcion Propiedades del menu contextual de dicho fichero)
Podrias indicar una funcion API, para que antes de abrir un fichero en cuestion, estableciera como programa de apertura por defecto aquel que le pasemos como ruta a un argumento de la funcion.
Estoy intentando abrir ficheros .pdf con el lector de pdfs SumatraPdf ejecutando el siguiente codigo:
Dim AppPdf As Object
Set AppPdf = CreateObject("Shell.Aplication")
AppPdf.Open(Ruta)
* Ruta es la ruta del fichero pdf a abrir, incluyendo su extension
Es el unico codigo que funciona correctamente pero antes he debido establecer manualmente el programa por defecto que abre los pdf en la opcion Propiedades del menu contextual. Lo que intento es que esta accion manual sea ejecutada tambien por codigo y solo es posible con una funcion API. Seria posible?
Hola,
Eliminarcreo recordar no es configurable la aplicación por defecto
Revisaré documentación y si encuentro solución lo comentaré
Slds
2 cuestiones mas...
ResponderEliminar1- Sabrias cual es la ruta que emplea Microsoft Edge?
2- Al ejecutar el codigo indicado en el anterior comentario, a traves de los metodos Hoja1.Activate o Hoja1.cells(1, 1).Select intento que el foco se situe en Excel despues de abrir el pdf pero no lo consigo con dichas lineas de codigo. Que linea de codigo necesitaria para que el foco se situara de nuevo en Excel tras la apertura del fichero pdf?
Gracias
Hola,
Eliminarsuponiendo la macro está en el Excel al que quieres volver
Thisworkbook.Hoja1.Activate
¿La ruta que emplea para Edge?, a qué te refieres?
Slds
Hola de nuevo Ismael
ResponderEliminar- El problema no es la referencia Hoja1 porque lo tengo establecida como:
Set Hoja1 = ThisWorkbook.Worksheets(NOMBREHOJA)
De hecho, he probado a ejecutar codigo de prueba posterior a la apertura del Objeto Pdf tal como...
Dim AppPdf As Object
Set AppPdf = CreateObject("Shell.Aplication")
AppPdf.Open(Ruta)
Hoja1.Cells(1, 1).Select
Hoja1.Cells(1, 1).Value = 1
* Ruta es la ruta del fichero pdf a abrir, incluyendo su extension
La ejecucion de este codigo es correcta, abriendo el fichero pdf correctamente y posteriormente escribiendo el valor 1 en Excel en la celda A1, pero quedando finalmente el foco en el objeto Pdf (SumatraPdf, MicrosoftEdge o el que sea) sin mediar codigo por medio y no en Excel como seria lo logico. Un poco extraño, la verdad.
2 - Respecto a saber la ruta de MicrosoftEdge lo digo para poder indicar la ruta de dicho objeto con la sentencia .Open para hacer una prueba, pero la ruta no es tan evidente como puede ser la del Objeto InternetExplorer o la de SumatraPdf, tal como:
/.../InternetExplorer.exe o
/.../SumatraPdf.exe
No consigo averiguar dicha ruta.
3 - Respecto a la funcion API que me cambie el programa predeterminado por defecto para abrir los ficheros pdf sigo sin averiguar nada.
Gracias