Ejecutar las macros de un XLSM desde Java

Vamos con algo muy sencillo pero que hasta que he encontrado la formula más sencilla he tenido que dar unas cuantas de vueltas. Se trata de la ejecución de macros desde Java, para eso tenemos dos maneras, hoy nos vamos a ocupar solamente de una, vamos a ejecutar una macro almacenada dentro de un Excel.

Lo primero que necesitamos es una API externa denominada JACOB que es un JAVA-COM bridge estas se utilizan normalmente para realizar llamadas a JNI (Java Native Interface) en este caso para poder realizar llamadas a las DLL que nos permite la ejecución de una macro, para ello nos tenemos que bajar el jar y el dll de la pagina de sourceforge donde se aloja el proyecto.

Ahora dentro del Eclipse, o el IDE que utilicéis para programar, tenemos que importar un nuevo JAR y a este asignarle el directorio en el que se encuentra el dll.


No os engañéis por el nombre, "LFR/lib/jacob.dll" es un directorio, no un fichero, solo que soy así de poco original.

Hay una cosa importante en este programa y es que tenéis que conocer como se llama la macro y en que pagina esta escrita ya que le tenéis que dar toda la ruta entera, de ahí que esto sea solamente un ejemplo, para tenerlo todo perfectamente programado todo esto tendría que estar parametrizado, preferiblemente desde base de datos, lo cual nos evita un pase a producción, y la función tendría que aceptar como parámetros de entrada diferenciados la ruta, el libro, la pagina y el nombre de la macro para poder montarlo nosotros de manera dinámica.

Veamos el código, os lo dejo para que podáis familiarizaros con el, es relativamente fácil, de todas maneras vamos a comentar los puntos claves para que se vea más claro.

package LFR.Liem.Dazkun.Test.Java.POIExecutor;

import java.io.File;

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;

public class POIExecutor {
 /**
  * 
  * 
  * @author Liem Dazkun
  * @param file
  *            Fichero del que se ejecuta la macro
  * @return boolean TRUE si se ejecuto correctamente la macro
  * 
  */
 public boolean openFile(String file) {
  try {
   final String macroName = "!Hoja1.multiplica";
   executeMacro(new File(file), macroName);
   return true;
  } catch (Exception e) {
   e.printStackTrace();
  }
  return true;
 }

 public boolean executeMacro(File file, String macroName) {
  ComThread.InitSTA();

  final ActiveXComponent excel = new ActiveXComponent("Excel.Application");

  try {

   final Dispatch workbooks = excel.getProperty("Workbooks")
     .toDispatch();
   final Dispatch workBook = Dispatch.call(workbooks, "Open",
     file.getAbsolutePath()).toDispatch();

   final Variant result = Dispatch.call(excel, "Run",
     new Variant(file.getName() + macroName));

   Dispatch.call(workBook, "Save");

   com.jacob.com.Variant f = new com.jacob.com.Variant(true);
   Dispatch.call(workBook, "Close", f);

  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   excel.invoke("Quit", new Variant[0]);
   ComThread.Release();
   return true;
  }

 }
}

Bueno por lo que podemos ver openFile es la que hace la llamada a la macro en este caso es una macro que tengo almacenada en la 'Hoja1' y que se llama 'multiplica', por eso os decía que era muy importante saber como se llamaban las macros, después llamamos a una función denominada executeMacro con dos parámetros, el primero será el Excel al que le ejecutaremos la macro y el segundo será el nombre de la misma.
Cargaremos el Excel que es un 'ActiveXComponent', un objeto propiedad de la librería Jacob, y lo inicializaremos como un 'Excel.Application'. Después se cargara el libro sobre el que trabajaremos y en la siguiente línea se ejecutara la macro, salvamos el libro para asegurar que las modificaciones quedan almacenadas y cerramos el flujo de datos del JNI.
Con eso ya tendríamos toda la llamada a la macro de un Excel.

Para hacer tanto tiempo que no escribo nada no esta del todo mal, ha sido una investigación interesante de hacer.

3 comentarios: (+add yours?)

Unknown dijo...

Hola. Estoy trabajando con netbeans, y me da un error
Exception in thread "main" java.lang.UnsatisfiedLinkError: no jacob-1.18-M2-x86 in java.library.path

En la parte ComThread.InitSTA();

Alguna sugerencia. Muchas Gracias

Liem Dazkun dijo...

Lo primero de todo pedirte disculpas por la tardanza Diana, por culpa de un bot tu mensaje quedo camuflado.

Lo segundo, la librería la tendrías que importar o bien directamente al buildpath o con el Maven, dependiendo de como sea tu proyecto.

Unknown dijo...

Hola, a mi me ocurría lo mismo al lanzar el .jar. Llevandote el fichero jacob-1.18-M2-x86.dll a la ruta donde hayas generado el fichero.jar funciona perfectamente.
Saludos y gracias por la info de este blog!