Documentos de Académico
Documentos de Profesional
Documentos de Cultura
************************************
* DESCOMPRIMIR
************************************
cMiZip = "C:MiArchivo.Zip"
cDirDesti = "c:Directorio"
oShell = Createobject("Shell.Application")
For Each oArchi In oShell.NameSpace(cMiZip).Items
oShell.NameSpace(cDirDesti).CopyHere(oArchi)
EndFor
Grid y DynamicBackColor
Un excelente ejemplo de como podemos utilizar DynamicBackColor para establecer un color a la
celda dependiendo de una condición.
Localo
o = Createobject("myform")
o.Show(1)
Procedure Load
Select * From (_samples+"datacustomer") ;
Order By MaxOrdAmt Desc;
Into Cursor TmpCust
Use In "customer"
EndProc
Procedure Init
This.grid1.SetAll("dynamicbackcolor",;
"Thisform.GetBackColor(allt(country))")
EndProc
Procedure Resize
This.grid1.Width= This.Width
This.grid1.Height= This.Height
EndProc
Function GetBackColor(cCountry)
Local nColor
Do Case
Case cCountry == "Germany"
nColor = Rgb(255,255,255)
Case cCountry == "UK"
nColor = Rgb(224,224,0)
Case cCountry == "Sweden"
nColor = Rgb(224,224,160)
Case cCountry == "France"
nColor = Rgb(100,224,160)
Case cCountry == "Spain"
nColor = Rgb(100,224,160)
Otherwise
nColor = Rgb(224,100,224)
EndCase
Return nColor
EndFunc
EndDefine
=StrToFile(cComando, cArchivo)
=ShellExecute(0,"Open",cArchivo,"","",0)
With Thisform.Edit1
Set Memowidth To Floor(.Width/(Fontmetric(6,.FontName,.FontSize)+1))
nLines = Memlines(.Value)
EndWith
If Empty(cRet)
If tDateTime2 > tDateTime1
nS = tDateTime2 - tDateTime1
Else
nS = tDateTime1 - tDateTime2
Endif
nS = Int(nS)
cTime = Transform(Int(nS/3600),"9999")+":"+ ;
Transform(Mod(Int(nS/60),60),"99")+":"+ ;
Transform(Mod(nS,60)," 99")
Return cTime
Else
=Messagebox("Los valores deben ser DateTime")
Return ""
Endif
Endfunc
Haz clic con el ratón derecho en una zona libre del Escritorio y luego selecciona Nuevo y Acceso
directo.
En la ventana del acceso directo debes escribir shutdown -s -t 00 y pulsa el botón Siguiente,
después le pones el nombre que quieras al acceso directo y pinchas el botón Finalizar.
Si quieres dejar algo de tiempo para cerrar las aplicaciones debes poner esto shutdown.exe -s -
t 12 de esta forma dejarás un margen prudencial de 12 segundos para apagar el PC.
? SECONDS() - lnInicio
Texto parpadeante
Para realizar el efecto debemos crear un formulario, una etiqueta y un control timer; la
propiedad Interval de este se la establecemos a 700 (milisegundos).
Cambiamos la propiedad caption de la etiqueta a al texto que deseamos, luego ejecutamos.
PerVMFiles = 80
hWin = GetDeskTopWindow()
P./ ¿Hay alguna forma de imprimir las páginas 10 y 11 de un Report especificando sólo 10-11?
R./ Usa la clausula PROMPT de la orden REPORT FORM para mostrar el diálogo de impresión.
Aquí se puede señalar el rango de páginas para ser impresos
P./ De qué forma puedo usar la tecla ESC para salir de un Formulario?
R./ La mejor forma es poner la propieda Cancel del botón de comandos a .T.. Esto sería la
forma más correcta de trabajar en perfecta consonancia con el diseño de Interfaces de
windows. En el evento Click() de el botón de comandos escribe:
Thisform.Release()
También se podría usar el evento KeyPress del formulario con:
PROCEDURE KeyPress
LPARAMETERS nKeyCode, nShiftAltCtrl
IF nKeyCode = 27
RELEASE THISFORM
ENDIF
ENDPROC
Grid multiselección
Una de las preguntas habilítales es como podemos hacer para poder seleccionar múltiples
registros en un grid.
En este caso utilizamos un checkbox para hacer la selección de los registros....
Public oForm
oForm = Createobject("FrmGrid")
oForm.AddObject("Grid1","Grid")
With oForm.Grid1
.ColumnCount = 3
.Height = 288
.Width = 320
.Left = 24
.Top = 24
.Column1.Width= 17
.Column1.Header1.Caption= ""
.Column1.AddObject("Check1","CheckBox")
.Column1.Check1.Visible = .T.
.Column1.CurrentControl= "Check1"
.Column1.Sparse= .F.
.Column2.Width= 50
.Column2.Header1.Caption= "Código"
.Column3.Width= 194
.Column3.Header1.Caption= "Nombre"
.SetAll("DynamicBackColor","IIF(Ok, RGB(0,220,210), RGB(255,255,255))", "COLUMN")
.Visible = .T.
Endwith
oForm.Visible = .T.
Define Class FrmGrid As Form
Top = 0
Left = 0
Height = 334
Width = 376
DoCreate = .T.
Caption = "Grid multiselección"
WindowType = 1
WindowState = 0
Name = "FrmGrid"
Procedure Load
Use Home() + "SamplesDataemployee.dbf"
Select .F. As OK, Emp_ID, Trim(Last_Name)+" " + Trim(First_Name) ;
From Employee Into Cursor Empleados Readwrite
EndProc
Procedure Unload
Use In Employee
EndProc
EndDefine
Si bien con la tecla [Esc] sales del browse, debes de tener cuidado, debido a que dicha tecla
cancela la opcion que puedas estar ejecutando. te recomiendo por cuestion didactica que
utilices mejor los siguiente ritina:
Para poder salir del browse usando la tecla ENTER, puedes utilizar el siguiente código:
*************************
* ---- Primero desconectamos la tecla escape.
SET ESCAPE OFF
USE
* ---- Ahora cada vez que presiones ENTER, será como presionar escape.
ON KEY LABEL ENTER KEYBOARD "{ESC}"
BROWSE
Es muy importante que "desconectes", el ENTER, pues de lo contrario cada vez que presiones
enter, FOX "entiende" que has presionado ESCAPE.
oExcel.Range("A1:M1;A2:M2;A3:M3").Select
oGraph = oExcel.Charts.Add()
With oGraph
.Type = -4101
.Legend.Clear
.Perspective=100
.Elevation=0
EndWith
oExcel.Visible = .T.
ANetResources(APcs, "NOMBRE-RED",1)
For i = 1 to Alen(APcs)
? APcs(i)
Next
_rtfFile = "c:"+Sys(3)+".rtf"
Scan
Set Textmerge To &_rtfFile Noshow
Set Textmerge On
\{ tf1ansiqj <>}
Set Textmerge To
Append General gText From &_rtfFile Class "RICHTEXT.RICHTEXTCTRL.1"
EndScan
With Thisform.Edit1
Set Memowidth To Floor(.Width/(Fontmetric(6,.FontName,.FontSize)+1))
nLines = Memlines(.Value)
EndWith
nHanConn = Sqlstringconnect(m.cCadConnDSN)
If nHanConn > 0
=MessageBox("Conectado a la base de datos de SQl Server")
Else
=MessageBox("Error al conectarse a la base de datos de SQl Server")
EndIf
Endscan
?PADL(lnVar,5,_0_)
Resultado
00350
Tengo una aplicación compilada con Vfp8 hice un instalador y anda muy bien en todas las redes
en donde la he instalado
salvo en una condición
R:\AdmCons\AdmCons.exe
No se pudo iniciar la aplicación porque su configuración es incorrecta. Reinstalar la aplicación
puede solucionar el problema.
Me pasa con cualquier servidor Win Me Win XP etc pero siempre cuando el cliente es un XP con
SP2
En la misma situación he hecho para probar EXE compilado con VFP6 y anda sin problema
gdiplus.dll
msvcr70.dll
vfp8r.dll
vfp8resn.dll
vfp8t.dll
Re: RESALTAR UNA CELDA DE UN GRID ....
Por sotero (#866) a las 20:40 del 14-02-2006
Thisform.HitRow = RECNO(tcTabla)
ThisForm.LockScreen = .T.
cDynamic = 'IIF(RECNO() = Thisform.HitRow , RGB(255,255,0), RGB(255,255,255))'
&tcGrid..SetAll('DynamicBackColor', cDynamic, 'Column')
&tcGrid..Refresh
ThisForm.LockScreen = .F.
***********************************************
Si estás en VFP 9 esto lo puedes suprimir y poner en el GRID la propiedad
HighlightStyle a 2 (Enable highlighting for current row and persist when grid is
not the current active control.)
El problema es que son códigos de página diferentes entre los archivos dos y Windows, prueba
esto
Cuando diseñamos nuestra aplicacion tenemos como base una resolucion para nuestra
aplicacion, normalmente yo la diseño en 800 x 600, para garantizar el buen funcionamiento yo
le indico a los usuarios si estan usando la configuración básica aceptable.
Espero les pueda ayudar:
IF dimensiones()=.T.
=cambiar_resolucion(800,600)
ENDIF
FUNCTION dimensiones
#DEFINE h_screenwidth 0 && Screen width
#DEFINE h_screenheight 1 && Screen HEIGHT
LOCAL lnalto_screen, lnancho_screen
DECLARE INTEGER GetSystemMetrics IN Win32API;
INTEGER nIndex
lnalto_screen = getsystemmetrics(h_screenheight)
lnancho_screen = getsystemmetrics(h_screenwidth)
IF ( lnalto_screen < 600 OR lnancho_screen < 800 )
IF MESSAGEBOX("Es recomendable que la resolución "+CHR(13)+;
"de su monitor sea de 800 x 600"+CHR(13)+"Para que el sistema se ejecute
adecuadamente"+CHR(13)+CHR(13)+;
"¿ Desea cambiarla ?",4+32+0,"Verifique pregunta" ) = 6
RETURN .T.
ELSE
RETURN .F.
ENDIF
ELSE
RETURN .F.
ENDIF
#undefine h_screenwidth
#undefine h_screenheight
ENDFUNC
FUNCTION cambiar_resolucion
LPARAMETERS tnwidth, tnheight
LOCAL lnwidth, lnheight, lnmodenum, lcdevmode, lnresp
lnmodenum = 0
lcdevmode = REPLICATE(CHR(0), 156)
lnwidth = IIF(EMPTY(tnwidth), 800, tnwidth)
lnheight = IIF(EMPTY(tnheight), 600, tnheight)
DECLARE INTEGER EnumDisplaySettings IN Win32API STRING lpszDeviceName, INTEGER
iModeNum, STRING @lpDevMode
DECLARE INTEGER ChangeDisplaySettings IN Win32API STRING @lpDevMode , INTEGER
dwFlags
*!* Se usa obtener todos los modos disponibles
DO WHILE enumdisplaysettings(NULL, lnmodenum, @lcdevmode) <> 0
lnmodenum = lnmodenum + 1
ENDDO
lcdevmode = STUFF(lcdevmode, 41, 4, long2str(1572864))
lcdevmode = STUFF(lcdevmode, 109, 4, long2str(tnwidth)) && Ancho
lcdevmode = STUFF(lcdevmode, 113, 4, long2str(tnheight)) && Alto
lnresp = changedisplaysettings(@lcdevmode, 1)
IF lnresp = 0
MESSAGEBOX("La resolución de su monitor ha sido cambiada"+CHR(13)+;
"Ahora podra trabajar adecuamente",0+48+0,"atención" )
ELSE
MESSAGEBOX("No se pudo cambiar la resolución de su monitor",0+48+0,"atención" )
ENDIF
ENDFUNC
FUNCTION long2str
LPARAMETERS lnlongval
*!* Convierte un long integer a un 4-byte character string
*!* Sintaxis: LongToStr(lnLongVal)
*!* Valor devuelto: lcRetStr
*!* Argumentos: lnLongVal
*!* lnLongVal especifica el long integer a convertir
LOCAL lncnt, lcretstr
lcretstr = ''
FOR lncnt = 24 TO 0 STEP -8
lcretstr = CHR(INT(lnlongval/(2^lncnt))) + lcretstr
lnlongval = MOD(lnlongval, (2^lncnt))
NEXT
RETURN lcretstr
ENDFUNC
*****
Les explico, si ud hace diseñado una aplicación en una resolución de 800x600 (en este caso);
podra advertir a los usuarios que su monitor no esta configurado adecuademente y este por si
solo podra sin salir del sistema cambiar la resolución.
INFORMACION DE DRIVES
¿Quieres conocer toda la información de todas las unidades o drives instalados en tu sistema?
Bueno pues esta función relizada con WSH lo hace para ti.
Function WSHListDrives
LOCAL loFSO, loDrivesCol, loDrive, ;
llIsReadyDrive, lnDriveType, lcDriveType, lnWait
loFSO = CREATEOBJECT('Scripting.FileSystemObject')
loDrivesCol = loFSO.Drives
llIsReadyDrive = loDrive.IsReady
? IIF( llIsReadyDrive=.T., [El Drive esta listo.],[El drive no esta listo.])
IF llIsReadyDrive
? "Espacio disponible: " + ALLTRIM(STR(loDrive.AvailableSpace)) + [ bytes.]
? "Espacio Libre: " + ALLTRIM(STR(loDrive.FreeSpace)) + [ bytes.]
? "Tamaño total: " + ALLTRIM(STR(loDrive.TotalSize)) + [ bytes.]
? "Ruta: " + loDrive.Path
? "Nombre del volumen: " + loDrive.VolumeName
ENDIF
IF lnDriveType=3
? "Nombre compartido: " + loDrive.ShareName
ELSE
IF lnDriveType=2
? "Nombre compartido: " + RTRIM(LEFT(SYS(0),15))
ENDIF
ENDIF
WAIT
CLEAR
NEXT
Endfunc
fso = CreateObject("Scripting.FileSystemObject")
fldr = fso.CreateTextFile("C:prueba.txt")
Endfunc
Procedure Num2dWord
Lparameter tnNum
Local c0,c1,c2,c3
lcresult = Chr(0)+Chr(0)+Chr(0)+Chr(0)
If tnNum < (2^31 - 1) then
c3 = Chr(Int(tnNum/(256^3)))
tnNum = Mod(tnNum,256^3)
c2 = Chr(Int(tnNum/(256^2)))
tnNum = Mod(tnNum,256^2)
c1 = Chr(Int(tnNum/256))
c0 = Chr(Mod(tnNum,256))
lcresult = c0+c1+c2+c3
Endif
Return lcresult
Endproc
Function GetInfoF(cFile)
Declare Long _lopen In "kernel32" As lOpen String lpPathName, Long iReadWrite
Declare Long _lclose In "kernel32" As lclose Long hFile
Declare Long GetFileSize In "kernel32" Long hFile, Long @lpFileSizeHigh
Local nArchivo As Long, nLongitud As Long
Local lpMax As Long
lpMax =1
nArchivo= lOpen(cFile, OF_READ)
nLongitud = GetFileSize(nArchivo, @lpMax )
lclose(nArchivo)
Return nLongitud
Endfunc
ODBC_ADD_SYS_DSN = 1
"BackgroundFetch=Yes" + CHR(0) + ;
"SourceDb=
uta.dbc" + CHR(0) +;
"Sourcetype=DBC"
ENDIF
Este efecto es muy comun en las aplicaciones hoy en dia tal, como MSN. que cuando nos
escriben un mensaje, parpadea el titulo de una ventana en la barra de tareas.
Desde VFP podemos hacerlo. La función que nos permite hacerlo la declaramos asi:
Declare FlashWindow IN user32.dll ;
LONG hwnd,;
Long bInvert
Cuando querramos que nuestra aplicacion parpadee ejecutamos lo siguiente: Si queremos que
el _screen Parpadee
FlashWindow(application.hWnd,.t.)
Si queremos que el form lo haga
FlashWindow(thisform.HWnd,.t.)
NOTA: FlashWindow solo hará que la ventana parpadee una sola vez, si queremos que el
parpadeo persista, deberemos colocar el código en un Timer, y en el evento Activate del Form,
desactivar el Timer, o que el Timer lo ejecute X veces. El segundo parámetro nos permite
decirle si queremos que parpadee si esta activada o no. Al pasarle .T. la ventana parpadeara
una sola vez, este o no activada, si le pasamos .F. parpadeara solo si la ventana esta inactiva y
quedara de otro color el boton en la barra de tareas. Este código esta en VFP 7. Si quieren
ejecutarla desde VFP 6 deberan obtener el handle de la ventana a la que le quieran aplicar el
efecto. En PortalFox esta la función para hacerlo. Creo que son estas APIs:
GetWindow
FindWindow
OBTENER DIRECCION IP CON WINSOCK
Una manera de conseguir la dirección IP local con Winsock.
************************************************************
*
* Clase: GET_IPADDRESS
*
* Devuelve la dirección IP local
*
* Parametros:
*
* ninguno
*
* Ejemplos:
*
* ipdir=get_ipaddress()
*
* Retorno
*
* dirección IP o cadena vacia si no está instalado el WinSock
*
* Nota
*
* Adaptado de John Harvey
*
* Fecha de Creación : 29/01/2001 Pablo Roca
* Ultima Modificacion: 29/01/2001 Pablo Roca
*
************************************************************
LOCAL lcret
IPSocket = CreateObject("MSWinsock.Winsock")
IF TYPE('IPSocket')='O'
lcret = IPSocket.LocalIP
ELSE
MESSAGEBOX("Winsock no está instalado!")
lcret = ""
ENDIF
RETURN lcret
FUNCTION ChangeRes
LPARAMETERS tnWidth, tnHeight
LOCAL lnWidth, lnHeight, lnModeNum, lcDevMode
*!* Valores
lnModeNum = 0
lcDevMode = REPLICATE(CHR(0), 156)
lnWidth = IIF(EMPTY(tnWidth), 800, tnWidth)
lnHeight = IIF(EMPTY(tnHeight), 600, tnHeight)
*!* Instrucciones DECLARE DLL para cambiar resolución
DECLARE INTEGER EnumDisplaySettings IN Win32API STRING lpszDeviceName,;
INTEGER iModeNum, STRING @lpDevMode
DECLARE INTEGER ChangeDisplaySettings IN Win32API STRING @lpDevMode ,;
INTEGER dwFlags
*!* Bucle para obtener todos los modos disponibles
DO WHILE EnumDisplaySettings(NULL, lnModeNum, @lcDevMode) <> 0
lnModeNum = lnModeNum +1
ENDDO
*!* Configurar la structura DevMode
lcDevMode = STUFF(lcDevMode, 41, 4, LongToStr(1572864))
lcDevMode = STUFF(lcDevMode, 109, 4, LongToStr(tnWidth)) && Ancho
lcDevMode = STUFF(lcDevMode, 113, 4, LongToStr(tnHeight)) && Alto
*!* Cambiar resolucion
ChangeDisplaySettings(@lcDevMode, 1)
ENDFUNC
RUN /N CALC.EXE
* LA PONEMOS ENCIMA
SetForegroundWindow(nHWD)
* LA ACTIVAMOS
SetActiveWindow(nHWD)
RETURN .T.
ELSE
* VENTANA NO ACTIVA
RETURN .F.
ENDIF
Cuando generamos el menú con esta opción activada, se deberá llamar al menú desde el
método Init del formulario de nivel superior con una sentencia como la siguiente
DO MiMenu.mpr WITH THISFORM,.T.
Editando el archivo MiMenu.mpr podemos ver en las primeras líneas comentadas, una ayuda
con las distintas formas de llamar al menú generado.
Código de ejemplo
El siguiente código es un ejemplo para mostrar un formulario de nivel superior con un menú
incorporado. Para ejecutarlo con un menú personalizado, se debe habilitar la línea DO
MiMenu.mpr WITH Thisform, .T. en el método Init del formulario, y quitar la llamada al
procedimiento MiMenuEjemplo.
PUBLIC goMiForm
goMiForm=CREATEOBJECT("MiForm")
goMiForm.SHOW(1)
RETURN
*---
*--- Definición de MiForm
*---
DEFINE CLASS MiForm AS FORM
SHOWWINDOW = 2
DOCREATE = .T.
AUTOCENTER = .T.
CAPTION = "Ejemplo de menú en un formulario SDI"
NAME = "MiForm"
PROCEDURE INIT
*DO MiMenu.mpr WITH Thisform, .T.
DO MiMenuEjemplo WITH THISFORM, .T.
ENDPROC
PROCEDURE DESTROY
RELEASE MENU (THIS.NAME) EXTENDED
ENDPROC
ENDDEFINE
*---
*--- MiMenuEjemplo.spr
*---
PROCEDURE MiMenuEjemplo
LPARAMETERS oFormRef, getMenuName, lUniquePopups
LOCAL cMenuName, nTotPops, a_menupops, cTypeParm2, cSaveFormName
IF TYPE("m.oFormRef") # "O" OR ;
LOWER(m.oFormRef.BASECLASS) # 'form' OR ;
m.oFormRef.SHOWWINDOW # 2
MESSAGEBOX([Este menú solo puede ser llamado en un formulario de nivel superior])
RETURN
ENDIF
m.cTypeParm2 = TYPE("m.getMenuName")
m.cMenuName = SYS(2015)
m.cSaveFormName = m.oFormRef.NAME
IF m.cTypeParm2 = "C" OR (m.cTypeParm2 = "L" AND m.getMenuName)
m.oFormRef.NAME = m.cMenuName
ENDIF
IF m.cTypeParm2 = "C" AND !EMPTY(m.getMenuName)
m.cMenuName = m.getMenuName
ENDIF
DIMENSION a_menupops[3]
IF TYPE("m.lUniquePopups")="L" AND m.lUniquePopups
FOR nTotPops = 1 TO ALEN(a_menupops)
a_menupops[m.nTotPops]= SYS(2015)
ENDFOR
ELSE
a_menupops[1]="archivo"
a_menupops[2]="edición"
a_menupops[3]="ayuda"
ENDIF
*---
*--- Definición del menú
*---
DEFINE MENU (m.cMenuName) IN (m.oFormRef.NAME) BAR
DEFINE PAD _1mv0kg6re OF (m.cMenuName) PROMPT "\
DEFINE PAD _1mv0kg6rf OF (m.cMenuName) PROMPT "\
DEFINE PAD _1mv0kg6rg OF (m.cMenuName) PROMPT "A\
ON PAD _1mv0kg6re OF (m.cMenuName) ACTIVATE POPUP (a_menupops[1])
ON PAD _1mv0kg6rf OF (m.cMenuName) ACTIVATE POPUP (a_menupops[2])
ON PAD _1mv0kg6rg OF (m.cMenuName) ACTIVATE POPUP (a_menupops[3])
IF m.cTypeParm2 = "C"
m.getMenuName = m.cMenuName
m.oFormRef.NAME = m.cSaveFormName
ENDIF
ENDPROC
PROCEDURE _Salir
_SCREEN.ACTIVEFORM.RELEASE
ENDPROC
Al ejecutar el código anterior, veremos el formulario SDI con el menú como lo muestra la
siguiente figura:
Comentarios
Recordar que Visual FoxPro trae en la aplicación "Solution" un ejemplo similar donde se
muestra un formulario SDI, en el cual se agrega un menú, una barra de herramientas, y se
crean ventanas secundarias. Para ver la aplicación "Solution" ejecute:
DO (HOME(2) + "Solution\Solution")
O directamente corra el ejemplo del formulario SDI ejecutando:
DO FORM (HOME(2) + "\Solution\Forms\SDIForm.scx")
Buscar texto en un Cuadro de Edicion
Para ver el ejemplo ejecute el siguiente código:
*-- Creo un archivo de texto
SET SAFETY OFF
LOCAL lc
TEXT TO lc NOSHOW
Este es un texto tomado de un archivo de
texto, para buscar una palabra y remarcarla
si la encuentra.
IF lnRecno # RECNO()
THISFORM.Grid1.SETFOCUS && el nombre del TextBox
THISFORM.Text1.SETFOCUS && el nombre del Grid
ENDIF
RETURN
*--- END InteractiveChange ---
NOTA: SE DEBE TENER LAS SIGUIENTES CONSIDERACIONES:
La tabla debe estar ordenada ASCENDENTEMENTE o existir un indice por Nombre y estar
seleccionado (el método busca si hay un SET ORDER establecido)
Fijarse bien el nombre del TextBox y del Grid para invocar el método SetFocus (en el ejemplo
Grid1 y Text1)
Fijarse el alias de la tabla del grid (en el ejemplo MiTabla)
Conviene que los nombres estén en mayúsculas y añadir en la propiedad Format del TextBox =
! para que sean ingresados en mayúsculas
Conviene que la propiedad del Grid RecordMark = .T. para que se vea el registro marcado, o
manejar el color del registro seleccionado
El modelo de objetos de Outlook es muy rico y poderoso. Esta interfaz está disponible como un
servidor de automatización, o sea, que todo lo podemos automatizar mediante programación
desde Visual FoxPro.
Enviar y leer correo con Outlook desde Visual FoxPro
El modelo de objetos de Outlook es muy rico y poderoso. Esta interfaz está disponible como un
servidor de automatización, o sea, que todo lo podemos automatizar mediante programación
desde Visual FoxPro.
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.
Lo primero que debemos hacer para automatizar Outlook, es crear un objeto Outlook. 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.
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)
loNameSpace = .NULL.
loOutlook = .NULL.
Problemas de seguridad
Outlook XP y Outlook 2000 SP2, incluyen los parches de seguridad de Microsoft. Estos parches
restringen, entre otras cosas, el acceso a la libreta de direcciones y el envío de correo mediante
automatización, con el fin de evitar códigos maliciosos que toman los datos de nuestra libreta
de direcciones 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".
Cuando intentamos acceder a la libreta de direcciones aparece el cuadro de dialogo el cual nos
permitirá un acceso inmediato, o de 1, 2, 5, ó 10 minutos que debemos seleccionar.
Esta otra figura nos muestra la página para la lectura de la bandeja de entrada.
ENDIF
ENDFOR
llRet = loMensaje.SEND
ELSE
llRet = .F.
ENDIF
ENDWITH
RETURN llRet
ENDPROC
*--------------------------------------------------
* Lee los mensajes según parámetro
*--------------------------------------------------
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
RETURN llRet
ENDPROC
*--------------------------------------------------
* Destroy
*--------------------------------------------------
PROCEDURE DESTROY()
WITH THIS
.oNameSpace = .NULL.
.oOutlook = .NULL.
ENDWITH
ENDPROC
*--------------------------------------------------
* Cargo una Matriz con los destinatarios
*--------------------------------------------------
PROCEDURE CargarVector(tcTexto, tcVector)
LOCAL ln AS INTEGER, lnI AS INTEGER, la(1) AS CHARACTER
IF EMPTY(tcTexto)
RETURN 0
ENDIF
tcTexto = CHRTRAN(tcTexto,',',';')
ln = ALINES(la, tcTexto, .T., ';')
lcV = "This." + tcVector
DIMENSION &lcV.(ln)
FOR lnI = 1 TO ln
&lcV.(lnI) = la(lnI)
ENDFOR
RETURN ln
ENDPROC
ENDDEFINE && Clase cOutlook
A la definición de la clase, como así también los formularios, y el ejemplo los podemos
descargar del siguiente vínculo: outlook.zip para su evaluación. Los ejemplos están realizados
con Visual FoxPro 8 y Outlook 2002 (Outlook XP).
Una forma fácil de crear un formulario por código como un explorador de internet.
PUBLIC oForm
oForm = NEWOBJECT("MiExplorador")
oForm.SHOW
DEFINE CLASS MiExplorador AS FORM
TOP = 0
LEFT = 0
DOCREATE = .T.
CAPTION = "PortalFox :: Nada corre como un zorro"
WINDOWSTATE = 0
NAME = "Form1"
WIDTH = 800
HEIGHT = 600
ADD OBJECT olecontrol1 AS OLECONTROL WITH ;
TOP = 10, ;
LEFT = 10, ;
WIDTH = 780, ;
HEIGHT = 580, ;
NAME = "Olecontrol1", ;
OLECLASS = "Shell.Explorer.2"
PROCEDURE RESIZE
THIS.olecontrol1.HEIGHT = THIS.HEIGHT - 20
THIS.olecontrol1.WIDTH = THIS.WIDTH - 20
ENDPROC
PROCEDURE olecontrol1.REFRESH
NODEFAULT
ENDPROC
PROCEDURE olecontrol1.INIT
THIS.NAVIGATE("http://www.portalfox.com")
ENDPROC
ENDDEFINE
Como idea adicional, podrías utilizar tu propio método para generar un reporte o mandar tu
reporte a ASCII (REPORT FORM < TuReporte > TO FILE ASCII ) , pasarlo a archivo y
mostrarlo....
lcFile = SYS(2015)+".txt"
OPEN DATABASE HOME(2)+"data\testdata.dbc"
SET TEXTMERGE ON
SET TEXTMERGE TO (lcFile) NOSHOW
lcTitle="Reporte de Clientes"
IF !USED("Customer")
USE Customer IN 0
ENDIF
SELECT Customer
\< < PADC(lcTitle,80," ") > >
NOTA: gracias a Jose Luis Santana Blasco y a Rafael Angel Ponce Yllanes
por la aclaración del NOCONSOLE, con esto se mejora mucho la velocidad.
Nota: Se ha reportado que con Campos MEMO, si se utiliza la propiedad STRETCH WITH
OVERFLOW suelen ocurrir resultados inesperados