Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Automatizacion
Automatizacion
Automatizando Office
con Visual FoxPro
Luis Mara Guayn es programador en lenguajes xBase desde el ao 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 versin de Visual FoxPro.
Es el Responsable del rea de Desarrollo Informtico de Vicente Trapani S.A., un estableci miento citrcola industrial en la provincia de Tucumn, en la Repblica Argentina.
Es cofundador en el ao 2000, y SysOp de PortalFox, el mayor portal gratuito para todos los
desarrolladores en Visual FoxPro de habla hispana.
En los aos 2002 y 2003 fue nombrado por Microsoft como MVP (Most Valuable Professional)
en Visual FoxPro, por su colaboracin en las distintas comunidades en lnea.
Pgina 2
Introduccin
Visual FoxPro es una poderosa herramienta, pero hay tareas que no las puede realizar l solo. Por
ejemplo si tenemos una aplicacin en la cual queremos enviar un correo, realizar un grfico, 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 cdigo.
Todos los ejemplos descriptos fueron realizados con Microsoft Visual FoxPro 8 y Microsoft Office XP.
Cmo comenzar?
Lo primero que debemos preguntarnos es cmo comenzar la tarea de automatizacin? Esta pre gunta pareciera que tiene una respuesta difcil, pero veremos...
La mayora de la informacin disponible en: la Ayuda de Office, los artculos de la Base de Conocimientos 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 ms para los desarrolladores en
Visual FoxPro de habla hispana, la informacin disponible en Internet esta generalmente en ingls.
En espaol, existen varios artculos, ideas y trucos disponibles en los siguientes sitios de la Web:
En ingls 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 electrnico en el sitio de
Hentzenwerke: http://www.hentzenwerke.com
Pgina 3
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 metro 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 ms lento el proceso de automatizacin.
Pgina 4
Pgina 5
Una vez instanciado el objeto desde la "Ventana de Comandos" o declarado desde el "Editor", con
solo escribir el nombre del objeto nos aparecern sus propiedades y mtodos (Figura 4) y la sintaxis
correspondiente (Figura 5).
Pgina 6
1
2
3
Pgina 7
#INCLUDE 'Archivo.h'
Para la creacin de estos archivos disponemos al menos de dos herramientas:
Cdigo en VFP de Trevor Hancock (Base de Conocimientos de Microsoft, Artculo 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
Pgina 8
Cuando esto ocurre liberamos el objeto de Visual FoxPro con la siguiente sentencia en el mtodo
ApplicationEvents2_Quit:
THIS.oWord = .NULL.
El programa MailMerge.prg
Este es el cdigo de nuestro programa "MailMerge.prg", en donde creamos una instancia de la
clase cWord y comenzamos a ejecutar sus mtodos.
LOCAL lo AS OBJECT, loDoc AS OBJECT
lo = NEWOBJECT('cWord','cWord.prg')
IF lo.CrearServidor()
*-- Vinculo los eventos de Word a mtodos 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
Pgina 9
CATCH
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!!!')
FINALLY
ENDTRY
FINALLY
ENDTRY
RETURN VARTYPE(THIS.oWord) = 'O'
Para que este ejemplo sea ms descriptivo, vamos a hacer visible a la aplicacin Word para ver paso
a paso como se ejecutan los distintos mtodos. Esto lo hacemos en la sentencia:
lo.oword.VISIBLE = .T.
Pgina 10
Combinar la carta
En el mtodo CombinarCarta() ejecutamos las siguientes sentencias para:
Hacer la carta del tipo Documento Principal.
Abrir el archivo con la fuente de datos.
Ejecutar la combinacin
WITH toDoc.MailMerge
.MainDocumentType = 0 && wdFormLetters
.OpenDataSource(THIS.cDataSource)
.Execute()
ENDWITH
Guardar la carta
Para finalizar tenemos el mtodo GuardarCarta() que guarda el documento principal, con la posibilidad mediante un parmetro 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 aplicacin visible para que el usuario lo guardar directamente de la ventana de Word. Tambin estable cemos la carpeta donde estn los documentos de este ejemplo, para que Word por defecto la seleccione en la ventana de "Guardar...".
WITH THIS.oWord
.ChangeFileOpenDirectory(THIS.cDirDoc)
.VISIBLE = .T.
ENDWITH
Pgina 11
Antes de comenzar a analizar el ejemplo en Visual FoxPro vamos a comprender algunos conceptos
para poder hacer ms fcil la automatizacin de Excel desde Visual FoxPro, esto siempre lo logramos
con los archivos de ayuda. Tambin vamos a conocer distintas formas que disponemos para pasar
los datos de Visual FoxPro a Excel.
Pgina 12
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 mtodo 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
opcin es mucho ms lenta que las anteriores, pero es til cuando la hoja de Excel ya tiene un for mato establecido o cuando queremos escribir frmulas 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
Pgina 13
Generar un grfico
A partir de los resultados de una consulta a las tablas de la base de datos "Northwind", generaremos
un grfico del tipo de columnas en 3 dimensiones (xl3DColumnStacked) como lo vemos en la Figura
9, y daremos formato a algunos objetos del grfico, como los ttulos, los ejes y las barras.
Pgina 14
El programa Grafico.prg
En este programa vamos a crear un objeto cExcel y ejecutaremos los mtodos vistos anteriormente
para la creacin del grfico.
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 grfico
lo.GenerarGrafico(loLibro, 'Ventas Anuales por Empleado (AO 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 mtodo 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
Pgina 15
Pgina 16
Con anterioridad a la programacin de los mtodos debemos disear la forma de la tabla dinmica
que deseamos generar. El diseo elegido lo observamos en la Figura 10.
Programa TablaDinamica.prg
Este es el cdigo 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 dinmica
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 dinmica ejecutamos el mtodo GenerarTablaDinamica() de la clase cExcel.
Este mtodo nos genera la tabla dinmica (Figura 11) ordenada descendentemente por las Ventas
por Pas.
PROCEDURE GenerarTablaDinamica(toLibro)
LOCAL laPagina(1), laFilas(2), laColumnas(1), lcRango
*--- Arrays con los datos de la tabla dinmica
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 Dinmicas
.ActiveSheet.PivotTableWizard(1, lcRango, '', 'TablaDinamica')
*--- Armo la Tabla dinmica
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
Pgina 17
ENDWITH
*--- Formato de los datos hoja tabla dinmica
.Cells.SELECT
.SELECTION.COLUMNS.AUTOFIT
.ActiveSheet.NAME = 'Ventas Anuales'
.RANGE('A2').SELECT
ENDWITH
RETURN
ENDPROC
Pgina 18
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 mtodos Logon y Logoff.
Versiones de Outlook
Seguramente conocemos varias versiones de Outlook, solo daremos las principales caractersticas de
cada uno.
Outlook Express: No es Outlook, es un programa completamente diferente y no es servidor
de automatizacin.
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 interfaz MAPI.
Outlook XP: Combina las versiones Internet Mail y Corporate Workgroup.
Un breve ejemplo
Una de las tareas ms fcil de automatizar en Outlook es el envo de un correo. Veremos un ejemplo
de solamente unas pocas lneas.
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 Espaa 2003." + LF_CR
lcCuerpo = lcCuerpo + "Saludos desde A Corua." + LF_CR
*-- Creo objetoa Outlook y NameSpace
loOutlook = CREATEOBJECT("Outlook.Application")
loNameSpace = loOutlook.GetNameSpace("MAPI")
*-- Ejecuto los mtodos
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 envo de correo mediante auto matizacin, con el fin de evitar cdigos maliciosos que toman los datos de nuestra libreta de direcciones y envan correo sin nuestro consentimiento.
Pgina 19
Cuando intentamos enviar un correo desde Visual FoxPro, se nos presenta el siguiente cuadro de
dialogo, que luego de 5 segundos habilita el botn "Si" (Figura 13).
Y ahora que?
Estas son algunas de las opciones disponemos nosotros para trabajar con estos parches de seguridad:
Mantener la versin 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 fcilmente a la automatizacin 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 versin libre con fines de desarrollo.
Express ClickYes: Es un pequeo programa residente que se maneja mediante la API de Windows. Este "presionar" el botn "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.
Pgina 20
*-- Propiedades
oOutlook = .NULL.
oNameSpace = .NULL.
...
...
Como vimos anteriormente en la definicin de la clase cWord, la sentencia IMPLEMENTS nos permitir interactuar con los eventos de Outlook desde Visual FoxPro.
Solo pondremos cdigo para interactuar
ApplicationEvents_10_Quit():
con el evento
WITH THIS
.oNameSpace = .NULL.
.oOutlook = .NULL.
ENDWITH
El formulario de ejemplo
En este ejemplo utilizaremos un formulario con un objeto PageFrame con dos Pginas, una para en viar correo y la otra para leer los correos desde la Bandeja de Entrada.
En el mtodo 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 mtodos 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
Tambin en el mtodo Init() llamamos a un formulario para el inicio de sesin:
DO FORM Inicio WITH THISFORM.oCorreo TO llAceptar
IF NOT (llAceptar AND THISFORM.oCorreo.IniciarSesion())
MESSAGEBOX('Fall el inicio sesin', 48, 'Inicio de sesin')
RETURN .F.
ENDIF
Pgina 21
Enviar un correo
Antes de invocar el mtodo EnviarCorreo(), configuramos todas las propiedades necesarias para
el envo de correo. Esto lo hacemos en el mtodo Click() del botn "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 mtodo EnviarCorreo() de la clase cOutlook creo un nuevo mensaje y lo armo segn las propiedades 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
Pgina 22
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
Pgina 23
llRet = .T.
ELSE
llRet = .F.
ENDIF
ENDWITH
ELSE
llRet = .F.
ENDIF
RETURN llRet
ENDPROC
Resumen
En este documento vimos solo algunos ejemplos de automatizacin de Office. Las posibilidades son
muchas y cada una depende de la solucin que debemos implementar en nuestras aplicaciones. Recuerden que todo el poder que nos brindan las distintas herramientas de Office, pueden ser maneja das desde Visual FoxPro. Solo es cuestin de adoptar alguna de las tcnicas vistas y ponerse a tra bajar...
Pgina 24