Automatizando Office con Visual FoxPro

25 

Loading.... (view fulltext now)

Loading....

Loading....

Loading....

Loading....

Texto completo

(1)

Automatizando Office

con Visual FoxPro

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

luismaria@portalfox.com

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.

(2)
(3)

Automatizando Office con Visual FoxPro

Tabla de contenidos

Automatizando Office con Visual FoxPro ... 3

Tabla de contenidos ... 3

Introducción ... 4

¿Cómo comenzar? ... 4

Instanciando un Servidor de Automatización ... 4

El Examinador de Objetos de Visual FoxPro ... 5

Aprendamos con IntelliSense ... 6

El uso de las Macros de Office ... 7

Las constantes de Office ... 8

Combinar correspondencia con Word ... 9

Definición de la clase cWord ... 9

El programa MailMerge.prg ... 10

Los métodos en la clase cWord ... 10

Abrir y/o crear la carta ... 11

La fuente de los datos ... 12

Combinar la carta ... 12

Guardar la carta ... 12

Gráficos y tablas dinámicas con Excel ... 12

¿Dónde está la ayuda? ... 13

Formas de exportar los datos de Visual FoxPro a Excel ... 13

Definición de la clase cExcel ... 14

Los métodos de la clase cExcel ... 14

Exportar los datos ... 15

Abrir los libros exportados ... 15

Guardar y cerrar el libro ... 15

Generar un gráfico ... 15

El programa Grafico.prg ... 16

Generar una tabla dinámica ... 17

Programa TablaDinamica.prg ... 18

Otros métodos auxiliares ... 19

Enviar y leer correo con Outlook ... 19

Versiones de Outlook ... 20

Un breve ejemplo ... 20

Problemas de seguridad ... 20

¿Y ahora que? ... 21

Definición de la clase cOutlook ... 21

El formulario de ejemplo ... 22

Los métodos de la clase cOutlook ... 22

Enviar un correo ... 23

Leer los correos ... 24

(4)

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')

(5)

loOutlook = CREATEOBJECT('Outlook.Application')

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).

(6)

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.

(7)

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).

(8)

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

(9)

#INCLUDE 'Archivo.h'

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.

(10)

ApplicationEvents2_Quit: THIS.oWord = .NULL.

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 OBJECT lo = 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!' ) ENDIF

ELSE

MESSAGEBOX('No se pudo instanciar el servidor', 16, 'Error!') ENDIF

lo = .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, implemen-tado 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 ... FINALLY TRY

*-- Instancio el objeto

THIS.oWord = GETOBJECT( , 'Word.Application')

WAIT WINDOW 'Ya existe una instancia de Word...' TIMEOUT 2 CATCH

(11)

*-- 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!!!') FINALLY

ENDTRY FINALLY ENDTRY

RETURN 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 carta

El 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 loDoc

ENDPROC

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 loDoc ENDPROC

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.

(12)

La fuente de los datos

Tambié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).

 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 carta

En 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 carta

Para 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() ENDIF ENDPROC

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 mostran-dos grandes herramientas de Excel como lo son los gráficos y las tablas dinámicas.

(13)

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.

¿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 XL5 EXPORT 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

(14)

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() .ActiveSheet.Paste() .SaveAs('C:\Planilla3.xls') ENDWITH loExcel.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 campos FOR lnCol = 1 TO FCOUNT()

loHoja.Cells(1,lnCol).VALUE = FIELD(lnCol) ENDFOR

lnFil = lnFil + 1 && Resto de las filas SCAN ALL

FOR lnCol = 1 TO FCOUNT()

loHoja.Cells(lnFil,lnCol).VALUE = EVALUATE(FIELD(lnCol)) ENDFOR lnFil = lnFil + 1 ENDSCAN loLibro.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.

(15)

Exportar los datos

Para 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

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

SELECT (tcCursor)

COPY TO (lcArchivo) TYPE XL5 RETURN

ENDPROC

Abrir los libros exportados

Con 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 loLibro

ENDPROC

Guardar y cerrar el libro

Con 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 RETURN ENDPROC

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.

(16)

Figura 9: Grafico de barras en 3 dimensiones

El programa Grafico.prg

En 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 OBJECT lo = NEWOBJECT('cExcel','cExcel.prg') *-- Genero cursor y exporto datos

lo.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!') ENDIF

lo = .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

(17)

*-- 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 RETURN ENDPROC

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.

(18)

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.prg

Este es el código de nuestro programa: LOCAL lo AS OBJECT, loLibro AS OBJECT lo = NEWOBJECT('cExcel','cExcel.prg') *-- Genero cursor y exporto datos lo.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!') ENDIF

lo = .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

(19)

ENDWITH

*--- Formato de los datos hoja tabla dinámica .Cells.SELECT

.SELECTION.COLUMNS.AUTOFIT

.ActiveSheet.NAME = 'Ventas Anuales' .RANGE('A2').SELECT

ENDWITH RETURN ENDPROC

Figura 11: Tabla dinámica generada

Otros métodos auxiliares

En 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).

(20)

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

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 CHARACTER

LOCAL loOutlook AS "Outlook.Application", ; loNameSpace AS OBJECT, loMailItem AS OBJECT #DEFINE LF_CR CHR(10)+CHR(13)

*-- Datos del correo lcPerfil = "Prueba" lcContrasenia = "prueba"

lcDestinatario = "luismaria@portalfox.com" lcTema = "Prueba"

lcCuerpo = "Prueba desde la Conferencia Visual FoxPro España 2003." + LF_CR lcCuerpo = lcCuerpo + "Saludos desde A Coruña." + LF_CR

*-- Creo objetoa Outlook y NameSpace

loOutlook = CREATEOBJECT("Outlook.Application") loNameSpace = loOutlook.GetNameSpace("MAPI") *-- Ejecuto los métodos

loNameSpace.Logon(lcPerfil , lcContrasenia) loMailItem = loOutlook.CreateItem(0) loMailItem.Recipients.ADD(lcDestinatario) loMailItem.Subject = lcTema loMailItem.Body = lcCuerpo loMailItem.SEND loNameSpace.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.

(21)

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).

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

(22)

*-- Propiedades oOutlook = .NULL. oNameSpace = .NULL. ...

...

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!' ) ENDIF

ENDIF

También en el método Init() llamamos a un formulario para el inicio de sesión: DO FORM Inicio WITH THISFORM.oCorreo TO llAceptar

IF 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.

(23)

Enviar un correo

Antes 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') .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') ENDIF

ENDWITH RETURN

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

(24)

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 llRet = loMensaje.SEND ELSE llRet = .F. ENDIF ENDWITH RETURN llRet ENDPROC

Leer los correos

Para 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 curMsg

IF THISFORM.oCorreo.LeerMensajes(THIS.PARENT.opgTipo.VALUE = 1, 'curMsg') GO TOP IN curMsg

THIS.PARENT.grdMensajes.SETFOCUS ELSE

MESSAGEBOX('No existen mensajes para traer', 64, 'Aviso') ENDIF

THIS.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

(25)

llRet = .T. ELSE llRet = .F. ENDIF ENDWITH ELSE llRet = .F. ENDIF RETURN llRet ENDPROC

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...

Figure

Actualización...

Related subjects :