Automatizando Office con Visual FoxPro

29
Conferencia Visual FoxPro España 2003 Automatizando Office con Visual FoxPro Luis María Guayán Vicente Trapani S.A. Tucumán, Argentina [email protected]

description

Visual FoxPro es una poderosa herramienta, pero hay tareas que no las puede realizar él solo. Por ejemplo si tenemos una aplicación en la cual queremos enviar un correo, realizar un gráfico, escribir un documento, formatear un texto, etc., necesitamos de otras herramientas. Para automatizar estas tareas desde Microsoft Visual FoxPro, elegimos la herramienta Microsoft Office. En este documento vamos a ver como podemos Automatizar Office desde Visual FoxPro, vamos a conocer las distintas herramientas que disponemos y veremos algunos ejemplos de código.

Transcript of Automatizando Office con Visual FoxPro

Conferencia Visual FoxPro España 2003

Automatizando Officecon Visual FoxPro

Luis María GuayánVicente Trapani S.A.Tucumán, Argentina

[email protected]

Luis María Guayán es programador en lenguajes xBase desde el año 1990. En 1994 co-menzó a desarrollar en Microsoft FoxPro 2.6 para Windows. Desde allí transitó por todas las versiones de FoxPro, hasta la actualidad con la última versión de Visual FoxPro.Es el Responsable del Área de Desarrollo Informático de Vicente Trapani S.A., un estableci-miento citrícola industrial en la provincia de Tucumán, en la República Argentina.Es cofundador en el año 2000, y SysOp de PortalFox, el mayor portal gratuito para todos los desarrolladores en Visual FoxPro de habla hispana.En los años 2002 y 2003 fue nombrado por Microsoft como MVP (Most Valuable Professional) en Visual FoxPro, por su colaboración en las distintas comunidades en línea.

Automatizando Office con Visual FoxPro

Automatizando Office con Visual FoxPro

Tabla de contenidos

Introducción......................................................................................................................................... 3¿Cómo comenzar?................................................................................................................................3Instanciando un Servidor de Automatización........................................................................................3El Examinador de Objetos de Visual FoxPro.........................................................................................4Aprendamos con IntelliSense...............................................................................................................5El uso de las Macros de Office..............................................................................................................6Las constantes de Office......................................................................................................................7Combinar correspondencia con Word...................................................................................................8

Definición de la clase cWord.............................................................................................................8El programa MailMerge.prg...............................................................................................................9Los métodos en la clase cWord.........................................................................................................9

Abrir y/o crear la carta.................................................................................................................10La fuente de los datos..................................................................................................................11Combinar la carta.........................................................................................................................11Guardar la carta...........................................................................................................................11

Gráficos y tablas dinámicas con Excel................................................................................................11¿Dónde está la ayuda?....................................................................................................................12Formas de exportar los datos de Visual FoxPro a Excel...................................................................12Definición de la clase cExcel...........................................................................................................13Los métodos de la clase cExcel.......................................................................................................13

Exportar los datos........................................................................................................................14Abrir los libros exportados............................................................................................................14Guardar y cerrar el libro...............................................................................................................14

Generar un gráfico..........................................................................................................................14El programa Grafico.prg...............................................................................................................15

Generar una tabla dinámica............................................................................................................16Programa TablaDinamica.prg.......................................................................................................17Otros métodos auxiliares.............................................................................................................18

Enviar y leer correo con Outlook........................................................................................................18Versiones de Outlook......................................................................................................................19Un breve ejemplo............................................................................................................................19Problemas de seguridad..................................................................................................................19

¿Y ahora que?..............................................................................................................................20Definición de la clase cOutlook........................................................................................................20El formulario de ejemplo.................................................................................................................21Los métodos de la clase cOutlook...................................................................................................21

Enviar un correo...........................................................................................................................22Leer los correos............................................................................................................................23

Resumen............................................................................................................................................ 24

Página 2

Automatizando Office con Visual FoxPro

Introducción

Visual FoxPro es una poderosa herramienta, pero hay tareas que no las puede realizar él solo. Por ejemplo si tenemos una aplicación en la cual queremos enviar un correo, realizar un gráfico, escribir un documento, formatear un texto, etc., necesitamos de otras herramientas. Para automatizar estas tareas desde Microsoft Visual FoxPro, elegimos la herramienta Microsoft Office. En este documento vamos a ver como podemos Automatizar Office desde Visual FoxPro, vamos a conocer las distintas herramientas que disponemos y veremos algunos ejemplos de código.

Todos los ejemplos descriptos fueron realizados con Microsoft Visual FoxPro 8 y Microsoft Office XP.

¿Cómo comenzar?

Lo primero que debemos preguntarnos es ¿cómo comenzar la tarea de automatización? Esta pre-gunta pareciera que tiene una respuesta difícil, pero veremos...

La mayoría de la información disponible en: la Ayuda de Office, los artículos de la Base de Conoci -mientos de Microsoft (MSKB) o en la Red de Desarrolladores de Microsoft (MSDN) esta escrita en Visual Basic y Visual Basic for Application. Como una desventaja más para los desarrolladores en Visual FoxPro de habla hispana, la información disponible en Internet esta generalmente en inglés.

En español, existen varios artículos, ideas y trucos disponibles en los siguientes sitios de la Web:

Sitio de MSDN Latinoamérica: http://www.microsoft.com/latam/msdn Sitio de MSDN España: http://www.microsoft.com/spain/msdn Sitio de MSDN en Español: http://www.microsoft.com/spanish/msdn PortalFox: http://www.portalfox.com Revista FoxPress: http://www.fpress.com Revista UTMag (edición en español): http://www.universalthread.com/spanish/magazine

En inglés existe un excelente libro para tener en cuenta a la hora de automatizar Office: "Microsoft Office Automation with Visual FoxPro" escrito por Tamar E. Granor y Della Martin, editado en el mes de Junio de 2000. Está disponible para su compra en formato impreso y electrónico en el sitio de Hentzenwerke: http://www.hentzenwerke.com

Instanciando un Servidor de Automatización

Para comenzar a utilizar la automatización de Office, vamos a crear una instancia del servidor. Esto se logra desde Visual FoxPro con las funciones CREATEOBJECT() o GETOBJECT().

Con CREATEOBJECT() siempre se crea una nueva instancia del servidor, aunque exista una instancia de dicho servidor. Para crear una instancia de Word, Excel y Outlook se ejecutan las siguientes fun-ciones:

loWord = CREATEOBJECT('Word.Application')loExcel = CREATEOBJECT('Excel.Application')loOutlook = CREATEOBJECT('Outlook.Application')

Página 3

Automatizando Office con Visual FoxPro

Con GETOBJECT() se instancia al servidor de la siguiente forma:

loExcel = GETOBJECT( , 'Excel.Application')

Si no existe una instancia disponible del servidor, el comando fallará y aparecerá el Error OLE 1426. Para evitar este error sin importar si existe o no una instancia del servidor, pasamos como paráme-tro el nombre del archivo.

loExcel = GETOBJECT('C:\MiPlanilla.xls', 'Excel.Application')

Por defecto, la instancia del servidor estará oculta. Podemos hacer visible la instancia con la propie-dad Visible = .T. Esto lo haremos solo para ver los cambios que producimos, generalmente en la etapa de desarrollo, ya que esto hará más lento el proceso de automatización.

El Examinador de Objetos de Visual FoxPro

A partir de la versión 7 de Visual FoxPro, podemos disponer del "Examinador de Objetos" que nos permite examinar una gran cantidad de información útil sobre las propiedades y métodos de cual-quier servidor de automatización, en este caso de las aplicaciones de Microsoft Office.

El Examinador de Objetos de Visual FoxPro también nos permite ver la ubicación del archivo de ayuda, con solo hacer clic en el nodo raíz. El nombre del archivo de ayuda es mostrado en la parte inferior del Examinador de Objetos (Figura 1). Si el archivo de ayuda se encuentra en la misma car-peta de la aplicación, éste se mostrará como un vínculo y lo podemos abrir desde allí. Si el archivo de ayuda no se encuentra, el Examinador de Objetos nos indicará "No instalado". Se puede copiar el archivo de ayuda a la carpeta de la aplicación. En el caso de Microsoft Office XP en Español este se encuentra en "C:\Archivos de programa\Microsoft Office\Office10\3082\"

Figura 1: El Explorador de Objetos de Visual FoxPro

Con el Explorador de Objetos, podemos recorrer las distintas propiedades, eventos y métodos (PEMs), con la sintaxis correspondiente a cada caso (Figura 2).

Página 4

Automatizando Office con Visual FoxPro

Figura 2: Los métodos y su sintaxis

Con el Examinador de Objetos también podemos tener acceso a los valores de las distintas cons-tantes de cada aplicación (Figura 3).

Figura 3: Las constantes y sus valores

Aprendamos con IntelliSense

Uno de los cambios más llamativos a partir de la versión 7 de Visual FoxPro, es la implementación de IntelliSense. Esta herramienta nos permite conocer acerca de los objetos, sus métodos y propie-dades directamente desde la "Ventana de Comandos" o desde el "Editor de programas" de Visual FoxPro.

Página 5

Automatizando Office con Visual FoxPro

Una vez instanciado el objeto desde la "Ventana de Comandos" o declarado desde el "Editor", con solo escribir el nombre del objeto nos aparecerán sus propiedades y métodos (Figura 4) y la sintaxis correspondiente (Figura 5).

Figura 4: Las distintas propiedades y métodos mostrados con IntelliSense

Figura 5: La sintaxis de un método

El uso de las Macros de Office

Una manera fácil de comenzar la tarea de automatización, es generar el código de automatización grabando una Macro con la aplicación de Office que deseamos automatizar.

Esto lo logramos con el "Grabador de Macros" que recuerda las tareas que ejecutamos y genera au-tomáticamente un código en VBA (Visual Basic for Application).

Figura 6: Grabar una macro en Word

Página 6

Automatizando Office con Visual FoxPro

Figura 7: Código VBA generado automáticamente

Podemos fácilmente traducir el código generado en VBA a Visual FoxPro:

ChangeFileOpenDirectory 'D:\Espana\Automation\Documentos\'Documents.Open FileName:='Automation.doc', ConfirmConversions:=False, _ReadOnly:=False, AddToRecentFiles:=False, PasswordDocument:='', _PasswordTemplate:='', Revert:=False, WritePasswordDocument:='', _WritePasswordTemplate:='', Format:=wdOpenFormatAuto

El código en Visual FoxPro quedaría:

loWord.Documents.Open('D:\Espana\Automation\Documentos\Automation.doc', ;.F., .F., .F., '', '', .F., '', '',wdOpenFormatAuto)

Los pasos a seguir para esta traducción son los siguientes:

Encerrar con paréntesis la lista de parámetros Observar una llamada tipo del método y ordenar los parámetros en el orden que estos apare-

cen. Quitar los nombres de los parámetros y los símbolos ':=' Reemplazar los nombres de constantes por sus valores, como por ejemplo "True" con .T. ó

1.Una alternativa (como en el ejemplo) es definir estos valores con:

#DEFINE wdOpenFormatAuto 0#DEFINE true .T.

Las constantes de Office

Si bien disponemos de las herramientas como el Examinador de Objetos o IntelliSense para facilitar la tarea de automatización, existen otras herramientas que nos permitirán tener acceso a las cons-tantes y crear fácilmente un archivo de encabezados ("Archivo.h") para definirlas y utilizarlas direc-tamente desde Visual FoxPro.

Un ejemplo de un archivo de encabezados es:

#DEFINE olTo 1#DEFINE olCC 2#DEFINE olBCC 3

En Visual FoxPro incluimos un archivo de cabecera con la siguiente orden:

#INCLUDE 'Archivo.h'

Página 7

Automatizando Office con Visual FoxPro

Para la creación de estos archivos disponemos al menos de dos herramientas:

Código en VFP de Trevor Hancock (Base de Conocimientos de Microsoft, Artículo 285396) disponible en:http://support.microsoft.com/?scid=285396

Programa de utilidades de Rick Strahl (GetConstants) disponible libremente en:http://www.west-wind.com/webtools.asp

Combinar correspondencia con Word

En este ejemplo vamos a abrir, crear y completar con datos una carta en Microsoft Word desde Visual FoxPro, y vamos a utilizar una gran herramienta de Word como lo es "Combinar Correspon-dencia" (Mail Merge). Los datos a combinar en este ejemplo los tomaremos de la base de datos "Northwind". En un caso desde Visual FoxPro y en el otro desde un servidor SQL Server 2000.

Definición de la clase cWord

Para combinar correspondencia disponemos de una clase definida por el usuario, llamada cWord con los métodos necesarios para esta tarea. Miremos las primeras líneas de la definición de esta clase:

DEFINE CLASS cWord AS CUSTOM *-- Interfaz de ApplicationEvents2 IMPLEMENTS ApplicationEvents2 IN 'Word.Application' *-- * Propiedades *-- oWord = .NULL. && Objeto Word ... ...

En la definición de la clase cWord vemos la cláusula IMPLEMENTS que nos especifica que la definición de la clase hereda la interfaz de otro componente COM (en este caso Word). Esto nos permitirá que los eventos de Word interactúen con el código de Visual FoxPro. Con ello podremos controlar, por ejemplo si hacemos la aplicación visible, cuando el usuario cierra Word y evitar que un objeto de Visual FoxPro quede referenciado a este.

Cuando implementamos una interfaz debemos incluir en la definición de clase todos los métodos de dicha interfaz. Utilizamos el nombre de interfaz como principio del nombre de los métodos (por ejemplo, ApplicationEvents2_Quit). Así evitamos conflictos entre dos interfaces que contengan métodos con el mismo nombre.

Para esta tarea que pareciera tan compleja, usamos el Examinador de Objetos de Visual FoxPro para arrastrar y colocar una definición de interfaz en el código y ahorrar tiempo. La instrucción IMPLEMENTS, junto con los demás métodos implementados con sus parámetros, ¡¡¡se escribirá auto-máticamente!!!

Para vincular los métodos del servidor con los métodos de la interfaz implementados en el objeto de Visual FoxPro, utilizamos la función EventHandler() presente desde la versión 7 de Visual FoxPro. Este tipo de vínculo requiere que el objeto Visual FoxPro y el componente COM estén activos.

Como expresamos anteriormente, toda esta implementación en nuestra aplicación es solo para sa-ber cuando se ejecuta el evento Quit en el servidor de automatización. Cuando esto ocurre liberamos el objeto de Visual FoxPro con la siguiente sentencia en el métodoApplicationEvents2_Quit:

THIS.oWord = .NULL.

Página 8

Automatizando Office con Visual FoxPro

El programa MailMerge.prg

Este es el código de nuestro programa "MailMerge.prg", en donde creamos una instancia de la clase cWord y comenzamos a ejecutar sus métodos.

LOCAL lo AS OBJECT, loDoc AS OBJECTlo = NEWOBJECT('cWord','cWord.prg')IF lo.CrearServidor() *-- Vinculo los eventos de Word a métodos del objeto 'lo' IF NOT EVENTHANDLER(lo.oWord, lo) MESSAGEBOX('No se pudo vincular a los eventos de Word', 16, 'Error!' ) ENDIF *-- Maximizo y hago visible lo.oWord.WINDOWSTATE = 1 && wdWindowStateMaximize lo.oWord.VISIBLE = .T. loDoc = lo.AbrirCarta('Carta') lo.GenerarDataSource('CSV') *lo.GenerarDataSource('ODC') lo.CombinarCarta(loDoc) lo.GuardarCarta(loDoc, .T.) *-- Desvinculo los eventos de Word IF NOT EVENTHANDLER(lo.oWord, lo, .T.) MESSAGEBOX('No se pudo desvincular a los eventos de Word', 16, 'Error!' ) ENDIFELSE MESSAGEBOX('No se pudo instanciar el servidor', 16, 'Error!')ENDIFlo = .NULL.RETURN

Los métodos en la clase cWord

En primer lugar invocamos el método CrearServidor() que nos crea una instancia de Word. Ante–riormente vimos que podíamos instanciar a Word con la función:GETOBJECT('Word.Application') y si no existía una instancia de Word, surge el Error OLE 1426.

Para evitar esto, Visual FoxPro 8 trae un nuevo mecanismo para el manejo de errores, implementado mediante el bloque de sentencias TRY ... CATCH ... FINALLY.

El código que potencialmente puede producir un error, se aísla en la cláusula TRY (estas se pueden anidar), y cuando el error efectivamente ocurre, el control de la ejecución pasa al código en la cláu-sula CATCH, en el orden que aparecen dentro del bloque (en forma similar a cómo funciona el DO CASE). La cláusula opcional FINALLY, se ejecuta, se haya producido o no el error.

Lo novedoso de este mecanismo es que siempre que se produce un error dentro de un bloque TRY ... CATCH ... FINALLY, Visual FoxPro crea un objeto de la nueva clase Exception.

*-- Manejo el error con TRY ... CATH ... FINALLYTRY *-- Instancio el objeto THIS.oWord = GETOBJECT( , 'Word.Application') WAIT WINDOW 'Ya existe una instancia de Word...' TIMEOUT 2CATCH TRY *-- Creo el objeto THIS.oWord = CREATEOBJECT('Word.Application') WAIT WINDOW 'Nueva instancia de Word...' TIMEOUT 2 CATCH MESSAGEBOX('Microsoft Word no está instalado.', 16, 'Problemas!!!')

Página 9

Automatizando Office con Visual FoxPro

FINALLY ENDTRYFINALLYENDTRYRETURN VARTYPE(THIS.oWord) = 'O'

Para que este ejemplo sea más descriptivo, vamos a hacer visible a la aplicación Word para ver paso a paso como se ejecutan los distintos métodos. Esto lo hacemos en la sentencia:

lo.oword.VISIBLE = .T.

Abrir y/o crear la cartaEl método AbrirCarta(), abre la carta de Word si esta existe o crea una nueva carta con el método CrearCarta(). Ambos métodos retornan un objeto Document de Word.

PROCEDURE AbrirCarta(tcArchivo) LOCAL loDoc AS 'Word.Document' tcArchivo = FORCEEXT(tcArchivo,'DOC') IF NOT FILE(THIS.cDirDoc + tcArchivo) *-- Si no existe la carta, la creo loDoc = THIS.CrearCarta(tcArchivo) ELSE *-- Si existe la carta, la abro loDoc = THIS.oWord.Documents.OPEN(THIS.cDirDoc + tcArchivo) *-- y me aseguro que no tiene un documento asociado loDoc.MailMerge.MainDocumentType = -1 && wdNotAMergeDocument ENDIF *-- Retorno un objeto Document RETURN loDocENDPROC

PROCEDURE CrearCarta(tcArchivo) LOCAL loDoc AS 'Word.Document' *-- Creo un nuevo documento loDoc = THIS.oWord.Documents.ADD(,,0) *-- Guardo el documento como... loDoc.SAVEAS(THIS.cDirDoc + tcArchivo) *-- Activo el documento loDoc.ACTIVATE *-- Comienzo a 'escribir' el documento WITH THIS.oWord.SELECTION .FONT.NAME = 'Tahoma' .FONT.SIZE = 10 ... ENDWITH RETURN loDocENDPROC

En el caso de crear o abrir una carta ya existente de Word, estas deben contener los nombres de los campos para su reemplazo en la combinación. Estas cartas serán los documentos principales para la combinación.

La fuente de los datosTambién debemos crear o abrir los documentos con los datos a combinar. En este ejemplo tenemos dos casos:

Crear un archivo tipo CSV (Valores Separados por Comas) desde una cláusula SELECT a la ta-bla "Customers" de la base de datos "Northwind" (que viene con Visual FoxPro).

Página 10

Automatizando Office con Visual FoxPro

Mediante una Conexión de Datos de Office (ODC) ya existente, traer los datos de la tabla "Customers" de la base de datos "Northwind", pero esta vez desde un servidor SQL Server 2000.

Para esta tarea tenemos el método GenerarDataSource() que crea el archivo con los datos y esta-blece la propiedad cDataSource.

Combinar la cartaEn el método CombinarCarta() ejecutamos las siguientes sentencias para:

Hacer la carta del tipo Documento Principal. Abrir el archivo con la fuente de datos. Ejecutar la combinación

WITH toDoc.MailMerge .MainDocumentType = 0 && wdFormLetters .OpenDataSource(THIS.cDataSource) .Execute()ENDWITH

Guardar la cartaPara finalizar tenemos el método GuardarCarta() que guarda el documento principal, con la posibi-lidad mediante un parámetro de cerrar el documento.

PROCEDURE GuardarCarta(toDoc, tlCierra) *-- Guardo el documento toDoc.SAVE() IF tlCierra *-- Cierro el documento toDoc.CLOSE() ENDIFENDPROC

En este ejemplo el documento combinado que se genera quedará abierto, entonces hacemos la apli-cación visible para que el usuario lo guardar directamente de la ventana de Word. También estable-cemos la carpeta donde están los documentos de este ejemplo, para que Word por defecto la selec-cione en la ventana de "Guardar...".

WITH THIS.oWord .ChangeFileOpenDirectory(THIS.cDirDoc) .VISIBLE = .T.ENDWITH

Gráficos y tablas dinámicas con Excel

En nuestras aplicaciones podemos aumentar el impacto de los resultados de las consultas, mostran-do los datos en distintos formatos. Para ello vamos a recurrir a dos grandes herramientas de Excel como lo son los gráficos y las tablas dinámicas.

Antes de comenzar a analizar el ejemplo en Visual FoxPro vamos a comprender algunos conceptos para poder hacer más fácil la automatización de Excel desde Visual FoxPro, esto siempre lo logramos con los archivos de ayuda. También vamos a conocer distintas formas que disponemos para pasar los datos de Visual FoxPro a Excel.

Página 11

Automatizando Office con Visual FoxPro

¿Dónde está la ayuda?

Para tener una idea mas clara acerca de todos los objetos de Excel, debemos siempre examinar la ayuda disponible. Parte de esta ayuda la podemos encontrar en el tema "Referencia Visual Basic de Microsoft Excel" en el archivo de ayuda "vbaxl10.chm" en la carpeta de la instalación de Office.

En esta ayuda podemos conocer acerca de los objetos disponibles en Excel, como por ejemplo el objeto Chart (Figura 8)

Figura 8: El Objeto Chart de Excel

Formas de exportar los datos de Visual FoxPro a Excel

La forma mas simple, es seleccionar el área de trabajo con datos de Visual FoxPro (estos pueden estar en una tabla, un cursor o una vista) y ejecutar las ordenes COPY TO ... ó EXPORT TO ... con los parámetros correspondientes. Si elegimos esta manera, solo necesitamos tener activa la aplica-ción de Visual FoxPro.

SELECT 'MiCursor'COPY TO 'C:\Planilla1' TYPE XL5EXPORT TO 'C:\Planilla2' TYPE XL5

Otra forma que disponemos es usando el método DataToClip() del objeto Application de Visual FoxPro ('VisualFoxPro.Application'). La variable del sistema _VFP hace referencia al objeto Application de la instancia actual. De esta manera copiamos un conjunto de registros como texto al "Portapapeles". Recordar que el tercer parámetro del método DataToClip() debemos configurarlo en 3 para que los campos se delimiten con tabulaciones.

SELECT 'MiCursor'GO TOP_VFP.DataToClip(,,3)

Una vez que tenemos los datos en el Portapapeles, creamos una instancia de Excel desde Visual FoxPro, creamos un nuevo Libro y pegamos los datos desde el Portapapeles con el método Paste().

loExcel = CREATEOBJECT('Excel.Application')loLibro = loExcel.Workbooks.Add()WITH loLibro .Activate()

Página 12

Automatizando Office con Visual FoxPro

.ActiveSheet.Paste() .SaveAs('C:\Planilla3.xls')ENDWITHloExcel.Visible = .T.STORE .NULL. TO loLibro, loExcel

Otra forma de pasar los datos de Visual FoxPro a Excel, es tener ambas aplicaciones activas y reco-rrer los datos de nuestra tabla y escribirlos directamente en una Hoja de Excel celda por celda. Esta opción es mucho más lenta que las anteriores, pero es útil cuando la hoja de Excel ya tiene un for-mato establecido o cuando queremos escribir fórmulas en la hoja de Excel.

SELECT 'MiCursor'loExcel = CREATEOBJECT('Excel.Application')loLibro = loExcel.Workbooks.Add()loHoja = loLibro.ActiveSheet()lnFil = 1 && Nombres de camposFOR lnCol = 1 TO FCOUNT() loHoja.Cells(1,lnCol).VALUE = FIELD(lnCol)ENDFORlnFil = lnFil + 1 && Resto de las filasSCAN ALL FOR lnCol = 1 TO FCOUNT() loHoja.Cells(lnFil,lnCol).VALUE = EVALUATE(FIELD(lnCol)) ENDFOR lnFil = lnFil + 1ENDSCANloLibro.SaveAs('C:\Planilla4.xls')loExcel.Visible = .T.STORE .NULL. TO loHoja, loLibro, loExcel

Definición de la clase cExcel

En esta clase definida por el usuario tendremos los métodos que nos permitirán crear y manejar los gráficos y las tablas dinámicas de Excel desde Visual FoxPro.

DEFINE CLASS cExcel AS CUSTOM *-- * Propiedades *-- oExcel = .NULL. ...

Los métodos de la clase cExcel

El primer método que vamos a invocar de esta clase es CrearServidor() que establece la propie-dad oExcel como una referencia a la instancia de Excel creada.

THIS.oExcel = CREATEOBJECT('Excel.Application')

Al igual que en la clase cWord que vimos anteriormente, este método tiene el mismo manejo de errores con el bloque TRY ... CATCH ... FINALLY para manejar el error OLE 1426.

Exportar los datosPara exportar nuestros datos de Visual FoxPro a Excel vamos a elegir la opción de COPY TO ... que la ejecutamos en el método ExportarDatos().

PROCEDURE ExportarDatos(tcCursor) LOCAL lcArchivo AS CHARACTER

Página 13

Automatizando Office con Visual FoxPro

lcArchivo = FORCEEXT(THIS.cDirDoc + tcCursor, 'XLS') *-- Opción COPY TO ... SELECT (tcCursor) COPY TO (lcArchivo) TYPE XL5 RETURNENDPROC

Abrir los libros exportadosCon el método AbrirLibro() vamos a abrir el libro para comenzar con la automatización. Este mé-todo retorna un objeto WorkBook de Excel.

PROCEDURE AbrirLibro(tcArchivo) LOCAL loLibro AS 'Excel.Workbook' tcArchivo = FORCEEXT(tcArchivo,'XLS') IF FILE(THIS.cDirDoc + tcArchivo) loLibro = THIS.oExcel.Workbooks.OPEN(THIS.cDirDoc + tcArchivo) ELSE *-- Si no existe el libro loLibro = .NULL. ENDIF *-- Retorno un objeto Workbook RETURN loLibroENDPROC

Guardar y cerrar el libroCon el método GuardarLibro() vamos a guardar el libro. Este método tiene un parámetro adicional que indica si se cierra el libro luego de guardarlo.

PROCEDURE GuardarLibro(toLibro, tlCierra) *-- Guardo el Libro toLibro.SAVE() IF tlCierra *-- Cierro el Libro toLibro.CLOSE() ENDIF RETURNENDPROC

Generar un gráfico

A partir de los resultados de una consulta a las tablas de la base de datos "Northwind", generaremos un gráfico del tipo de columnas en 3 dimensiones (xl3DColumnStacked) como lo vemos en la Figura 9, y daremos formato a algunos objetos del gráfico, como los títulos, los ejes y las barras.

Página 14

Automatizando Office con Visual FoxPro

Figura 9: Grafico de barras en 3 dimensiones

El programa Grafico.prgEn este programa vamos a crear un objeto cExcel y ejecutaremos los métodos vistos anteriormente para la creación del gráfico.

LOCAL lo AS OBJECT, loLibro AS OBJECTlo = NEWOBJECT('cExcel','cExcel.prg')*-- Genero cursor y exporto datoslo.VtaAnualEmpleado(1997, 'VtaAnualEmpleado')lo.ExportarDatos('VtaAnualEmpleado')IF lo.CrearServidor() *-- Maximizo y hago visible lo.oExcel.WINDOWSTATE = -4137 && xlMaximized lo.oExcel.VISIBLE = .T. *-- Abro el libro copiado loLibro = lo.AbrirLibro('VtaAnualEmpleado') *-- Genero gráfico lo.GenerarGrafico(loLibro, 'Ventas Anuales por Empleado (AÑO 1997)') *--- Grabo planilla y cierro lo.GuardarLibro(loLibro, .T.) *-- Cierro el servidor lo.CerrarServidor()ELSE MESSAGEBOX('No se pudo instanciar el servidor', 16, 'Error!')ENDIFlo = .NULL.RETURN

El método GenerarGrafico() de la clase cExcel, es el que hace la tarea propiamente dicha.

PROCEDURE GenerarGrafico(toLibro, tcTitulo) LOCAL lcRango AS CHARACTER, oGrafico AS 'Excel.Chart', ; loHoja AS 'Excel.WorkSheet' loHoja = toLibro.ActiveSheet lcRango = 'A1:' + CHR(loHoja.UsedRange.COLUMNS.COUNT + 64) + ; ALLTRIM(STR(loHoja.UsedRange.ROWS.COUNT)) loGrafico = THIS.oExcel.Charts.ADD WITH loGrafico

Página 15

Automatizando Office con Visual FoxPro

*-- Tipo, rango y localización .ChartType = 55 && xl3DColumnStacked .SetSourceData(loHoja.RANGE(lcRango), 2) .Location(1, 'Ventas Anuales') && xlLocationAsNewSheet = 1 *-- Titulo .HasTitle = .T. .ChartTitle.TEXT = tcTitulo WITH .ChartTitle.FONT .NAME = 'Arial' .SIZE = 14 .Bold = .T. ENDWITH *-- Ejes WITH .Axes(1,1) .HasTitle = .T. .AxisTitle.TEXT = 'Empleado' ENDWITH WITH .Axes(2,1) .HasTitle = .T. .AxisTitle.TEXT = '$' ENDWITH *-- Sin Leyendas .HasLegend = .F. *-- Formato 3D .RightAngleAxes = .T. .AutoScaling = .T. *-- Grupo de gráfico WITH .ChartGroups(1) *-- Ancho separación barras .GapWidth = 50 ENDWITH ENDWITH RETURNENDPROC

Generar una tabla dinámica

Nuestro programa de ejemplo es "TablaDinamica.prg" que crea una instancia de la clase cExcel y ejecuta sus métodos para generar la tabla dinámica.

En nuestro ejemplo vamos a generar una tabla dinámica que nos muestra las Ventas Anuales agru-padas por País, Empleado y Tipo de Producto.

Figura 10: Diseño de la tabla dinámica

Página 16

Automatizando Office con Visual FoxPro

Con anterioridad a la programación de los métodos debemos diseñar la forma de la tabla dinámica que deseamos generar. El diseño elegido lo observamos en la Figura 10.

Programa TablaDinamica.prgEste es el código de nuestro programa:

LOCAL lo AS OBJECT, loLibro AS OBJECTlo = NEWOBJECT('cExcel','cExcel.prg')*-- Genero cursor y exporto datoslo.VtaAnualPais('VtaAnualPais')lo.ExportarDatos('VtaAnualPais')IF lo.CrearServidor() *-- Maximizo y hago visible lo.oExcel.WINDOWSTATE = -4137 && xlMaximized lo.oExcel.VISIBLE = .T. *-- Abro el libro copiado loLibro = lo.AbrirLibro('VtaAnualPais') *-- Genero tabla dinámica lo.GenerarTablaDinamica(loLibro) *--- Grabo planilla y cierro lo.GuardarLibro(loLibro, .T.) *-- Cierro el servidor lo.CerrarServidor()ELSE MESSAGEBOX('No se pudo instanciar el servidor', 16, 'Error!')ENDIFlo = .NULL.RETURN

Para generar la tabla dinámica ejecutamos el método GenerarTablaDinamica() de la clase cExcel. Este método nos genera la tabla dinámica (Figura 11) ordenada descendentemente por las Ventas por País.

PROCEDURE GenerarTablaDinamica(toLibro) LOCAL laPagina(1), laFilas(2), laColumnas(1), lcRango *--- Arrays con los datos de la tabla dinámica laPagina(1)='Anio' laFilas(1)='Pais' laFilas(2)='Empleado' laColumnas(1)='TipoProd' lcRango = 'A1:E1275' WITH THIS.oExcel *--- Formato de los datos hoja principal .Cells.SELECT .SELECTION.COLUMNS.AUTOFIT .COLUMNS('E:E').SELECT .SELECTION.NumberFormat = '$ #,##0.00' .RANGE('A2').SELECT *--- Llamo al generador de Tablas Dinámicas .ActiveSheet.PivotTableWizard(1, lcRango, '', 'TablaDinamica') *--- Armo la Tabla dinámica WITH .ActiveSheet.PivotTables('TablaDinamica') .AddFields(@laFilas, @laColumnas, @laPagina) WITH .PivotFields('Ventas') .ORIENTATION = 4 && xlDataField .NumberFormat = '$ #,##0.00' ENDWITH .PivotFields('Pais').AutoSort(2,'Suma de ventas') && xlDescending = 2 ENDWITH

Página 17

Automatizando Office con Visual FoxPro

*--- Formato de los datos hoja tabla dinámica .Cells.SELECT .SELECTION.COLUMNS.AUTOFIT .ActiveSheet.NAME = 'Ventas Anuales' .RANGE('A2').SELECT ENDWITH RETURNENDPROC

Figura 11: Tabla dinámica generada

Otros métodos auxiliaresEn la clase cExcel también existen otros métodos, solo válidos para este ejemplo.

Estos métodos son VtaAnualPais() y VtaAnualEmpleado() utilizados para crear los cursores con los datos para exportar a Excel. Estos cursores toman los datos de la base de datos "Northwind" que se encuentra en la carpeta "\Samples\Northwind" de la carpeta de la instalación original de Visual FoxPro.

Enviar y leer correo con Outlook

El modelo de objetos de Outlook es muy rico y poderoso. Esta interfaz está disponible como un ser-vidor de automatización, o sea, que todo lo podemos hacer por programa desde Visual FoxPro.

Como en temas anteriores, siempre recurriremos a la ayuda. Allí podemos ver los distintos objetos de Outlook (Figura 12).

Figura 12: La ayuda de Outlook en el archivo "vbaol10.chm"

Lo primero que debemos hacer para automatizar Outlook, es crear un objeto Outlook, similar a como vimos anteriormente con las otras herramientas de Office.

Página 18

Automatizando Office con Visual FoxPro

Una vez creado el objeto, debemos acceder al origen de los datos, pero esto no lo logramos en forma directa, debemos crear un objeto "NameSpace" apropiado que actuará como entrada (en este ejemplo MAPI). El objeto NameSpace proporciona entre otros, los métodos Logon y Logoff.

Versiones de Outlook

Seguramente conocemos varias versiones de Outlook, solo daremos las principales características de cada uno.

Outlook Express: No es Outlook, es un programa completamente diferente y no es servidor de automatización.

Outlook 97 y 2000: Existen dos versiones, Internet Mail Only (IMO) y Corporate Workgroup (C/W). Ambas trabajan con Internet Mail, pero solo Corporate Workgroup trabaja con la in-terfaz MAPI.

Outlook XP: Combina las versiones Internet Mail y Corporate Workgroup.

Un breve ejemplo

Una de las tareas más fácil de automatizar en Outlook es el envío de un correo. Veremos un ejemplo de solamente unas pocas líneas.

LOCAL lcPerfil AS CHARACTER, lcContrasenia AS CHARACTER , ; lcDestinatario AS CHARACTER, lcTema AS CHARACTER , ; lcCuerpo AS CHARACTERLOCAL loOutlook AS "Outlook.Application", ; loNameSpace AS OBJECT, loMailItem AS OBJECT#DEFINE LF_CR CHR(10)+CHR(13)*-- Datos del correolcPerfil = "Prueba"lcContrasenia = "prueba"lcDestinatario = "[email protected]"lcTema = "Prueba"lcCuerpo = "Prueba desde la Conferencia Visual FoxPro España 2003." + LF_CRlcCuerpo = lcCuerpo + "Saludos desde A Coruña." + LF_CR*-- Creo objetoa Outlook y NameSpaceloOutlook = CREATEOBJECT("Outlook.Application")loNameSpace = loOutlook.GetNameSpace("MAPI")*-- Ejecuto los métodosloNameSpace.Logon(lcPerfil , lcContrasenia)loMailItem = loOutlook.CreateItem(0)loMailItem.Recipients.ADD(lcDestinatario)loMailItem.Subject = lcTemaloMailItem.Body = lcCuerpoloMailItem.SENDloNameSpace.Logoff

loNameSpace = .NULL.loOutlook = .NULL.

Problemas de seguridad

Outlook XP y Outlook 2000 SP2, incluyen los parches de seguridad de Microsoft. Estos parches res-tringen, entre otras cosas, el acceso a la libreta de direcciones y el envío de correo mediante auto-matización, con el fin de evitar códigos maliciosos que toman los datos de nuestra libreta de direc-ciones y envían correo sin nuestro consentimiento.

Cuando intentamos enviar un correo desde Visual FoxPro, se nos presenta el siguiente cuadro de dialogo, que luego de 5 segundos habilita el botón "Si" (Figura 13).

Página 19

Automatizando Office con Visual FoxPro

Figura 13: "...intentando enviar correo..."

Cuando intentamos acceder a la libreta de direcciones aparece el cuadro de dialogo el cual nos per-mitirá un acceso inmediato, o de 1, 2, 5, ó 10 minutos que debemos seleccionar (Figuras 14).

Figura 14: "...intentando tener acceso a direcciones..."

¿Y ahora que? Estas son algunas de las opciones disponemos nosotros para trabajar con estos parches de seguri-dad:

Mantener la versión de Office 2000 SR-1 y no actualizarla ni instalarle parches de seguridad, con los peligros que esto significa.

Si se tienen Outlook y Exchange instalados, el administrador de Exchange, puede disminuir las alertas o registrar algunas aplicaciones como seguras.

Outlook Redemption: Es un objeto COM que se adapta fácilmente a la automatización y utiliza la MAPI extendida. Esta DLL fue escrita por Dmitry Streblechenko (MS Outlook MVP) y esta dispo-nible en http://www.dimastr.com/redemption. Este es un producto comercial con un valor de U$S 200 aproximadamente. Existe para descarga una versión libre con fines de desarrollo.

Express ClickYes: Es un pequeño programa residente que se maneja mediante la API de Win-dows. Este "presionará" el botón "Si" antes de que el dialogo aparezca. Este programa es gratis y esta disponible en http://www.express-soft.com/mailmate/clickyes.html. En el mismo sitio existe un ejemplo para Visual FoxPro.

Definición de la clase cOutlook

En este ejemplo disponemos de una clase definida por el usuario, llamada cOutlook con los distintos métodos para realizar el envío y la lectura de los correos. La definición de esta clase es la siguiente:

DEFINE CLASS cOutlook AS CUSTOM *-- Heredo la interfaz de _Application Events_10 IMPLEMENTS ApplicationEvents_10 IN 'Outlook.Application' *-- Propiedades oOutlook = .NULL. oNameSpace = .NULL. ... ...

Página 20

Automatizando Office con Visual FoxPro

Como vimos anteriormente en la definición de la clase cWord, la sentencia IMPLEMENTS nos permi-tirá interactuar con los eventos de Outlook desde Visual FoxPro.

Solo pondremos código para interactuar con el evento Quit de Outlook en el método ApplicationEvents_10_Quit():

WITH THIS .oNameSpace = .NULL. .oOutlook = .NULL.ENDWITH

El formulario de ejemplo

En este ejemplo utilizaremos un formulario con un objeto PageFrame con dos Páginas, una para en-viar correo y la otra para leer los correos desde la Bandeja de Entrada.

En el método Init() creamos una instancia de la clase cOutlook y vinculamos los eventos de Outlook:

THISFORM.oCorreo = NEWOBJECT('cOutlook','cOutlook.prg')IF THISFORM.oCorreo.CrearServidor() *-- Vinculo los eventos de Outlook a métodos del objeto oCorreo IF NOT EVENTHANDLER(THISFORM.oCorreo.oOutlook, THISFORM.oCorreo) MESSAGEBOX('No se pudo vincular a los eventos de Outllok', 16, 'Error!' ) ENDIFENDIF

También en el método Init() llamamos a un formulario para el inicio de sesión:

DO FORM Inicio WITH THISFORM.oCorreo TO llAceptarIF NOT (llAceptar AND THISFORM.oCorreo.IniciarSesion()) MESSAGEBOX('Falló el inicio sesión', 48, 'Inicio de sesión') RETURN .F.ENDIF

Los métodos de la clase cOutlook

En el inicio del formulario invocábamos al método CrearServidor() que establece una referencia a la instancia de Outlook en la propiedad oOutlook.

THIS.oOutlook = CREATEOBJECT('Outlook.Application')

Igualmente como en las clases anteriores, este método tiene el manejo de errores con el bloque TRY ... CATCH ... FINALLY.

En este método también creamos un objeto NameSpace que nos permitirá acceder a las carpetas especiales de Outlook.

THIS.oNameSpace = THIS.oOutlook.GetNameSpace('MAPI')

Enviar un correoAntes de invocar el método EnviarCorreo(), configuramos todas las propiedades necesarias para el envío de correo. Esto lo hacemos en el método Click() del botón "Enviar".

WITH THISFORM.oCorreo .CargarVector(THIS.PARENT.txtTo.VALUE, 'aTO')

Página 21

Automatizando Office con Visual FoxPro

.CargarVector(THIS.PARENT.txtCC.VALUE, 'aCC') .CargarVector(THIS.PARENT.txtAdjunto.VALUE, 'aAdjuntos') .cTema = ALLTRIM(THIS.PARENT.txtTema.VALUE) .cCuerpo = ALLTRIM(THIS.PARENT.edtCuerpo.VALUE) IF .EnviarCorreo() MESSAGEBOX('Mensaje enviado con éxito.', 64, 'Aviso') THISFORM.LimpiarPagina() ELSE MESSAGEBOX('No se pudo enviar el mensaje.', 48, 'Problemas') ENDIFENDWITHRETURN En el método EnviarCorreo() de la clase cOutlook creo un nuevo mensaje y lo armo según las pro-piedades anteriormente configuradas.

PROCEDURE EnviarCorreo() LOCAL loMensaje AS OBJECT, llRet AS Logical LOCAL lnI AS INTEGER, lnIndex AS INTEGER *-- Creo un nuevo mensaje WITH THIS loMensaje = .oOutlook.CreateItem(0) IF VARTYPE(loMensaje) = 'O' loMensaje.Subject = .cTema loMensaje.Body = .cCuerpo *-- Recipientes lnIndex = 0 *-- TO lnLen = ALEN(.aTO) FOR lnI = 1 TO lnLen IF NOT EMPTY(.aTO(lnI)) lnIndex = lnIndex + 1 loMensaje.Recipients.ADD(.aTO(lnI)) loMensaje.Recipients(lnIndex).TYPE = 1 ENDIF ENDFOR *-- CC lnLen = ALEN(.aCC) FOR lnI = 1 TO lnLen IF NOT EMPTY(.aCC(lnI)) lnIndex = lnIndex + 1 loMensaje.Recipients.ADD(.aCC(lnI)) loMensaje.Recipients(lnIndex).TYPE = 2 ENDIF ENDFOR *-- BCC lnLen = ALEN(.aBCC) FOR lnI = 1 TO lnLen IF NOT EMPTY(.aBCC(lnI)) lnIndex = lnIndex + 1 loMensaje.Recipients.ADD(.aBCC(lnI)) loMensaje.Recipients(lnIndex).TYPE = 3 ENDIF ENDFOR *-- Adjuntos lnLen = ALEN(.aAdjuntos) FOR lnI = 1 TO lnLen IF NOT EMPTY(.aAdjuntos(lnI)) AND FILE(.aAdjuntos(lnI)) loMensaje.Attachments.ADD(.aAdjuntos(lnI)) ENDIF ENDFOR

Página 22

Automatizando Office con Visual FoxPro

llRet = loMensaje.SEND ELSE llRet = .F. ENDIF ENDWITH RETURN llRetENDPROC

Leer los correosPara leer los correos de la bandeja de entrada invocamos el método LeerMensajes() de la clase cOutlook desde el método Click() del botón "Leer".

ZAP IN curMsgIF THISFORM.oCorreo.LeerMensajes(THIS.PARENT.opgTipo.VALUE = 1, 'curMsg') GO TOP IN curMsg THIS.PARENT.grdMensajes.SETFOCUSELSE MESSAGEBOX('No existen mensajes para traer', 64, 'Aviso')ENDIFTHIS.PARENT.edtCuerpo.REFRESH

En el método LeerMensajes() creamos un objeto loInbox y traemos todos los mensajes, o solo los mensajes "No leídos" y recorremos uno a uno para cargarlos en un cursor que luego mostraremos en una Grilla y un Cuadro de Edición.

PROCEDURE LeerMensajes(tlNoLeidos, tcAlias) LOCAL loInbox AS 'Outlook.MAPIFolder', loMensajes AS 'Outlook.Items' LOCAL loMsg AS OBJECT, lnI AS INTEGER, llRet AS Logical IF EMPTY(tcAlias) tcAlias = 'curMsg' ENDIF *-- Inbox loInbox = THIS.oNameSpace.GetDefaultFolder(6) *-- Mensajes del Inbox IF tlNoLeidos loMensajes = loInbox.Items.RESTRICT("[Unread] = True") ELSE loMensajes = loInbox.Items ENDIF IF VARTYPE(loMensajes) = 'O' WITH loMensajes IF .Count > 0 *-- Recorro los mensajes FOR lnI = 1 TO .COUNT loMsg = .ITEM(lnI) WITH loMsg INSERT INTO (tcAlias) ; (EnviadoPor, Tema, Recibido, Cuerpo, NoLeido) ; VALUES (.SenderName, .Subject, .ReceivedTime, .Body, .UnRead) ENDWITH ENDFOR llRet = .T. ELSE llRet = .F. ENDIF ENDWITH ELSE llRet = .F. ENDIF

Página 23

Automatizando Office con Visual FoxPro

RETURN llRetENDPROC

Resumen

En este documento vimos solo algunos ejemplos de automatización de Office. Las posibilidades son muchas y cada una depende de la solución que debemos implementar en nuestras aplicaciones. Re-cuerden que todo el poder que nos brindan las distintas herramientas de Office, pueden ser maneja-das desde Visual FoxPro. Solo es cuestión de adoptar alguna de las técnicas vistas y ponerse a tra-bajar...

Página 24