? ValCif("01234567D") *---------------------------------------------------------------------- * Jose Maria Arrabal Alcañiz * Funcion para validar CIF (Código de Identificacion Fiscal) * Entrada: Cadena * Salida : Cadena con CIF calculado *---------------------------------------------------------------------- FUNCTION ValCIF PARAMETER cdni PRIVATE A LOCAL impar,par,simpar,SPAR A = UPPER(ALLTRIM(CHRTRAN(cdni,",.-",""))) IF LEN(A) # 9 WAIT WINDOW "Error en la longitud del CIF" TIME 3 RETURN "" ENDIF cNumero = SUBSTR(A,2,7) IF PADL(VAL(cNumero),7,"0") # cNumero WAIT WINDOW "Error en la contrucción del CIF" TIME 3 RETURN ENDIF cLetra = LEFT(A,1) * * Sumar cifras pares y las impares las multiplicamos por 2 * SPAR = 0 simpar = 0 par = 0 impar = 0 FOR nI = 1 TO 7 STEP 2 impar = VAL(SUBSTR(cNumero,nI,1))*2 impar = INT(impar/10) + impar%10 par = VAL(SUBSTR(cNumero,nI+1,1)) simpar = simpar+impar SPAR = SPAR+par NEXT R = 10-(SPAR+simpar)%10 cCif = cLetra+cNumero IF cLetra $ "ABCDEFGH" cCif = cCif+CHR(ASC('0')+R) ELSE cCif = cCif+SUBSTR("JABCDEFGHI",R+1,1) ENDIF IF A # cCif MESSAGEBOX("El CIF "+A+" introducido es incorrecto "+CHR(13)+; "El correcto sería "+cCif+CHR(13)+; "Compruebe que los datos son correctos",16) ENDIF RETURN cCif ENDFUNC *----------------------------------------------------------------------Jose Maria Arrabal Alcañiz
26 de diciembre de 2002
Validar CIF (Código de Identificacion Fiscal) [Solo España]
22 de diciembre de 2002
Obtener Fecha y Hora del Servidor
Lo único que se necesita es tener derechos de escritura en la carpeta, folder o recurso compartido del servidor.
ltFecha = ServerTime("F:/")
Pasandole de Parametro un recurso compartido:
ltFecha = ServerTime("//SERVIDOR/VALUACION")Si no se pasan parametros, tomara por default la fecha del equipo donde se ejecute el programa:
ltFecha = ServerTime()Espero que les sea de utilidad.
Espartaco Palma Martínez
****************************************************************** * ---- Server DateTime ----- Fecha y Hora del Servidor * ---- Espartaco Palma Martinez esparta@NO_SPAMsofthome.net * ---- FUNCTION: ServerTime * ---- RETURNS: Server DateTime, NULL if cannot get DateTime * ---- RETORNA: Fecha y Hora Server, NULL si no puede obtenerlo * ---- PARAMETERS: tcPath - TYPE: Character * ---- Basandose en las recomendaciones de Alex Feldstein (MS MVP) * ---- sobre como obtener la fecha y hora del servidor. * ---- DATE: 03/DIC/2002 For Use in Visual FoxPro. * ---- Sample: * ---- ltFecha = FechaHora("F:/") * ---- ltFecha = FechaHora("//SERVER/VALUACION/") * ---- NOTE: If you are using minor versions of VFP7, use FoxTools * ---- NOTA: Si esta usando versiones menores a VFP7, use FoxTools ****************************************************************** FUNCTION ServerTime (tcPath) LOCAL ltFileTime tcPath = IIF(TYPE('tcPath') # 'C', SPACE(0),ADDBS(tcPath)) lcFile = tcPath+SUBSTR(SYS(2015), 4) +".tmp" IF STRTOFILE("Getting Hour/Obteniendo la Hora",lcFile) > 0 ltFileTime = FDATE(lcFile,1) ERASE (lcFile) RETURN ltFileTime ENDIF RETURN NULL ******************************************************************
19 de diciembre de 2002
Formulario Triangular
Coloca un formulario y escribe en el INIT:
#DEFINE C_ALTERNATE 1 #DEFINE C_WINDING 2 declare integer CreatePolygonRgn in gdi32 ; string@ ppoints, integer npoints, integer nfillmode declare integer SetWindowRgn in user32 ; integer hWnd, integer hRgn , integer bRedraw cPoints = num2dword(0)+num2dword(0); +num2dword(THIS.Width+(sysmetric(13)*1)); +num2dword(THIS.Height+sysmetric(9)+(sysmetric(12)*2)); +num2dword(0)+num2dword(THIS.Height+sysmetric(9)+(sysmetric(12)*2)) set library to foxtools.fll lnw = _WFindTitl(THIS.Caption) lnh = _WhToHWnd(lnw) lnr = CreatePolygonRgn(@cPoints, 3, C_WINDING) SetWindowRgn(lnh, lnr, 1) returnLuego, crea un .PRG llamado num2dword y coloca lo siguiente:
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 else * no es un numero valido para DWORD endif return lcresultMauricio Henao Romero
13 de diciembre de 2002
Impedir el cambio de dimension de una columna en un grid
THISFORM.Grid1.SetAll("Resizable", .F., "Column")Mauricio Henao Romero
8 de diciembre de 2002
Ejecutar comandos DOS en forma oculta
DECLARE LONG WinExec IN kernel32 ; STRING lpCmdLine , ; LONG nCmdShow cComando=" regsvr32.exe micontrol.ocx" WinExec(cComando, 0)
- 0 - no se muestra (se ejecuta, pero no se hace visible)
- 1 - la Ventana se Muestra Normal
- 2 - minimizado
- 3 - Maximizado
- 4 - Tamaño Normal, pero no le entrega el Foco al Programa/Comando que se ejecuta
Esto se puede implementar mejor, colocando este código en una función y pasando por parámetros el comando a ejecutar y el forma de visualización. Los comandos a ejecutar pueden ser de DOS o WINDOWS.
Horacio M. Kohler (HK)
5 de diciembre de 2002
Exportar favoritos
Probado en W98, 2000 y XP
x = createobject("Shell.UIHelper.1") x.ImportExportFavorites(.f.,"c:/Favoritos.Html") x = .null. release x
Tatis
2 de diciembre de 2002
Un servidor de base de datos económico con VFP y COM+
Introducción
Así que, en primer lugar, quiero dejar algo en claro: estoy de acuerdo en que un servidor de base de datos de primera clase es la verdadera solución. De hecho, los que usamos en mi empresa es SQL Server (y también Oracle). Si tiene al menos un par de años de experiencia con VFP, usar SQL Server no es tan difícil. Pero hay circunstancias en las que migrar a otro motor no es una opción válida. Tal vez ya tenga un montón de aplicaciones que no quiera -o no pueda- modificar, corriendo sobre tablas libres o DBCs de VFP. Lo que necesita es descargar ciertas tareas intensivas; necesita una solución remota en VFP.
Desde los tiempos del DOS (en la era del FoxBase+), me apoyé en algún tipo de proceso del lado del servidor para optimizar tareas largas, manteniendo la carga de trabajo de los clientes lo más baja posible. En aquellos días, podíamos simplemente tener una tabla con un nombre de procedimiento y un campo de estado. Cuando uno quería correr uno de estos procedimientos, abría la tabla, buscaba el registro apropiado, y si el estado estaba "desocupado", lo cambiaba a "ejecutar".
Al igual que los mamíferos, los sistemas operativos evolucionan. Windows llegó (presencié la aparición de Windows 2.0 ¡buah!), y pudimos moldear un poco nuestro código para que fuese más piadoso. Pero las cosas seguían sin ser ideales.
Este servicio es un administrador de componentes que se encarga del caché, la optimización de recursos, la coordinación de transacciones entre componentes, y más. Para ponerlo en términos simples, es un espacio que alberga sus DLLs para compartirlas con aplicaciones externas en una forma controlada y eficiente. Para el propósito de este artículo, es también muy importante que hace muy sencillo el compartir estos componentes a través de la red.
Las bases de nuestro servidor de base de datos barato son sencillas. Veamos primero todo el código:
DEFINE CLASS Executor AS Session OLEPUBLIC PROCEDURE SqlCommand( tcCommand as String ) as String local lcReturn lcReturn = "" Close Databases all Set Path To c:\develop\vfp\miniserver &tcCommand If Empty( Alias() ) or Reccount() = 0 * Nada que hacer Else CursorToXML( Alias(), "lcReturn", 1, 1 + 8, 0, "1") endif Return lcReturn ENDPROC ENDDEFINE
DEFINE CLASS Executor AS Session OLEPUBLIC
PROCEDURE SqlCommand( tcCommand as String ) as String
local lcReturn lcReturn = ""
Close Databases all Set Path To c:\develop\vfp\miniserver
&tcCommand
If Empty( Alias() ) or Reccount() = 0 * Nothing to do Else CursorToXML( Alias(), "lcReturn", 1, 1 + 8, 0, "1" ) endif
Return lcReturn ENDPROC ENDDEFINE
Ahora debemos probarlo. Guárdelo como MiniServer.prg y desde la ventana de Comandos, úselo:
loServer = NewObject( "Executor", "MiniServer.prg" ) messageBox( loServer.SqlCommand( "select * from products into cursor Parts") )
loServer = NewObject( "Executor", "MiniServer.prg" ) lcXml = loServer.SqlCommand( "select * from products into cursor Parts") XMLToCursor( lcXml, "Results" ) Browse
loServer = CreateObject( "miniServer.Executor" ) lcXml = loServer.SqlCommand( "select * from products into cursor Parts") XMLToCursor( lcXml, "Results" ) Browse
Pero hasta ahora estamos corriendo el cliente y el servidor en la misma máquina. Movamos la DLL a otra parte. Puede hacer las siguientes pruebas en su PC de desarrollo sin problemas, pero lo aliento a hacerlo en otra, sólo para que aprecie cuán fácil es construir un entorno de procesamiento distribuido con VFP y COM+.
Tenga cuidado con un detalle. La ruta que fije en la DLL debe apuntar al directorio en que realmente residen sus datos dentro del servidor en el que correrá su componente. Veremos más adelante porqué es tan importante indicar explícitamente esta ruta.
Figura 2: Consola Administrativa de Servicio de Componentes
Primero navegue el árbol de la Consola hasta su máquina, y dentro de ésta, hasta Aplicaciones COM+. Allí haga clic derecho y cree una nueva aplicación. Se disparará un Asistente así que es sumamente sencillo hacerlo:
- Haga clic en Siguiente en la pantalla de Introducción
- Haga clic en Crear una aplicación vacía
- Llámela MiniServer e indíquela como Aplicación de servidor.
- Seleccione el usuario que el proceso utilizará al correr. Esto puede ser peliagudo en aplicaciones reales y en un ambiente seguro. En muchos casos puede creara un usuario especial sólo con los derechos para asegurar que el componente puede funcionar según lo previsto. Para nuestro propósito actual, use su nombre de usuario y contraseña.
- Haga clic en Finalizar.
- Haga clic en Siguiente en la pantalla de Introducción.
- Haga clic en Instalar nuevos componentes.
- Navegue hasta el directorio donde copió la DLL y selecciónelo.
- Se poblarán dos listas. Arriba se ven los detalles de los archivos, mientras que debajo se ven los diferentes métodos. Sólo mírelos y haga clic en Siguiente.
- Haga clic en Finalizar.
Figura 3: Nuestro flamante componente COM+
- Haga clic en Siguiente en la pantalla de Introducción.
- Ingrese la ruta y nombre completo para el archivo de Microsoft Installer que este proceso va a generar. Usemos el mismo directorio donde ubicamos la DLL y en honor a la creatividad, llamémoslo Miniserver.msi. Luego seleccione Proxy de Aplicación, ya que esta opción creará un setup para que nuestros clientes sepan cómo instanciar el componente remotamente (la otra opción le permite generar una instalación para reproducir este paquete en otro servidor).
- Haga clic en finalizar. Le prometo que no usaremos más Asistentes en el resto del artículo.
Ahora puede ejecutar este archivo MSI y rápidamente preparará cualquier terminal para utilizar su componente. Para probarlo, simplemente copie y pegue este pequeño cliente de ejemplo:
Close Databases all Clear Local loServer as MiniServer Local lcXml as string, lcFilter as String loServer = CreateObject( "miniServer.Executor" ) lcFilter = InputBox( "Filtro", "Ingrese las primeras letras a buscar", "B" ) lcXml = loServer.SqlCommand( "select * from products where prod_name like '" ; + Alltrim( lcFilter ) +"%'" ) If Empty( lcXml ) * Nada que hacer Else XMLToCursor( lcXml, "Results" ) Browse last endif
Lo que sucedió es que la ruta especificada en la clase no existe en el servidor. No importa si puso los datos en el mismo directorio que la DLL. Cuando ésta se instancia dentro de Servicio de Componentes, su directorio por defecto es System32 dentro de la carpeta principal de Windows. Pero podría que tampoco se de este caso, así que siempre debe asegurarse de establecer las rutas apropiadas.
Por supuesto sólo hemos arañado la superficie de lo que podemos hacer con COM+. La idea principal de este ejemplo es brindar una manera de ejecutar comandos SQL básicos (select, update, delete, etc). Puede mejorar este componente fácilmente con un método para reindexar y compactar su base de datos (si está usando tablas de VFP), ejecutar procesos largos o complejos, y mucho más. Por supuesto, también deberá agregarle control de errores.
El objetivo principal es mostrar cuán fácil es usar esta funcionalidad. No hace falta que lea libros enteros o haga un largo curso para poder poner esta solución en marcha, utilizando VFP 7, XML y COM+ para construir su primer aplicación distribuida en menos de media hora.
Martín Salías (Buenos Aires, Argentina) es Director de Investigación y Desarrollo en Merino Aller, una empresa de software ERP y MRP para el mercado latinoamericano y español. Lleva veinte años como desarrollador y ha usado todas las versiones de FoxPro desde FoxBase+. Es Consultor de Universal Thread y co-Editor de UTMag/Rapozine.
15 de noviembre de 2002
Como saber si el usuario tiene permisos administrativos
Declare IsNTAdmin IN advpack.dll ; LONG dwReserved, ; LONG lpdwReserved ? IsNTAdmin(0,0)
Jorge Mota
7 de noviembre de 2002
Saber los Captions de todos los forms abiertos
Local Caption_F, Ciclo if _screen.formcount=0 ? 'Ningun Formulario Abierto.' RETURN endif dimension CAPTION_F [ _SCREEN.FORMCOUNT] **Obtenemos los captions, y los metemos a la matriz for Ciclo = 1 to _SCREEN.FORMCOUNT caption_F[ciclo]=ALLTRIM(_screen.forms(ciclo).caption) endfor ***mostramos los captions encontrados. for Ciclo = 1 to _SCREEN.FORMCOUNT ? 'Numero ' + STR(CICLO) + ' Caption: ' + caption_F[ciclo] endforJorge Mota
30 de octubre de 2002
.NET for Visual FoxPro Developers
by Kevin McNeish (English)
Title: .NET for Visual FoxPro Developers
Author: Kevin McNeish
Edited by: Cathi Gero
ISBN: 1-930919-30-1
Length: 550 pages
Press date: September, 2002
Ebook format: .PDF
Resumen
Si tienes curiosidad acerca de lo que ofrece .Net, este libro proporciona una visión fuerte de .NET Framework y los lenguajes C# y Visual Basic. Le ayudara a evaluar estas nuevas tecnologías a través de la vision de Visual FoxPro. Si ya están "vendidos" y están listos para aprender detalles acerca de cómo utilizar .NET en sus proyectos de desarrollo de software, este libro ofrece un montón de información de "Como", "Paso a paso" y "Mejores prácticas" que le ayudará escalar la curva de aprendizaje de .NET y marchar rápidamente.
El primer capítulo establece el escenario para el resto del libro por responder preguntas como "¿Qué es .Net"? y "¿Por qué debería estar interesado en .NET"?
En los otros capítulos se ven los detalles de los lenguajes C# y Visual Basic .NET para ayudarle a decidir qué es lo mejor para Uds.
Mas información: http://www.hentzenwerke.com/catalog/netvfp.htm
29 de octubre de 2002
Campos calculados en tablas SQL Server 2000
Se preguntaran que tiene que ver esto con los campos calculados, por ejemplo al definir una tabla en SQL Server, podemor crear un campo calculado de esta manera, ejemplo:
CREATE TABLE [tbl_empleados] ( [id_empleado] [char] (10) COLLATE Modern_Spanish_CI_AS NOT NULL , [nombres] [char] (60) COLLATE Modern_Spanish_CI_AS NULL , [cuota_facturacion] [numeric](10, 2) NULL CONSTRAINT [PK_tbl_empleados] PRIMARY KEY NONCLUSTERED ( [id_empleado] ) ON [PRIMARY] ) ON [PRIMARY]
Almacenamos en ella una lista de empleados con su codigo, nombre y cuota de facturacion, posteriormente necesitariamos una tabla donde almacenar las horas laboradas y hacer un calculo del valor con relacion al tiempo trabajado multiplicado por su cuota de facturacion, hariamos entonces una tabla parecida a esta:
CREATE TABLE [tbl_registro_de_empleados] ( [id_empleado] [char] (10) COLLATE Modern_Spanish_CI_AS NOT NULL , [nombres] [char] (60) COLLATE Modern_Spanish_CI_AS NULL , [cuota_facturacion] [numeric](10, 2) , [horas_trabajadas] [numeric](10, 2) , [valor_a_pagar] as cuota_facturacion*horas_trabajadas ,
Esta tabla esta bien, podriamos almacenar el codigo del empleado, sus horas trabajadas, almacenar el valor por hora y el campo calculado nos daria el resultado de multiplicar las horas por su cuota de facturacion. Sin embargo, cuando hagamos un informe, necesitariamos hacer una relacion con el archivo de empleados, para saber su nombre, y alli es donde entra a funcionar lo que les quiero dar a conocer, en SQL 2K, se implementaron las UDF (funciones definidas por el usuario), podriamos usar un select e incluir una funcion que creemos que nos devuelva el nombre del usuario en base al registro seleccionado y esto tambien funciona, sin embargo, algo que probe y funciono de maravilla, fue el crear un campo calculado en una tabla que haga referencia a una funcion y que almacene automaicamente el valor que nos devuelve la funcion por cada registro introducido, evitando asi, el hacer relaciones de mas y sobre todo, el mantener actualizada la data en la tabla, e aqui el ejemplo de como realizar un campo calculado que llame a una funcion para devolver data de otra tabla.
Primero creamos la funcion:
CREATE FUNCTION fn_nombre_empleado (@id_empleado char(10)) RETURNS char(60) AS BEGIN declare @nombre char(60) select @nombre=ltrim(rtrim(nombres)) from tbl_empleados where id_empleado=@id_empleado return @nombre END
Luego redefinimos nuestra tabla y le agregamos un campo calculado que se llame nombre del empleado la tabla quedaria de esta manera:
CREATE TABLE [tbl_registro_de_empleados] ( [id_empleado] [char] (10) COLLATE Modern_Spanish_CI_AS NOT NULL , [nombres] [char] (60) COLLATE Modern_Spanish_CI_AS NULL , [cuota_facturacion] [numeric](10, 2) , [horas_trabajadas] [numeric](10, 2) , [valor_a_pagar] as cuota_facturacion*horas_trabajadas , [Nombre] as .dbo.fn_nombre_empleado(tbl_registro_de_empleados.id_empleado))
Con esto obtendriamos que cada vez que se ingrese un codigo de empleado, la tabla nos despegaria el nombre del mismo y podriamos hacer informes mucho mas rapido y sin tener que almacenar la data o hacer relaciones.
Espero que les sirva.
Fernando España
26 de octubre de 2002
Ejemplo basico de ADO
* EjemploADO.prg
* Algunos ejemplos de uso de ADO desde VFP * Jose M. Marcenaro - 2001/11/12 * ------------------------------------------- * Para tener intellisense de estos objetos (VFP 7) * agregué previamente la Type Library de ActiveX Data Objects * al Intellisense Manager (Solapa Types / Type Libraries) LOCAL loConn AS ADODB.CONNECTION LOCAL loCmd AS ADODB.COMMAND LOCAL loRs AS ADODB.Recordset LOCAL lcConnString AS STRING * conexion por seguridad integrada de Windows lcConnString = "Provider=SQLOLEDB;Data Source=(local);Initial Catalog=Northwind;Integrated Security=SSPI" * conexion por usuario / password de SQL Server * lcConnString = "Provider=SQLOLEDB;Data Source=MSSQL;Initial Catalog=Northwind;User Id=sa;Password=pass;" * abro la conexion loConn = CREATEOBJECT("ADODB.Connection") loConn.CursorLocation= 3 && adUseClient loConn.OPEN(lcConnString) * realizo un select simple loRs = loConn.Execute("SELECT * From Products") MostrarRecordset( loRs) loRs.CLOSE * creo un stored procedure para poder invocarlo despues ON ERROR xx=0 && ignorar si ya existe el SP loConn.Execute( ""; +"create procedure Probar (@Inicial varchar(10)) as "; +" select * from Products where ProductName like @Inicial+'%'") ON ERROR * invoco al SP recien creado loRs = loConn.Execute("exec Probar 'M'") MostrarRecordset( loRs) loRs.CLOSE * lo invoco mediante un objeto Command (acceso a coleccion parámetros) loCmd = CREATEOBJECT("ADODB.Command") loCmd.ActiveConnection = loConn loCmd.CommandText = "Probar" loCmd.CommandType = 4 && adCmdStoredProc loCmd.PARAMETERS.REFRESH && obtiene parametros del SP loCmd.PARAMETERS.ITEM(";@Inicial") = 'L' loRs = loCmd.Execute() MostrarRecordset( loRs) loRs.CLOSE RETURN ******************************************************** PROCEDURE MostrarRecordset( loRs AS ADODB.Recordset) LOCAL ln, li, lcStr lcStr = "" FOR ln = 1 TO 10 && primeros 10 registros como máximo IF loRs.EOF then EXIT FOR ENDIF FOR li=1 TO MIN(loRs.FIELDS.COUNT, 3) && primeros 3 campos como máximo lcStr = lcStr+TRANSFORM(loRs.FIELDS.ITEM(li).VALUE)+"," NEXT lcStr = lcStr+CHR(13) loRs.MoveNext NEXT MESSAGEBOX(lcStr) ENDPROCJose Marcenaro
Microsoft MVP
da Vinci - Argentina
22 de octubre de 2002
Verificar la ortografía en VFP con Word
Este código de Trevor Hancock está en el Artículo Q271819 de la Base de Conocimientos de Microsoft.
Para probarlo, solo hay que pegar el código en un nuevo .PRG y ejecutarlo desde VFP.
************ START CODE *********** PUBLIC oform1 oform1=CREATEOBJECT("form1") oform1.SHOW RETURN ************************************** * DEFINE CLASS form1 AS FORM HEIGHT = 250 WIDTH = 375 SHOWWINDOW = 2 AUTOCENTER = .T. BORDERSTYLE = 2 CAPTION = "VFP - Word Spell Checking" MAXBUTTON = .F. NAME = "Form1" ADD OBJECT edttexttocheck AS EDITBOX WITH ; HEIGHT = 163, ; LEFT = 23, ; TOP = 21, ; WIDTH = 332, ; NAME = "edtTextToCheck" ADD OBJECT cmdcheckspelling AS COMMANDBUTTON WITH ; TOP = 207, ; LEFT = 115, ; HEIGHT = 27, ; WIDTH = 149, ; CAPTION = "Check Spelling", ; NAME = "cmdCheckSpelling" PROCEDURE findword *~~~~~~~~~~~~~~~~~~~~~ * PROCEDURE FindWord * * AUTHOR: Trevor Hancock, , Microsoft Corporation * CREATED : 08/22/00 11:50:32 AM * * ABSTRACT: Locates an installation of MS Word using the FindExecutable API * Creates a file with a .doc extension, checks the association on that * file using FindExecutable, then deletes the file. FindExecutable returns * the full, null terminated path to the application associated with * .doc files (in this case). * ACCEPTS: Nothing * RETURNS: Full path to the application associated with .doc files on this machine. *~~~~~~~~~~~~~~~~~~~~~ LOCAL lcPath, lcResult, lcFileName, llRetVal, ; lcCurDir, lnFileHand, lcWordPath lcPath = SPACE(0) lcResult = SPACE(128) llRetVal = .F. *!* Determine the DIR this form is running from. JUSTPATH() and ADDBS() *!* could be used here instead (if using VFP6), but this code will work *!* in any VFP version. lcCurDir = SUBSTR(SYS(16,0),ATC([ ],SYS(16,0),2)+1) lcCurDir = SUBSTR(lcCurDir,1,RAT([],lcCurDir)) lcFileName = lcCurDir + SYS(3) + [.doc] *!* Create a file with a .doc extension. *!* Could use STRTOFILE() here in VFP6. lnFileHand = FCREATE(lcFileName,0) = FCLOSE(lnFileHand) DECLARE INTEGER FindExecutable IN shell32 STRING @lcFilename, ; STRING @lcPath , STRING @lcResult *!* Determine the file association on .DOC files IF FindExecutable(@lcFileName, @lcPath, @lcResult) > 32 *!* Strip off trailing chr(0) lcWordPath = UPPER(SUBSTR(lcResult,1,LEN(ALLTR(lcResult))-1)) IF [WINWORD] $ lcWordPath llRetVal = .T. ENDIF ENDIF *!* Clean up after ourselves ERASE (lcFileName) RETURN llRetVal ENDPROC PROCEDURE DESTROY IF TYPE([goWord]) = [O] IF TYPE([goWordDoc]) = [O] goWordDoc.SAVED = .T. goWordDoc.CLOSE ENDIF goWord.QUIT ENDIF RELEASE goWord, goWordDoc ENDPROC PROCEDURE INIT *--- English * THIS.edtTextToCheck.VALUE = "Thhis text has mistakees in it. We will seend " + ; * "it to Word and have it cheked." *-- Español THIS.edtTextToCheck.VALUE = "Ezte tecto esta escrito kon herrores ppara " + ; "que Word lo chequee." ENDPROC PROCEDURE cmdcheckspelling.CLICK *~~~~~~~~~~~~~~~~~~~~~ * PROCEDURE cmdcheckspelling.CheckSpelling * * AUTHOR: Trevor Hancock, Microsoft Corporation * CREATED : 08/22/00 12:03:46 PM * * ABSTRACT: Automates MS Word to check the spelling of text in * THISFORM.edtTextToCheck * ACCEPTS: Nothing * RETURNS: Nothing *~~~~~~~~~~~~~~~~~~~~~ IF TYPE([goWord]) # [O] && Check if you have already instantiated Word IF !THISFORM.FindWord() && You don't have Word up, so let's locate it. MESSAGEBOX([Microsoft Word is either not installed or is incorrectly registered.], + ; 0,[Word Start-Up Failed]) RETURN .F. ENDIF *!* Change the mouse pointer for all form controls to indicate processing (opening Word) WITH THISFORM .cmdCheckSpelling.MOUSEPOINTER = 11 .edtTextToCheck.MOUSEPOINTER = 11 .MOUSEPOINTER = 11 ENDWITH PUBLIC goWord, goWordDoc && Public vars for Word and Document1 in Word. goWord = CREATEOBJECT([WORD.APPLICATION]) && Create Word WITH goWord .WINDOWSTATE= 0 && wdWindowStateNormal (needs to be Normal before you can move it) .MOVE(1000,1000) && Move the window out of view goWordDoc = .Documents.ADD ENDWITH *!* Change mouse pointers back WITH THISFORM .cmdCheckSpelling.MOUSEPOINTER = 0 .edtTextToCheck.MOUSEPOINTER = 0 .MOUSEPOINTER = 0 ENDWITH ENDIF WITH goWordDoc .Content.TEXT = ALLTRIM(THISFORM.edtTextToCheck.VALUE) .ACTIVATE IF .SpellingErrors.COUNT > 0 .CHECKSPELLING ELSE =MESSAGEBOX([Spell check complete. No errors found],0,[Spell Check]) ENDIF *!* For some reason, Word likes to make itself visible here. Keep it hidden... goWord.VISIBLE = .F. THISFORM.edtTextToCheck.VALUE = .Content.TEXT ENDWITH ENDPROC ENDDEFINE * ********************************** *!*********** END CODE ***********
18 de octubre de 2002
COM+ con VFP 7.0 - Parte 3
Traducción de Jorge Espinosa.
Las transacciones son una parte importante de cualquier mecanismo de actualización de datos. La parte 3 de Craig Berntson nos introduce en la Coordinación de Transacciones Distribuidas y explica como usar las transacciones bajo COM+.
Transacciones
Sin ellas no podemos estar seguros que los datos están siendo escritos en todas las tablas involucradas en la actualización. Antes de introducirnos en como trabajan las transacciones en COM+ hagamos una revisión rápida de transacciones.
Revisión de Transacciones
Joe quiere transferir $100 de su cuenta de pagos a su cuenta de cheques. El camina hacia el cajero, inserta su tarjeta, presiona el botón para empezar la transferencia. Durante la escena, esta transferencia puede se lograda de dos maneras. La primera manera es que el balance de la cuenta de pagos de Joe puede ser reducida por $100 y el balance de su cuenta de cheques incrementada por $100. La segunda opción es que el balance de su cuenta de cheques puede ser incrementada y el balance de su cuenta de pagos puede ser decrementada. Pero que pasa si allí sucede una caída del sistema entre las dos actualizaciones? Bajo la primera escena Joe no estaría feliz. El perdió $100. En el segundo ejemplo Joe está muy feliz. El es $100 mas rico pero el banco tiene ahora una pérdida de $100. Lo que nosotros queremos es esto, en el evento de la caída mencionado, o ambas cuentas deben se actualizadas o ninguna debe ser actualizada. El propósito de la transacción es asegurar que esto pase. Las transacciones deben seguir la regla ACID, que es, atomicidad, consistencia, aislamiento y durabilidad.
La Atomicidad significa que o todas o ninguna de las actualizaciones se ejecuten. La Consistencia significa que si una transacción falla los datos son retornados al mismo estado que tenían antes de empezar la transacción. El aislamiento significa que una transacción no sepa que otra transacción se está haciendo. Finalmente, la Durabilidad significa que el estado de la transacción se guarda, no importa que pase en el sistema.
Esto es manejado generalmente a través de un log de transacciones.
Podemos implementar transacciones en nuestros datos de VFP al usar los comandos BEGIN TRANSACTION / END TRANSACTION / ROLLBACK. El pseudo código muestra un procedimiento de ejemplo:
BEGIN TRANSACTION LlSavingsOk = UpdateSavings(-100) LlCheckingOk = UpdateChecking(100) IF llSavings and llChecking END TRANSACTION Else ROLLBACK ENDIFLas transacciones en VFP solo trabajan con datos de VFP, y las transacciones de VFP carecen de la regla de la durabilidad de ACID. No hay ningún log de transacciones.
Si estuviésemos usando SQL Server necesitaríamos usar un mecanismo diferente. Podemos usar comandos de ADO para llamar a SQL server cuando empieza o termina una transacción o podemos codificar en un stored procedure.
Sin embargo, que pasaría si la cuenta de pagos estuviese en SQL Server y los datos de la cuenta de cheques estuviesen en Oracle? Las transacciones de Sql Server solamente trabajarían en Sql Server, y las transacciones de Oracle solamente trabajarían con Oracle. Necesitamos una sola transacción que trabaje con ambas base de datos. Aquí es donde entra la Coordinación de Transacción Distribuida (DTC).
Usando el DTC
El DTC nos permite tener transacciones que crucen base de datos. Esto significa que tenemos datos en ambos, Sql Server y Oracle, que actualizaríamos por la misma transacción. Bajo MTS y VFP 6, los datos de Fox no podrían participar en la transacción DTS.(Esto cambia bajo COM+ y VFP7. Veremos acerca de esto mas adelante). Volviendo al DTC.
El DTS usa un commit en dos fases. Básicamente, en la primera fase, el DTS pregunta a la base de datos si puede hacer una actualización. Si todos los datos responden que si a esa pregunta, comienza la segunda fase, en el que el DTS le dice a la base de datos que debe actualizar los datos. Si alguna de las base de datos responde que no el DTS le dice a las bases de datos que se hace un Roll Back de la actualización.
Veamos como las transacciones son manejadas bajo MTS:
DEFINE CLASS Math AS SESSION OLEPUBLIC FUNCTION Multiply(tnNum1, tnNum2) LOCAL lnResult, LomTX, LoContext ** Creamos una referencia al objeto MTS LoMtx = CREATEOBJECT("MTXAS.APPSERVER.1") ** Creamos una referencia al objeto Contexto LoContext = loMTX.GetObjectContext() lnResult = tnNum1*tnNum2 ** Cierra la transaccion si existe y le avisa a MTS que ha finalizado con el uso del componente LoContext.SetComplete() ** Si existiera un error podriamos abortar la transacción y avisarle a MTS que finalizamos ** Con el componente usando loContext.SetAbort() RETURN lnResult ENDFUNC ENDDEFINENote que cuando nosotros hacemos Commit o abortamos la transacción, MTS también descargará el componente.
Esto significa que si el próximo comando en nuestra aplicación necesita el componente, tendremos que hacer otro CREATEOBJECT() en el cliente. Veamos como cambia esto en COM+:
DEFINE CLASS Math AS SESSION OLEPUBLIC FUNCTION Multiply(tnNum1, tnNum2 as Number); AS Number HELPSTRING "Multiplica ; Dos números y devuelve el resultado" LOCAL lnResult, LomTX, LoContext, loContextState, ; LlDone, llGetDone, lnTxnState, lnGetTxnState LlDone = .T. LlGetDone = .F. LnTxnState = 1 LnGetTxnState = 0 ** Creamos una referencia al objeto MTS LoMtx = CREATEOBJECT("MTXAS.APPSERVER.1") ** Creamos una referencia al objeto Contexto LoContext = loMTX.GetObjectContext() ** Crea una interfaz al estado del Contexto LoContextState = GETINTERFACE(loContext, "iContextState") lnResult = tnNum1*tnNum2 ** Establece el seteo de la Transacción (Consistencia) ** 0 = Commit, 1 = Abort LoContextState.SetMyTransactionVote(lnTxnState) LoContextState.GetMyTransactionVote(@lnGetTxnState) ** Lleva adelante el seteo de Activacion ** .T. = Desactiva , .F. = Deja Activado loContextState.SetDesctivateOnReturn(llDone) loContextState.GetDeactivateOnReturn(@llGetDone) RETURN lnResult ENDFUNC ENDDEFINEEn este ejemplo, podemos hacer Commit o abortar la transacción con SetMyTransactionVote, pero tenga en cuenta la instancia del componente permanecerá activa hasta que explícitamente llamemos a SetDeactivateOnReturn con .T. como parámetro.
Ahora estará preguntándose donde encaja el DTC en esto COM+ automáticamente llama al DTC por usted
El DTC habla con la base de datos a través del uso de Resource Manager.
MTS y COM+ ya vienen con Resource Managers para Oracle y SQL Server, pero no para VFP. Es por esto que los datos de VFP no podrían tomar parte en una transacción MTS.
Resulta que Resource Managers es muy difícil para implementar. Entonces, por esto es que COM+ es llamado Compensating Resource Managers(CRMs). CRMs son mas fáciles de implementar que un Resource Managers. Al usar un CRM podemos tener nuestros datos involucrados en una transacción DTC.
Un CRM consiste en dos partes, la CRM Worker y la CRM Compensator. Esto corresponde a las dos fases del commit del DTC. Un detalle en discusión sobre el CRM está fuera del alcance de este capítulo. Sin embargo, VPF 7 viene con un ejemplo de aplicación CRM. Encontrará esto en las carpetas SAMPLES\COM+\CRM bajo la instalación de su VFP 7.
Configurando transacciones COM+
Como muchos de los rasgos en COM+, las transacciones son manejadas en tiempo de ejecución. Abrir el Component Service Manager (applet) y elegir uno de sus componentes. Click derecho en el componente y seleccione propiedades, seleccione el tab de transacciones. Verá cinco opciones de transacciones.(figura1 y tabla1)
También notará el ckeck box "Override global transaction timeout value". Cuando seleccione Required o Required new transaction, este ckeckbox estará habilitado. Entonces puede ingresar el número de segundos que correrá la transacción antes del time out.
Esto fue un acercamiento a las transacciones. En la parte 4 daremos un vistazo a las ventajas de COM+ con Queued Components.
Figura 1.
Tabla 1. Tipos de transacciones soportados por componentes COM+.
Seeting | Descripción |
---|---|
Disabled | La transacción no se necesita. Elija esta cuando no necesite la sobrecarga de trabajo que implica generar una transacción, por ejemplo un componente que no tiene que actualizar ningún dato. |
Not Supported | Impide a un componente usar la transacción, sin tener en cuenta el estado transaccional de la llamada al componente. |
Supported | El componente participa en la transacción si una está activa. |
Required | El componente participa en la transacción si una está activa. Si no hay ninguna transacción activa, genera una nueva. |
Requires New | Una transacción nueva es generada siempre. |
Craig Berntson es Microsoft Certified Solution Developer y fue nombrado cuatro veces Microsoft Most Valuable Profesional. Disertante de varias conferencias de FoxPro en todo EEUU y eventos de Microsoft en Salt Lake. Es Presidente del Salt Lake City Fox Users Group y actualmente ingeniero de software senior para 3m Health Information Systems. |
15 de octubre de 2002
Conferenciantes PortalFox España 2003
Miguel Egea (portalsql.com)
Nacío en Calasparra (Murcia) en 1969, Estudió Informática en la Universidad de Castilla la mancha y comenzó su vida laboral como Freelance desarrollando aplicaciones comerciales para sectores tan dispares como la peluquería o la banca. Fundó el departamento de informática de Laboratorios Munuera en Murcia y desarrolló sus primeras aplicaciones con Bases de Datos Access hace ya más de 10 años. Desde hace ya bastantes años está trabajando con SQL-SERVER desde su versión 6.5 hasta la actual 2.000. En este periodo ha dirigido y colaborado en numerosos desarrollos en la consultora Sinergia Tecnológica (http://www.sinergiatec.com) que está integrada en el grupo IT-Deusto (http://www.itdeusto.com). En Sinergia Tecnológica dirige varios proyectos de desarrollo de software en plataformas Microsoft con bases de datos diversas como Sql-Server, Oracle, ... Desde mayo de 2002 es MVP en SQL-SERVER por su colaboración en la comunidad On-Line de Microsoft. También pertenece a PASS Spanish Group, la asociación de profesionales de SQL-Server. Es el webmaster de www.portalsql.com.
Miguel Angel Hernaiz (gnomo.net)
Nacido en 1964 en Madrid, lleva desde el año 1981 metido entre pantallas y teclados (luego vendrían los ratones). Autodidacta de nacimiento, empezó en el mundo de los mini y grandes mainframes: S36, S38, Kienzle, AS400... Hasta que llegó a sus manos un PC de IBM. Descubrió Dbase, Clipper, las comunicaciones por modem... Alaska Xbase, Multimedia, Internet, Vfoxpro, Html, Sql server, Oracle... Gracias a Internet empezó a colaborar con el resto de desarrolladores de VFP, en chats, foros, News, y forma parte del VFUG y UniversalThread desde sus inicios, allá por el 96. Junto con Fernando y Juan Carlos, tuvo la gran oportunidad de elevar su nivel en OOP y plataformas cliente-servidor. Colabora con multiples empresas de Analista, Programador y Tecnico de Sistemas y Aplicaciones. Ha publicado en revistas y foros, y se empeña en que el nivel de los que le rodean sea más y mas alto cada día. Ahora anda por Barcelona entre servidores WEB y MAIL, .NET, SQL Server y Desarrollo de sitios Web, con VFP como capa de datos y reglas de negocio, por supuesto..
Pedro Hernandez Muñoz (www.pedrohernandez.com)
Nacido hace 36 años en Almelo ( Holanda ) , inició su andadura en el campo informático allá por el año 80 estudiando Cobol cuando tenia 14 años en Valencia. A partir del año 87 inició la programación xBase con dBase y Clipper, lo que le permitió colaborar durante un tiempo con una empresa que aún hoy es líder en el tema de la enseñanza y edición de libros, en la que también escribió artículos en sus publicaciones. Tras esta experiencia se traslada a Madrid y comienzo a desarrollar proyectos de aplicaciones siempre con FoxPro y Visual FoxPro para diversas empresas, editando en el año 1998 el libro “Visual FoxPro 5. Desarrollo de Aplicaciones” en la editorial McGrawHill distribuido en España y Latinoamérica.
Luis Maria Guayan (Vicente Trapani, S.A.)
37 años, nació en Tartagal, Salta (Argentina) y desde hace mas de 15 años que reside en Tucumán (Argentina). Comenzó a programar en lenguajes xBase en el año 1990 (dBase, FoxPlus y Clipper). Con estas herramientas realizó sus primeros trabajos profesionales como desarrollador independiente. En el año 1994 ingresó a trabajar a tiempo completo en el Área Sistemas de Vicente Trapani S.A., una empresa de Tucumán (Argentina) dedicada a la producción y exportación del limón y todos sus derivados. Allí comenzó a desarrollar con FoxPro 2.6 para Windows y todas las versiones posteriores hasta Visual FoxPro 7. Actualmente es el Responsable del Área Sistemas. Casi la totalidad del software desarrollado por el Área Sistemas de Vicente Trapani S.A. está realizado en FoxPro y Visual FoxPro. Desde el año 1997 participa en los Grupos de Noticias de Visual FoxPro en español. Es allí que a principios del año 2000 se contactó con Pablo Roca para colaborar con PortalFox y sumando esfuerzos poder contar con un sitio para todos los desarrolladores en Visual FoxPro de habla hispana. En Febrero del año 2002 fue nombrado Microsoft MVP (Most Valuable Professional) en Visual FoxPro, por sus colaboraciones en las distintas comunidades "on-line".
Fernando Guerrero (www.callsql.com)
Fernando nació en Santo-Tomé (Jaen) en el 1959, y desde el 1976 está directamente involucrado en el desarrollo de aplicaciones informáticas de un modo u otro. Actualmente ocupa el puesto de Principal Technologist y SQL Server Product Consultant en QA, la empresa de formación en Tecnología de Información líder en el Reino Unido, donde se encarga de desarrollar e impartir nuevos cursos, así como aplicar sus conocimientos en proyectos de consultoría y mentoring, en particular en tecnologías .NET y SQL Server. Fernando es un ponente habitual en conferencias del sector, como TechEd, PASS, VBUG, VBITS, MCTCon, SQLLive, VSConnections, SQL Server Magazine Live, y DevWeek. Escribe para SQL Server Magazine y SQL Server Professional, y es el coautor del libro "Microsoft SQL Server 2000 Programming by Example" (QUE Corporation 2001, ISBN 0-7897-2449-9, ISBN 9879460634 para la version en castellano), y está escribiendo en estos momentos otros libros sobre VB.NET y SQL Server 2000. Fernando es Ingeniero Técnico de Obras Públicas (Hidrología) y Diplomado Universitario en ICCP, MCDBA, MCSE+I, MCSD, MCT y SQL Server MVP (Most Valuable Professional) desde el año 2000.
Jose Enrique LLopis (FUTURA Systems & software)
Nacido en Alicante, 45 años y comencé a trabajar con los sistemas IBM-360. De ahí pasé sucesivamente por los sistemas 370, 32, DEC Rainbow , SECOINSA S30/5 y S20, etc .... Me entretuve con los PDP's y VAX/MicroVAX de Digital y a continuación me sumergí en los ordenadores personales. De ahí pasé a los UNIX, primero sobre los IBM RT6150/51 y después sobre plataforma PC. En 1984 entré en la Caja de Ahorros Provincial de Alicante y de ahí pasé a la Caja del Mediterráneo siempre con sistemas personales y departamentales siendo el responsable por ejemplo del diseño de la red de servidores central para toda la empresa. Comencé a trabajar en fox desde FoxBase, pasando por Foxpro hasta ahora que utilizo VFP7. Hoy día además de mi trabajo en la CAM soy socio de una empresa consultora especializada en productos para el mercado inmobiliario llamada FUTURA Systems & software.
Les Pinter (Pinter Consulting)
Nació en Houston, Texas, pero pasó gran parte de su adolescencia en México. Regresó a Texas para continuar sus estudios superiores. Mientras estudiaba para un doctorado en economía en la Universidad Rice, formó una empresa para desarrollar y comercializar un procesador de texto llamado La Varilla Mágica. En 1980, vendió la Varilla Mágica a un joven de 23 años de Seattle llamado Bill Gates, para formar la base de Microsoft Word. Continuó a trabajar en el área de las microcomputadoras, siguiendo la carrera de desarrollador de base de datos a partir de 1982.
En 1985 Les empezó a trabajar con un nuevo producto llamado FoxBASE, y después de varios años inició la publicación de una revista mensual, The Pinter FoxBASE Letter, en Menlo Park, California. Poco después, Fox Software anunció su nueva generación, FoxPro. Después de otros dos años, salió Visual FoxPro, un lenguaje plenamente orientado a objetos, y Microsoft compró la compañía Fox Software.
Desde entonces, Les publicó su revista mensual por más de 10 años en California, y por 4 años en Moscú. Tiene equipos de programadores en México y en Rusia que colaboran en proyectos de base de datos para varios clientes en los Estados Unidos. Ha participado como ponente en conferencias de Microsoft, Advisor Media, DevConnections y otras, y ha presentado seminarios y cursos en varias universidades en Rusia, México y Cuba. Es autor de seis libros y más de 270 artículos sobre varios temas de la informática. Su próximo libro, Desarrollo de Aplicaciones de Base de Datos con Visual Basic.NET y Visual FoxPro 7, estará disponible a fines de 2002. Les es también piloto privado, y tocaba guitarra en varios conjuntos musicales durante los años 60.
Fernando Puyuelo (Informática Borsan)
Nacido en Villafranca del Bierzo, León. Tiene 34 años. Licenciado en Ciencias Físicas, Especialidad Cálculo Automático en la Universidad Complutense de Madrid. Comenzó a trabajar en el año 1992 en Informática Borsan,S.L., empresa dedicada al desarrollo de software, y de la que actualmente es Director Técnico y socio. Comenzó el desarrollo de aplicaciones en Clipper, pero muy pronto comenzó el desarrollo de las mismas en el incipiente lenguaje que parecía iba a ser el sustituto a Xbase y Clipper, Foxpro. Después de evaluar la versión FOX 20, comenzó a trabajar con la versión Foxpro 2.5 DOS en la realización de un programa de generación de estadísticas. Ha pasado por todas las versiones de desarrollo de Foxpro, hasta llegar a la actual VFP 70. Actualmente la empresa maneja aplicaciones desarrolladas en todas las versiones de windows, incluyendo Fox 26, dada en muchas ocasiones las lentas ganas de migrar de los usuarios, pero poco a poco traspasando todo a VFP70. Actualmente desarrollando un nuevo programa de gestión de Sociedades de Tasación, terreno en el cual la empresa es lider en el mercado español, en un modelo de tres capas, usando como no, VFP. Fernando ha participado en todos los congresos y eventos relativos a foxpro desde 1994. Impulsor de los primeros chat en castellano, así como de la primera fase de la creación de las news de Microsoft en torno a Visual Foxpro. Miembro activo de la comunidad desde sus comienzos..
Pablo Roca (Clavo Congelados, S.A.)
Tiene 42 años, nacido en Palma de Mallorca (España). Pero toda la vida ha vivido en La Coruña. Dedicado al diseño y desarrollo de aplicaciones desde hace 22 años. Los inicios fueron en FORTRAN y tarjetas perforadas allá por el año 1980 en un viejo mainframe de la Universidad de Vigo. Ha trabajado desde técnico de sistemas en Fujitsu España, hasta el puesto que ocupa actualmente como Director de Informática en Clavo Congelados. Impartió diversas conferencias tanto en grupos de usuarios, como en reuniones de informática y colaboró en la presentación del Visual Foxpro 5 junto con Microsoft. Durante unos años fué colaborador de MacWorld España, donde escribia artículos y comparativas de bases de datos. Sus inicios en Foxpro fueron desde los tiempos de Fox Software, con el Foxbase, hasta hoy en día que está con el VFP. Creador de PortalFox, que es la comunidad en línea más grande de habla hispana sobre Visual Foxpro. Es nombrado en Octubre de 2001 como Microsoft MVP (Most Valuable Professional) de VisualFoxpro por su aportación a la comunidad.
Alberto Rodriguez (FoxPress)
Nacido en Palencia (España). 42 años. Licenciado en CC.Económicas y PDG del IESE. Empezó a trabajar en Informática con FoxBase+. Ha desarrollado aplicaciones de Gestión para Empresas, Hospitales y Seguros publicadas en las principales revistas del sector (www.flasof.com). Mentor de FoxPress (www.fpress.com) revista con 10 años de antiguedad y dedicada a Visual FoxPro. Organizador de 5 Congresos sobre Fox. Ha dado cursos y publicado artículos con cuyo código trabajan los desarrolladores de FoxPro de todo el mundo. Es Gerente de la empresa Flash Software.
Ramon Seoane (Molduras del Nororeste)
Licenciado en Informática por la Universidad de La Coruña. Amplia experiencia como programador desarrollando aplicaciones de tipo industrial con autómatas, de comunicaciones con toda clase de dispositivos, de gestión comercial, de producción, etc. En el año 1999 obtengo el título de MCP en Visual Basic y en el 2000 el de Visual Fox. Como responsable del departamento de Sistemas de la empresa Molduras del Noroeste, he llevado a cabo la ardua labor de llenar los escritorios de cabecitas de zorro, haciéndose más popular que el cocodrilo de Lacoste. He utilizado Visual Fox como cliente de SQL Server realizando una aplicación ERP enteramente con dichas herramientas, incluso cuando no había un sólo libro sobre el tema y había que echarle algo de imaginación. Fiel defensor del Fox desde sus orígenes, he impartido cursos desde aquel Dbase hasta el Visual Foxpro 6.0 y participado en el IV Congreso de desarrolladores de Fox comentando las posibilidades de la FOXISAPI aprovechando la información que amablemente Rick Strahl me permitión incorporar.
Jorge Serrano Pérez (portalvb.com)
Nació en Madrid en 1973. Ingeniero Técnico en Informática de Gestión por el ICAI y Microsoft MVP. Su experiencia profesional se extiende por el mundo de la gestión, de la informática y de las telecomunicaciones, especializado en el desarrollo web y de aplicaciones de gestión y de negocio fundamentalmente, posee conocimientos extensos sobre Visual Basic dónde comenzó con la versión 1.0 de este producto, ASP, C#, ADO .NET y SQL Server principalmente, si bien sus conocimientos se extienden en un amplio abanico de aplicaciones de diferentes características y fabricantes. Entre otras cosas, es el propietario, webmaster y administrador de PortalVB.com (http://www.portalvb.com), el primer portal del mundo de habla hispana dedicado a la tecnología .NET con gran cantidad de información para el informático, desarrollador, administrador de sistemas y estudiante, es autor y colaborador de diferentes libros para la editorial Anaya Multimedia (http://www.anayamultimedia.es), de artículos técnicos en la revista técnica Windows TI Magazine (http://www.windowstimag.com), y colaborador de MSDN Latinoamérica (http://www.microsoft.com/latam/msdn) y de MSDN España (http://www.microsoft.com/spain/msdn).
10 de octubre de 2002
Conferencia VFP España 2003
Se celebrará del 19 al 21 de Febrero de 2003 en La Coruña (España). Aunque el 19 serán sesiones introductorias (VFP, SQL Server y NET), lo que posiblemente hará que la mayoría de la gente venga solo el 20 y 21.
El costo está todavía sin cerrar del todo pero andará en torno a 100 Euros e incluirá una o dos comidas. Actualmente ya tenemos 10 conferenciantes, bastantes MVPs de SQL Server, VFP y NET.
Lo que si anticipo, que os tendréis que apurar cuando se abra el registro, ya que se calculan problemas de plazas (a pesar de que el salón admita unas 400 personas).
Posibles conferencias:
- Webservices (I) introducción
- WebServices (II) intermedio
- Novedades del VFP8
- Visual FoxPro y productos de terceros (Mabry, Xceed, Leadtools, PDF ...)
- Introduccion a NET Framework
- ADO.NET o ASP.NET (con VFP)
- VFP e internet (WebConnection, voodoo)
- Control de proyectos
- Introduccion a VFP
- Automatización de Office con VisualFoxpro
- Consultas en Visual FoxPro
- VFP y programacion orientada a objetos
- VFP con SQL Server
- VFP y NET
- Introduccion a SQL Server
- Administracion de SQL Server
- Optimizacion de SQL Server
- COM en VisualFoxpro -
- Acceso a Datos (vistas, SQL Passtrough, CursorAdapter) -
- Bases de datos en VFP (buffering, reglas de validacion, procedimientos almacenados, database events)
- 101 trucos en VFP
- Control de Errores en VFP (TRY .. CATCH)
- Event Binding
- Diseño multicapa (objetos de negocio, ...)
- Visual FoxPro Toolkit para .NET
- Intellisense con VFP
- .....
Revisión 15/10/2002
Las conferencias se haran en dos bloques, dia 19 solo alumnos y como dia extra para profesionales, seria de pago para los profesionales fundamentelmente por el tema de darles de comer.
Y dias 20 y 12, para ambos (profesionales y alumnos), para estos dos dias tenemos 12 horas de sesiones, la posibilidad de sesiones simultaneas está un tanto limitada, ya que la segunda sala, no tiene nada que ver (tanto en calidad como en espacio, es de 150 plazas). Por tanto se dará solo una sesión al tiempo. Lo que si haremos es en dicha aula complementaria, una vez que los conferenciantes finalizen se iran alla, para ampliar información, comentar trucos, etc ... la tendremos como un aula de charlas.
Dia 19 Sesiones:
- Introducción a SQL Server - Fernando Guerrero/Miguel Egea
- SQL Server (1) - Fernando Guerrero/Miguel Egea
- SQL Server (2) - Fernando Guerrero/Miguel Egea
- Introducción a VFP (1) - Pedro Hernandez
- Introducción a VFP (2) - Pedro Hernandez
- Introducción a VFP (3) - Pedro Hernandez
- Introducción a NET(1) - Jorge Serrano
- Introducción a NET(2) - Jorge Serrano
Debido a que tenemos solo posibilidad de 12 sesiones, mas la keynote, este seria un boceto mas aproximado:
- Webservices - Pablo Roca
- VFP y NET - Alberto Rodriguez
- VFP e internet - Jose Enrique Llopis
- Office con VFP - Luis Maria Guayan
- VFP y OOP - Ramon Seoane
- VFP y SQL Server - Ramon Seoane
- Acceso a Datos (vistas, SQL Passtrough, CursorAdapter) - Luis Maria Guayan
- Control de Errores en VFP (TRY .. CATCH) - Les Pinter
- XML - Les Pinter
- Optimizacion de SQL Server - Fernando Guerrero/Miguel Egea
- ASP.NET con VFP - Jorge Serrano
- Sesiones offline - Fernando Puyuelo/Miguel Angel Hernaiz
En la que mas tengo dudas es en la de XML, quizas seria mejor profundizar mas en otra area que dedicar 1 hora entera a XML.
Necesito opiniones sobre estas conferencias, alguna idea adicional sobre conferencias, etc ...
Pablo Roca
25 de septiembre de 2002
Convertir Números a Letras
Function Convnum(Total) * Autor: Anselmo Antonio Ortiz Alcocer * Corrreo: ortizanst@hotmail.com * 26/06/2001 Dimension aUnidades(9), aDecenas(14), aCentenas(10) aUnidades(1) = 'UN' aUnidades(2) = 'DOS' aUnidades(3) = 'TRES' aUnidades(4) = 'CUATRO' aUnidades(5) = 'CINCO' aUnidades(6) = 'SEIS' aUnidades(7) = 'SIETE' aUnidades(8) = 'OCHO' aUnidades(9) = 'NUEVE' aDecenas(1) = 'DIEZ' aDecenas(2) = 'ONCE' aDecenas(3) = 'DOCE' aDecenas(4) = 'TRECE' aDecenas(5) = 'CATORCE' aDecenas(6) = 'QUINCE' aDecenas(7) = 'VEINTE' aDecenas(8) = 'TREINTA' aDecenas(9) = 'CUARENTA' aDecenas(10) = 'CINCUENTA' aDecenas(11) = 'SESENTA' aDecenas(12) = 'SETENTA' aDecenas(13) = 'OCHENTA' aDecenas(14) = 'NOVENTA' aCentenas(1) = 'CIEN' aCentenas(2) = 'DOSCIENTOS' aCentenas(3) = 'TRESCIENTOS' aCentenas(4) = 'CUATROCIENTOS' aCentenas(5) = 'QUINIENTOS' aCentenas(6) = 'SEISCIENTOS' aCentenas(7) = 'SETECIENTOS' aCentenas(8) = 'OCHOCIENTOS' aCentenas(9) = 'NOVECIENTOS' vTotal = str(int(Total), 12) Do case Case empty(val(vTotal)) Texto = 'CERO PESOS' Case val(vTotal) = 1 Texto = 'UN PESO' Otherwise tCientos = obt_cant(substr(vTotal,10,3)) tMiles = obt_cant(substr(vTotal,7,3)) tMillones = obt_cant(substr(vTotal,4,3)) tMilMillones = obt_cant(substr(vTotal,1,3)) tCientos = tCientos tMiles = iif(empty(tMiles), '', ; iif(tMiles='UN', '', tMiles + ' ') + 'MIL ') tMillones = iif(empty(tMillones), '', ; tMillones + ' MILLON' + iif(tMillones='UN', ' ', 'ES ') +; iif(empty(tMiles + tCientos), 'DE', '')) tMilMillones = iif(empty(tMilMillones), '', ; iif(tMilMillones='UN', '', tMilMillones + ' ') + 'MIL ' +; iif(empty(tMillones), 'MILLONES ', ' ') +; iif(empty(tMillones + tMiles + tCientos), 'DE', '')) Texto = strtran(tMilMillones + tMillones + tMiles + tCientos, ' ', ' ') + ' PESOS' Endcase Return Texto + iif(!empty(Total), ' CON ' + ; strtran(transform(int((total - int(total)) * ; 100), '**'), '*', '0') + '/100 M.N.', '') Function obt_cant(valor) Public Unidades, Decenas, Centenas If empty(val(valor)) Return '' Endif Store '' to tUnidades, tDecenas, tCentenas Unidades = int(val(substr(valor,3,1))) && 123 Decenas = int(val(substr(valor,2,1))) && vTotal = 999 Centenas = int(val(substr(valor,1,1))) && ^^^ valor = int(val(valor)) tUnidades = iif(!empty(unidades), aUnidades(Unidades), '') If !empty(decenas) If decenas = 1 tDecenas = iif(val(right(str(valor,3),2)) >= 10 and ; val(right(str(valor,3),2)) <= 15, aDecenas(val(right(str(valor,3),2)) - 9), 'DIECI' + tUnidades) tUnidades = '' Else tDecenas = aDecenas(decenas + 5) if !empty(unidades) tDecenas = left(tDecenas, len(tDecenas) - 1) + 'I' Endif Endif Endif If !empty(centenas) tCentenas = aCentenas(centenas) If valor > 100 If centenas = 1 tCentenas = tCentenas + 'TO ' Else tCentenas = tCentenas + ' ' Endif Endif Endif Return tCentenas + tDecenas + tUnidadesAnselmo Antonio Ortiz Alcocer
23 de septiembre de 2002
Problemas con tu OCX ?
El problema es que las ocx están diseñadas para que ellas mismas se registren durante el evento constructor. Se llama a una función que tienen todas las ocx llamada DLLRegisterServer. El problema está en que Power Builder no llama a esta función. Incluso si ejecutas la utilidad que viene con windows REGSRV, la ocx falla al registrarse a si misma. Para corregir este problema, en el objeto donde tu estás usando la ocx, en el evento constructor llama a la funcion DLLRegisterServer.
Declara una función externa local en el objeto:
Function long DllRegisterServer()Library "ocxname.OCX"
y en el evento Constructor:
LONG ll_RC ll_RC = DllRegisterServer()
Ramón Rodríguez Martínez
17 de septiembre de 2002
Búsquedas sencillas
De cúantas maneras quieren tus usuarios hacer consultas? Probablemente demasiadas.
No te cuento cuántas veces mis usuarios me han dicho "quiero poder buscar lo que sea." Pero cuando les digo que esa posibilidad no viene incluido en el lenguaje, sino que va a costar dinero, y que dependiendo de como lo haga puede ser increiblemente lento, usualmente piden otra cosa - algo rápido y barato.
Afortunadamente hay por lo menos dos maneras de añadir un grupo de consultas a cualquier pantalla. Se hace en unos cuantos minutos, y dado el precio, la mayoría de los usuarios descubrirán que hace todo lo que quieren - o al menos, todo lo que están dispuestos a pagar. Lo cual es precisamente lo que queríamos saber en primer término...
1 - Usa un marco de página con una Listbox cuyas columnas pueden ser ordenadas en la última página.
Este truco se aprovecha del hecho de que en el caso de las ListBox con RowSourceType = 2 (Alias), el puntero de la tabla se mueve a medida que el usuario cambia el registro seleccionado en la ListBox. (RowSourceType = 2 quiere decir usa los primeros n campos de la tabla como fuente de datos para las columnas del control, mientras que RowSourceType = 6 (Campos) significa que vas a especificar los nombres y el orden de los campos que aparecen en las columnas del control. Al pasar el usuario por los registros mostrados en el control, el puntero de la tabla o el cursor se mueve correspondientemente. Así que el puntero de la tabla siempre está de acuerdo con el muestreo. Pero, me preguntas, esto ¿cómo me ayuda con las búsquedas?
Consideremos qué debe lograr una consulta. Se usa una consulta para localizar algo rápidamente. Tradicionalmente, usando SQL, hacemos una consulta que le muestra al usuario todo lo que se asemeja a lo que busca, y le dejamos escoger el registro exacto de esa selección reducida. Pero esto implica una TextBox para permitir que escriban lo que quieren buscar, lo cual puede contener errores ortográficos; y ¿buscamos mayúsculas o minúsculas? ¿Hay cupo en la pantalla para entrar la clave? ¿Donde pondremos la lista de candidatos a seleccionar? Y ¿qué haremos con los registros que no hacen juego con lo que entró el usuario pero sí son lo que está buscando?
En vez de hacer todo esto, aprovechemos el hecho de que una tabla FoxPro con todos sus registros ya está disponible. Sencillamente vamos a mostrar una grid con las columnas más importantes que le permitirá al usuario seleccionar visualmente, cambiando el orden de una u otra columna hasta ver lo que estaba buscando. Y lo mejor es que se puede hacer esto con sólo unas cuantas líneas de código.
Como siempre, FoxPro proporciona un mecanismo muy sencillo para hacer esto. Primero, añadimos un método llamado ColumnSort al formulario. Si usas una clase base para todos tus formularios, práctica que recomiendo sin reservaciones, añade esta clase a la clase de formulario que usas como plantilla. La sentencia PARAMETER debe ser la primer línea de código en el método:
* ColumnSort method PARAMETER IndexTagName WITH THISFORM SELECT (.DataEnvironment.InitialSelectedAlias) SET ORDER TO ( IndexTagName ) .LockScreen = .T. .PageFrame1.Pages[.PageCount].MyList1.Refresh .LockScreen = .F. ENDWITHYo siempre escribo el nombre de la tabla o alias principal de cada formulario en la propiedad InitialSelectedAlias del Entorno de Datos del mismo. Esto me permite escribir código genérico para habilitar métodos como BorrarRegistro, ProximoRegistro, etc.
Luego, como lo implica el códogo, arriba, añade un marco de página (PageFrame) al formulario y pon una ListBox en la última página. Como RowSourceType usa 6, y como RowSource el nombre de la tabla principal, más los nombres de las columnas que aparecen en la tabla, e.g. "CLIENTES.Nombre,Telefono,Ciudad,Estado,Contact", usando los nombres que aparecen en los encabezados de las columnas de la ListBox. Se usa la propiedad ColumnCount de la ListBox para precisar el número de columnas, y el número de píxeles de anchura de cada columna, separadas con comas en la propiedad ColumnWidth. Se suele usar Max(anchura del valor más ancho que aparecerá en la columna,anchura del título que aparece en el encabezado) como la anchura para cada columna.
Después de poner una etiqueta encima de cada columna, usa lo que sigue en el evento Click de cada una de las etiquetas:
THISFORM.SetIndex ( THIS.Caption )El método SetIndex cambia el orden de la tabla y refresca el contenido que aparece en la lista. (Si tu índice no tiene el mismo nombre que la etiqueta, sigue leyendo - no es difícil manejar tales casos.)
PROCEDURE SetIndex PARAMETERS IdxName WITH THISFORM SELECT (.DataEnvironment.InitialSelectedAlias ) SET ORDER TO ( IdxName ) .PageFrame1.Page2.MyList1.Requery ENDWITHFinalmente, esto es el código DblClick de la ListBox:
PROCEDURE mylist1.DblClick WITH THISFORM .LockScreen = .T. .PageFrame1.ActivePage = 1 .PageFrame1.Page1.Refresh .LockScreen = .F. ENDWITHEsto activa la primera página, la cual muestra el contenido de los demás campos del registro seleccionado mediante el puntero de la ListBox.
Como indicamos, esto asume que se usan TAGS de índice cuyos nombres son exactamente los mismos que las capciones de las etiquetas que sirven de encabezados para la ListBox en la última página. Si prefieres usar nombres de índices que no se pueden usar como TAGS de índice (por ejemplo, capciones que contienen espacios), el método SetIndex debe incluir una forma de usar la capción para encontrar el nombre del TAG, a saber:
SET ORDER TO TAG ( ; IIF(IdxName=[Nombre del Cliente],[Nombre], ; IIF(IdxName=[Apellido del Cliente],[Apellido] , IdxName)))
Un Doble-Click en la lista nos lleva a la primera página:
Qué pasa si hay muchísimos registros?
Desafortunadamente, este método es demasiado lento cuando se usa con tablas grandes en una máquina con memoria limitada. En una P266 con 32MB de memoria, trabaja bien hasta llegar a los 10,000 registros. Pero si a FoxPro le falta memoria para guardar el índice actual en la memoria, se vuelve lenta la búsqueda - cosa que los usuarios acostumbrados a la rapidez de FoxPro no toleran muy bien. Por esto, para efectuar búsquedas de tablas con muchos registros, es mejor crear una pantalla de búsqueda separada.
2 - Usa una pantalla de consulta para retornar la clave deseada
Para lograr esto, añade al forulario un botón de comando que a su vez abre otro formulario que contiene una ListBox para mostrar los resultados de la consulta. Pon una combobox y una textbox al fondo de la pantalla, para permitirle al usuario precisar las características que quiere usar para localicar registros. El combobox especifica el nombre del campo a usar, y el textbox el valor a buscar en el campo especificado.
El evento Valid del textbox es entonces responsable por cargar todos los registros que contienen el valor especificado en el campo seleccionado. El usuario usa una DobleClick en uno de los registros que aparecen en la lista para seleccionar un valor y retornar su código. El código del botón de consulta del formulario que abrió la pantalla de consulta entonces usa el valor retornado para encontrar y mostrar el registro seleccionado.
Dentro del formulario de consulta, el código Valid del combobox asegura el formateo correcto de la clave entrada por el usuario:
THISFORM.ActiveTag = THIS.Value WITH THISFORM.MyText1 DO CASE CASE THIS.Value = "Name" .Format = "K" .InputMask = "!!!!!!!!!!" THISFORM.KeyField = [UPPER(LastName)] THISFORM.OrderField = [LastName] CASE THIS.Value = "Company" .Format = "K" .InputMask = "!!!!!!!!!!" THISFORM.KeyField = [UPPER(Company)] THISFORM.OrderField = [Company] CASE THIS.Value = "Zip" .Format = "K" .InputMask = "#####" THISFORM.KeyField = [Zip] THISFORM.OrderField = [Zip] CASE THIS.Value = "Phone" .Format = "K" .InputMask = "(###)########" THISFORM.KeyField = [Phone] THISFORM.OrderField = [Phone] CASE THIS.Value = "CustNo" .Format = "K" .InputMask = "#####" THISFORM.KeyField = [CustNo] THISFORM.OrderField = [CustNo] ENDCASE .Visible = .T. ENDWITH THISFORM.Input = SPACE(30) KEYBOARD [{RightArrow}]El código del método Valid de la TextBox carga y muestra todos los registros que cumplen con los requisitos del usuario:
WITH THISFORM.MyList1 .RowSourceType = 0 .Clear ENDWITH WITH THIS * Crea la clave para el número de teléfono, * quitándole todo lo que no sea un dígito: IF .Value = [(] IF LEN(TRIM(.Value)) > 5 .Value = LEFT(THIS.Value,8) + [-] ; + IIF(SUBS(THIS.Value,9,1)=[-], ; SUBS(THIS.Value,9), ; [-]+SUBS(THIS.Value,9)) ENDIF ENDIF Key = TRIM(.Value) SELECT CUSTOMER SET ORDER TO TAG ( THISFORM.Mycombo1.Value ) SET EXACT OFF && Importante! Permite claves parciales * will not match if SET("EXACT") = "ON" SEEK Key DO WHILE ( NOT FOUND() ) AND ( LEN(Key) > 0 ) Key = LEFT ( Key, LEN(Key)-1 ) SEEK Key ENDDO ENDWITH IF ( NOT FOUND() ) OR ( LEN(Key) = 0 ) IF ( NOT FOUND() ) MessageBox ( "No match", 64, "My App" ) ENDIF THISFORM.Init RETURN ENDIF WITH THISFORM .LockScreen = .T. .MousePointer = 11 ENDWITH WITH THISFORM.MyList1 .RowSourceType = 0 cmd = [SELECT lastname,firstname,company,phone,zip,custno] ; + [ FROM CUSTOMER] ; + [ WHERE ]+THISFORM.KeyField+[ = "]+Key+["] ; + [ INTO CURSOR Matches] ; + [ ORDER BY ] + THISFORM.OrderField &cmd .RowSource = "Matches" .RowSourceType = 2 .Requery .Refresh .Selected[1] = .T. ENDWITH WITH THISFORM .SetAll ( "ForeColor", RGB(0,0,0),"MyLabel") * SORT NO LONGER APPLIES .LockScreen = .F. .MousePointer = 1 ENDWITHYo le cambio el color de los encabezados de las columnas para indicar visualmente que los datos ya están ordenados:
He aquí el código del evento DblClick de la ListBox:
THISFORM.ReleaseY el código del método UNLOAD del formulario es esto:
RETURN THISFORM.MyList1.ValueEn el código Click del botón de comando de Búsqueda del formulario original hay que tener esto:
DO FORM Search TO m.CustCode IF NOT EMPTY ( m.CustCode ) SEEK m.CustCode THISFORM.Refresh ENDIFExisten decenas de variaciones que pueden ser derivadas de este método básico. En mi ejemplo, deshabilité el mecanismo de consulta una vez que hayan seleccionado un campo de búsqueda de la ComboBox. Si quieren efectuar otra consulta, tienen que cerrar el formulario y volverlo a seleccionar, del mismo formulario, de otro, o del menú. En algunas aplicaciones sería preferible permitirle al usuario quedarse en la misma pantalla y hacer cuantas consultas quiere antes de seleccionar un registro y retornar - por ejemplo, para buscar padrones en una tabla de facturas antiguas para una auditoría.
Se pueden construir consultas sofisticadas para otros casos. Por ejemplo, para seleccionar todos los registros padres que tiene al menos un registro hijo con determinada característica - todas las facturas en las cuales una de las cosas compradas fué un podador de césped, por ejemplo - se puede crea una subconsulta:
SELECT &FldList. FROM INVOICES ; WHERE INVNO IN ; ( SELECT INVNO FORM INDETAIL ; WHERE [LAWNMOWER] $ UPPER(DESC) ) ; ORDER BY INVDATE DESC INTO CURSOR MatchesTambién se pueden crear subconsultas que incluyen una lista determinada de valores - por ejemplo, las facturas de los tres clientes más importantes:
SELECT &FldList. FROM INVOICES ; WHERE CustNo IN ("00102","00352","00601")
14 de septiembre de 2002
Calcular la edad de una persona
Función para calcular la edad de una persona.
*----------------------------------------------------- * FUNCTION Edad(tdNac, tdHoy) *----------------------------------------------------- * Calcula la edad pasando como parámetros: * tdNac = Fecha de nacimiento * tdHoy = Fecha a la cual se calcula la edad. * Por defecto toma la fecha actual. *----------------------------------------------------- FUNCTION Edad(tdNac, tdHoy) LOCAL lnAnio IF EMPTY(tdHoy) tdHoy = DATE() ENDIF lnAnio = YEAR(tdHoy) - YEAR(tdNac) IF GOMONTH(tdNac, 12 * lnAnio) > tdHoy lnAnio = lnAnio - 1 ENDIF RETURN lnAnio ENDFUNC
Luis María Guayán
11 de septiembre de 2002
Campos "Autoincrementales" en VFP (anteriores a la versión 8)
Nota: Esto es para versiones anteriores a VFP8, ya que esta versión ya posee nativamente la característica de campos autoincrementales en sus tablas.Para lograr esto seguimos los siguientes pasos:
- Creamos una Base de Datos de ejemplo: "EjemploDBC.DBC"
- Creamos dos Tablas de ejemplo: "Ejemplo1.DBF" y "Ejemplo2.DBF"
- Creamos una Tabla que almacenará los valores Id's Autoincremetales de cada tabla que lo necesite. Esta tendrá un registro por tabla: "Tabla_Ids.DBF"
- Creamos un Procedimiento Almacenado: "NuevoId(tcAlias)"
El siguiente código crea la DBC y DBF's.
CREATE DATABASE EjemploDBC SET DATABASE TO EjemploDBC *-- Tabla de Ejemplo CREATE TABLE Ejemplo1 ; (uId I NOT NULL DEFAULT NuevoId("Ejemplo1"), ; cCampo C(10) NOT NULL) *-- Tabla de Ejemplo CREATE TABLE Ejemplo2 ; (uId I NOT NULL DEFAULT NuevoId("Ejemplo2"), ; nCampo N(10,2) NOT NULL) *-- Tabla con los nombres de tablas y sus Id's CREATE TABLE Tabla_Ids ; (NombreTabla C(30) NOT NULL, ; uId I NOT NULL) INDEX ON UPPER(NombreTabla) TAG NomTab *-- Por cada tabla que requiera un campo * Id Autoincremental añado un registro INSERT INTO Tabla_Ids VALUES("Ejemplo1", 0) INSERT INTO Tabla_Ids VALUES("Ejemplo2", 0) CLOSE DATABASES RETURN
Para crear el Procedimiento Almacenado, modificamos la Base de Datos "EjemploDBC.DBC" con:
MODIFY DATABASE EjemploDBCy en la ventana de Procedimientos Almacenados (Menú -> Base de datos -> Modificar procedimientos almacenados) copiamos la siguiente función:
FUNCTION NuevoID(tcAlias) LOCAL lcAlias, lnID, lnAreaAnt, lcReprAnt lnID = 0 lnAreaAnt = SELECT() lcReprAnt = SET('REPROCESS') SET REPROCESS TO AUTOMATIC lcAlias = UPPER(ALLTRIM(tcAlias)) IF NOT USED("Tabla_Ids") USE EjemploDBC!Tabla_Ids IN 0 ENDIF SELECT Tabla_Ids IF SEEK(lcAlias, "Tabla_Ids", "NomTab") IF RLOCK() REPLACE uId WITH Tabla_Ids.uId + 1 IN Tabla_Ids lnID = Tabla_Ids.uId UNLOCK ENDIF ENDIF SELECT (lnAreaAnt) SET REPROCESS TO lcReprAnt RETURN lnID ENDFUNC
Las tablas "Ejemplo1.DBF" y "Ejemplo2.DBF" contienen el campo uId que automaticamente se incrementa por el Procedimiento Almacenado cada vez que hacemos un INSERT.
Ahora miremos el ejemplo de como funcionan:
IF NOT USED("Ejemplo1") USE Ejemplo1 IN 0 ENDIF IF NOT USED("Ejemplo2") USE Ejemplo2 IN 0 ENDIF FOR ln = 1 TO 100 INSERT INTO Ejemplo1 (cCampo) ; VALUES(SYS(2015)) INSERT INTO Ejemplo2 (nCampo) ; VALUES(RAND()*1000) ENDF
Note que no se incluye el campo uId en la lista de los campos. El campo uId contine un valor por defecto. Este valor por defecto es realmente una llamada al procedimiento almacenado (NuevoId()) que retorna un número único que se agrega al campo uId de la tabla pasada como parámetro cuando añadimos un nuevo registro.
Nota: Parte del código de este artículo fue tomado y ligeramente modificado del artículo de la MSKB http://support.microsoft.com/kb/316910/es
7 de septiembre de 2002
Cursor al final del texto de un TextBox
Un truco para que cada vez que un TextBox recibe el foco, el cursor se ubique al final del texto. Solo tienes que poner en el evento GotFocus del TextBox el siguiente código ...
This.SelStart = LEN(RTRIM(This.Value))
... y listo, el cursor se colocará al final del texto del TextBox
Luis María Guayán
4 de septiembre de 2002
Un efecto "GROOVY" en la aplicación
Podemos agregar un efecto de cambio de colores que reaccionen al mouse
1. Agregar al InitEvent de un formulario el código de "efecto degradado por colores" de Ramón Rodriguez Ramírez:
*!* Rutina para generar degradados en Visual FoxPro *!* ----------------------------------------------- *!* Para probar este ejemplo, pegue el código *!* en el método init de un formulario y *!* presione CTRL+D. *!* Autor: L.C.C. Ramón Rodríguez martínez *!* Pais de procedencia: México. *!* Actualizacion: 7 de agosto de 2002 *!* Version 1.0 Local i,color1, R, G, B With Thisform .scalemode = 3 &&pixeles .drawstyle = 0 && sólido .drawwidth = 2 R=0 G=255 B=0 FOR i = 1 to 255 *-- Disminuimos valor de colores If r > 0 then r = r - 1 endif IF g > 0 then g = g - 1 endif IF b > 0 Then b = b - 1 endif color1 = RGB(R,G,B) *Establesco el color .forecolor=Color1 *- Dibujo linea sobre el fomulario .line (0,.ViewPortHeight * (i - 1) / 255, .ViewPortwidth, .ViewPortheight * i / 255) NEXT i EndWith
2. En el MouseMoveEvent del formulario agragar lo siguiente:
LPARAMETERS nButton, nShift, nXCoord, nYCoord _screen.backcolor=this.Point(nXCoord,nYCoord)
Y listo, un efecto tipo "disco" para el Screen principal.
Quiero agradecer a Ramón por enseñarme el "efecto degradado". Espero te guste lo que hice con tu código.
Lucio Manuel Núñez Ramírez
21 de agosto de 2002
¿Cómo saber si el equipo está conectado a Internet?
Tomado del news de microsoft en ingles;
? IsInternetActive() *********************************** Function IsInterNetActive (tcURL) *********************************** * PARAMETERS: URL, no olvidar pasar la URL completa, con http:// al inicio * Retorna .T. si hay una conexion a internet activa *Tekno *Wireless Toyz *Ypsilanti, Michigan *********************************** tcURL = IIF(TYPE("tcURL")="C" AND !EMPTY(tcURL),tcURL,"http://www.yahoo.com") DECLARE INTEGER InternetCheckConnection in wininet; STRING lpszUrl,; INTEGER dwFlags,; INTEGER dwReserved RETURN ( InternetCheckConnection(tcURL, 1, 0) == 1) ENDFUNC ***********************************Esparta Palma
19 de agosto de 2002
Preview en forma maximizada y con diálogo de impresión
Esta línea de código me pareció muy buena, lo único que solo funciona de la versión 6.0 en adelante...
KEYBOARD '{CTRL+F10}' PLAIN CLEAR REPORT FORM tureporte NOCO TO PRINTER PROMPT PREV
Chris Ascencio
16 de agosto de 2002
REGISTRAR Y DESREGISTRAR UN ARCHIVO OCX O DLL
DECLARE INTEGER DLLSelfRegister IN "Vb6stkit.DLL" ; STRING lpDllName *-- Esta es la ruta donde esta el archivo nombredll="s:/sistemas/ocx/hwnd.ocx" liRet = DLLSelfRegister(NombreDll) IF liRet = 0 Then SelfRegisterDLL = .T. MESSAGEBOX ("Registrado ocx") ELSE SelfRegisterDLL = .F. MESSAGEBOX ("Error- No Registrado ocx") ENDIFJorge Mota
15 de agosto de 2002
Print Preview en ventana Maximizada versión OOP
Cómo ya muchos sabran, las ventanas de Preview del reporteador no están maximizadas, por lo que para remediarlo se ha utilizado la instrucción define window, pero a continuación les comparto algo que para no variar encontré en los foros en ingles del news de microsoft.
*----------------------------- * Fred Taylor * Microsoft Visual FoxPro MVP * News: microsoft.public.fox.vfp.reports.printing * Subject: Re: Report preview window size control * Date: 05/19/2002 *----------------------------- oForm = CREATEOBJECT("Form") WITH oForm .Caption = "Tu Titulo del Preview" .WindowState = 2 && Maximized .Show() REPORT FORM tureporte PREVIEW WINDOW (.Name) .Release() ENDWITHEsparta Palma
14 de agosto de 2002
ACTIVAR Y DESACTIVAR EL PROTECTOR DE PANTALLA
DECLARE LONG SystemParametersInfo IN "user32"; LONG uAction,; LONG UParam,; LONG lpvParam,; LONG fuWinIni SPI_SETSCREENSAVEACTIVE = 17 SPIF_SENDWININICHANGE = 2 *-- para impedir que se active : =SystemParametersInfo (SPI_SETSCREENSAVEACTIVE, .F., 0, SPIF_SENDWININICHANGE) *-- Para volver a restaurarlo como estaba antes de nuestra intervención : =SystemParametersInfo (SPI_SETSCREENSAVEACTIVE, .T., 0, SPIF_SENDWININICHANGE)
Jorge Mota
13 de agosto de 2002
Autoregistrar OCX
------------------------------------------ DECLARE LONG DllRegisterServer IN [archivo.ocx] IF DllRegisterServer() = 0 * OK ELSE * Not OK ENDIFEste método también puede ser usado con archivos COM DLL.
Eric den Doop
12 de agosto de 2002
VACIAR LA CARPETA DE DOCUMENTOS RECIENTES
DECLARE LONG SHAddToRecentDocs IN "Shell32"; LONG lFlags,; LONG lPv =SHAddToRecentDocs (0, 0)
Jorge Mota
8 de agosto de 2002
WebRAD: Building Database Applications on the Web with Visual FoxPro and Web Connection
by Harold Chattaway, Randy Pearson & Whil Hentzen.
Title: WebRAD: Building Database Applications on the Web with Visual FoxPro and Web Connection
Author: Harold Chattaway, Randy Pearson & Whil Hentzen
Edited: Barbara Peisch
ISBN: 1-930919-07-7
Length: 502 pages
Press date: June, 2002
Ebook format: .PDF
Table of Contents
1. Introduction
2. What's In Web Connection
3. Your First Web Connect App - Installing Web Connection
4. Your First Sample Application
5. How the Internet Works
6. Complete Web Development Environment
7. Server Hardware and Hosting
8. Configuring Server Software
9. How a Web Page Works
10. Getting in Tune - Overcoming Conceptual Hurdles
11. Managing Your Configuration
12. A Web Connection Application from Start to Finish
13. Identifying Users and Managing Session Data
14. Com vs File Based Operation
15. 101 Things You Want to Know How To Do
16. Extending the Framework
17. Advanced Trouble Shooting and Maintenance
18. Asynchronous Services
19. XML and Distributed Applications
20. SOAP and Web Services
21. Web Connection Add on Tools
22. Version Updates
23. Making Your Site Successful
Para mas información: http://www.hentzenwerke.com/catalog/webrad.htm
7 de agosto de 2002
CAMBIAR LA ETIQUETA DE UNA UNIDAD DE DISCO
DECLARE LONG SetVolumeLabel IN "kernel32" ; STRING lpRootPathName,; STRING lpVolumeName =SetVolumeLabel("D:/","PROBANDO")
Jorge Mota
2 de agosto de 2002
COM+ con VFP 7.0 - Parte 2
Traducción de Jorge Espinosa.
En la primera parte de esta serie hemos revisado COM y MTS y nos hemos introducido en los servicios de COM+ en Windows 2000.También le he mostrado como crear una nueva aplicación COM+ e instalar el componente en el servidor. Ahora, en la parte 2, le mostraré cómo hacer que el cliente use el componente en el servidor, la seguridad y el manejo de errores.
¿El cliente siempre tiene razón?
En el articulo anterior, le he mostrado cómo instalar su aplicación en el servidor. Pero esto, no sirve de mucho si usted no puede usar el componente desde el cliente. Lo bueno es, que Windows 2000 hace que esto sea muy fácil. Yendo un poco hacia atrás, al componente que hemos creado e instalado, le damos un vistazo al código:
DEFINE CLASS Math AS SESSION OLEPUBLIC FUNCTION Multiply(tnNum1, tnNum2) LOCAL lnResult, loMtx, loContext *Creamos una referencia al Objeto MTS loMtx=CREATEOBJECT("MTXAS.APPSERVER.1") *Creamos la ref. al Contexto loContext=loMtx.GetObjectContext() lnResult=tnNum1*tnNum2 *Si hay alguna transacción abierta hacemos el Commit y avisamos al MTS *que terminamos de usar el objeto. loContext.SetComplete() RETURN lnResult ENDFUNC ENDDEFINEHe llamado a este el más tonto de los componentes COM. Todo lo que hace es multiplicar dos números. Sin embargo, manteniendo este simple código de ejemplo, nos permite concentrarnos en los aspectos COM+ del mismo.
Ahora, necesitamos hacer la instalación en el cliente. Volvamos a Component Services Manager. Expanda el árbol debajo de COM+ Applications y seleccione MyFirstCOMApp. Esta es la aplicación que hemos creado e instalado en el artículo anterior. Ahora, clic derecho sobre MyFirstCOMApp y seleccionar Export. El asistente de COM Application Export aparecerá (Figura 1). En primera instancia le solicitará el path completo y el nombre del archivo para su aplicación. Ingrese C:\COMApps\MyFirstCOMAppProxy. Luego asegúrese de haber seleccionado "Application Proxy-Install on other machines to enable access to this machine". (La otra opción, "Server Application", es utilizada cuando quiere instalar el componente en otro servidor). clic en Next para finalizar.
El asistente creará dos archivos MyFirstCOMAppProxy.MSI.CAB y MyFirstCOMAppProxy.MSI. Estos archivos pueden ser copiados e instalados en la computadora cliente. Estos archivos NO contienen el componente. Usted no necesita de él en el cliente. Estos archivos contienen información acerca de los métodos públicos y propiedades que expone el componente y punteros hacia el servidor que lo aloja de modo que Windows pueda encontrarlo e instanciarlo.
Nuevamente, usted nunca instala el componente en el cliente. En su lugar instala un proxy. Su aplicación "piensa" que el componente esta alojado y corriendo localmente, pero no es así. Tome nota de esto. Un componente instalado sobre un servidor de aplicaciones nunca corre en el cliente. Por alguna razón, este es un concepto difícil de entender para algunas personas.
Usted instancia el componente servidor exactamente de la misma forma que lo hace localmente. Pruebe esto en su ventana de comandos:
ox = CREATEOBJECT("MyCom.Math") ? ox.Multiply(3, 4) && devolvera 12 ox = NULL¿Porque ésto funciona?. VFP hace un llamado a Windows, solicitando instanciar el componente. Windows toma información del componente desde la Registry y encuentra que el componente se aloja en un servidor de aplicaciones. Windows entonces instancia el proxy del componente y hace una llamada al servidor para instanciar el componente. VFP no sabe que esta "hablando" con el proxy; "piensa" que lo hace directamente con el componente. Cuando usted llama al método Multiply, el proxy envía el llamado al Server vía DCOM y el resultado es pasado al proxy, quien a su vez se lo pasa a VFP.
¿Se siente inseguro?
Ahora que el componente está instalado en el servidor y el proxy en el cliente, permítame mostrarle cuan fácilmente podemos permitir o prohibir accesos a los componentes.
COM+ utiliza seguridad basada en roles. Un rol es un tipo de usuario. Por ejemplo, en un banco podría tener cajeros, gerentes, oficiales de cuenta, personal de servicios de clientes, etc. Cada una de esas personas cumple un rol. Si quiere prohibir a los cajeros hacer préstamos, en COM+ podría setear seguridad sobre el componente "PRESTAMO" para prohibir esto. Lo bueno es que esto es una opción de configuración que se puede cambiar utilizando el Component Service Manager.
Los roles están basados en los usuarios y grupos de Windows, así el primer paso en setear el esquema de seguridad es establecer los grupos de seguridad de Windows. Puede ser confuso para entender donde encajan los roles en las jerarquías de grupos y usuarios. Los archivos de ayuda COM+ dicen que : " Los Roles son categorías de usuarios que han sido definidos para la aplicación con el propósito de determinar los permisos de acceso para los recursos de la aplicación. El desarrollador asigna los roles (como categorías de usuarios simbólicas) para la aplicación". Esto se asemeja a un grupo de usuarios de Windows, tome esto con cuidado, piense en el rol como un grupo de usuarios para una aplicación específica.
Ahora volviendo sobre nuestro ejemplo del banco, tenemos cuatro grupos: cajeros, gerentes, oficiales de cuenta y servicios al cliente. Avanzamos y creamos estos en el servidor utilizando el User Manager de Windows NT o la herramienta Computer Management de Windows 2000. Ingrese el primer grupo de usuario y luego los otros tres.
Una vez que estos grupos han sido creados volvemos sobre MyFirstCOMApp en Component Services Manager. clic sobre roles, clic derecho y seleccione New Role. Ingrese el primer rol, cajero, y clic OK (figura 2). No es necesario nombrar los roles igual a los grupos de usuarios de Windows pero esto hace que la administración sea mucho mas fácil. Ahora cree los tres roles restantes gerente, oficial de cuenta y servicios al cliente.
Luego necesitamos identificar los usuarios en cada rol. Expanda el árbol debajo de cajero. Verá una carpeta llamada Users. clic sobre esa carpeta, clic derecho y seleccione New User. Ubique en la lista de grupos y usuarios de Windows y seleccione el grupo de usuarios Cajero, clic sobre Add. Lo mismo para gerentes y servicios al cliente. clic en Ok. Podrá ver cada uno de los grupos de seguridad de Windows agregados en el rol cajero en Components Services (figura 3).
Cuando un usuario es agregado al sistema este debe ser agregado a su correspondiente grupo de Windows que automáticamente lo agregará dentro de su rol correspondiente. Entonces tenemos identificados los roles, pero aún no le hemos indicado a Components Services sobre el uso de seguridad. clic sobre MyFirstCOMApp, clic derecho y seleccionamos Properties, allí seleccionamos la solapaSecurity. Habilitamos la opción "Enforce access checks for this application" y clic en Ok (figura 4). Ignore la otra opción por ahora que será tratada en breve.
Ahora clic derecho sobre MyComm.math y seleccione Properties, luego la solapa Security. Usted verá que está habilitada la opción"Enforce component level access checks". También verá la lista de seguridad para los roles. Simplemente seleccione el rol que tendrá acceso a este componente (figura 5).
Usted puede desplegar y asignar el acceso de seguridad a la interfaz o método en el nivel que usted desee. Esto le permite a usted tener un mismo componente con varias interfaces, cada una conteniendo diferentes niveles de seguridad. Si un usuario no tiene el permiso apropiado para el uso del componente, se mostrará un mensaje de error indicando que el componente no puede ser instanciado.
Ahora retomemos al cuadro de diálogo de Aplication Security (figura 4), allí hay algunas opciones adicionales que necesitamos discutir. La primera es el nivel de seguridad. Esta controla cuando COM+ valida la seguridad de usuario. Con la primera opción, "Perform access checks only at the process level", el chequeo del rol no será hecho a nivel de componente, interfaz o método. En la segunda opción,"Perform access checks at the process and component level", la seguridad es chequeada cuando se produce una llamada al componente. Casi siempre se debería utilizar la segunda opción.
No obstante, la primera opción es útil cuando usted ya tiene validado al usuario.
El siguiente seteo es "Authentication level for calls". Éste tiene seis opciones descriptas en la tabla 1.
Viendo a través de la lista cada opción toma progresivamente mas seguridad, note que el mayor nivel de seguridad está en validar el usuario. La opción por defecto es Packet.
Tabla 1. Niveles de autenticación
Nivel | Descripción |
---|---|
None | Sin autenticación. |
Connect | Chequea la seguridad sólo cuando el cliente se conecta al componente. |
Call | Chequea la seguridad al comienzo de cada llamada. |
Packet | Chequea la seguridad y valida que todos los datos fueron recibidos. |
Packet Integrity | Chequea la seguridad y valida que ningún dato fue modificado en tránsito. |
Packet Privacy | Chequea la seguridad y encripta el paquete. |
Finalmente tenemos "Impresonation level". Este es el seteo de niveles de autoridad que los componentes tienen con otros procesos. Estos son cuatro niveles diferentes como describe la tabla 2. Por defecto es Impersonate.
Tabla 2. Niveles de Impersonamiento
Nivel | Descripción |
---|---|
Anonymous | El segundo proceso no sabe nada acerca del cliente. |
Identify | El segundo proceso puede identificar quién es el cliente. |
Impersonate | El segundo proceso puede impersonar al cliente, pero sólo por procesos en el mismo servidor. |
Delegate | El segundo proceso puede impersonar al cliente en todas las instancias. |
Bien, hemos discutido sobre seguridad declarativa basada en roles, la cual es definida y manejada en tiempo de ejecución. También puede usar la seguridad programática. Esto le permite expandir su código basado en los niveles de acceso del usuario. Por ejemplo, un oficial de cuentas solo podría estar autorizado a prestar $50.000. Después que el gerente hace la aprobación para autorizar el préstamo.
Do case Case IsCallerInrole("Loan Officer") lnMaxLoanAmt = 50000 Case IsCallerInRole("Manager") LnMaxLoanAmt = 100000 OTherWise LnMaxLoanAmt = 10000000000000 EndCaseUtilizar ambos, seguridad basada en roles combinado con seguridad programática, puede soportar cualquier esquema de seguridad que usted necesite.
Manejo de errores
Una de las preguntas que con mas frecuencia de ve en los foros es, "Como mostrar un error de vuelta al usuario?". Si usted piensa sobre esto la respuesta no parece fácil. Usted NO puede mostrar un cuadro de diálogo con el error de su componente. Este está corriendo en una computadora distinta de la que contiene la interfaz de usuario. VFP posee la función COMRETURNERROR(), para enviar el mensaje de error de vuelta al cliente. COMRETURNERROR() usa dos parámetros. El primero es el nombre del módulo donde ocurre el error. El segundo es el mensaje que muestra al usuario. Veámoslo en el código.
Define Class ErrorExample as SESSION OLEPUBLIC Procedure Error(tnError, tcMetodo, tnLine) Local lcMensaje LcMensaje = "Este es mi mensaje de error") COMRETURNERROR(tcMethod, lcMensaje) EndProc Function CauseError Error 15 Return "Esto nunca debería ser mostrado" EndFunc EnddefineAhora compile el código en una DLL e instancie lo siguiente.
Ox = CREATEOBJECT("MyError.ErrorExample") ? ox.CauseError()Usted debe esperar "Esto nunca debería ser mostrado" para mostrar en el escritorio de VFP. Sin embargo, aparece un cuadro de dialogo con el mensaje de error.
>Puede retornar cualquier información que quiera en el parámetro del mensaje. Podría querer mejorar el método de error al capturar información adicional utilizando la función AERROR( ) o escribiendo información en el log de errores. Esta es una advertencia: El método de error no se disparará si el error ocurre en el método Init.
Conclusión
Hemos cubierto instalación, seguridad y manejo de errores. En la próxima oportunidad discutiremos transacciones y veremos como COM+ y VFP 7 permite incluir datos de VFP en las transacciones, algo que no se podía hacer con MTS y VFP 6.
Craig Berntson es Microsoft Certified Solution Developer y fue nombrado cuatro veces Microsoft Most Valuable Profesional. Disertante de varias conferencias de FoxPro en todo EEUU y eventos de Microsoft en Salt Lake. Es Presidente del Salt Lake City Fox Users Group y actualmente ingeniero de software senior para 3m Health Information Systems. |