26 de mayo de 2008

Redondear a X centavos hacia arriba o hacia abajo

Estas funciones redondean a X centavos para arriba o para abajo según se necesite.
lnNro = 12345.67

? RedondearMasAx(lnNro, 5)
? RedondearMenosAx(lnNro, 5)
?
? RedondearMasAx(lnNro, 25)
? RedondearMenosAx(lnNro, 25)
?
? RedondearMasAx(lnNro, 50)
? RedondearMenosAx(lnNro, 50)

FUNCTION RedondearMenosAx(tnVal, tnDec)
  RETURN Floor(tnVal * 100/tnDec) / (100/tnDec)
ENDFUNC

FUNCTION RedondearMasAx(tnVal, tnDec)
  RETURN CEILING(tnVal * 100/tnDec) / (100/tnDec)
ENDFUNC
Cada aplicación tiene sus propias necesidades de redondeo, diferentes a la función nativa ROUND() de Visual FoxPro. Es por ello que en la comunidad siempre se consulta sobre este tema, y en PortalFox existen las siguientes rutinas que cubren la mayoría de las necesidades:

-- Redondear a 5 centavos por arriba --
http://comunidadvfp.blogspot.com/2004/07/redondear-5-centavos-por-arriba.html

-- Redondear hacia arriba o hacia abajo --
http://comunidadvfp.blogspot.com/2007/06/redondear-hacia-arriba-o-hacia-abajo.html

-- Truncar un número --
http://comunidadvfp.blogspot.com/2004/08/truncar-un-numero.html

Luis María Guayán

23 de mayo de 2008

Haciendo aplicaciones portables en VFP - Parte 3 de 3

Autor: Sergio Hugo Sanchez

Y por último, llegamos a Windows Vista y sus consecuencias de automatizar el proceso de instalación de los controles ActiveX. Despues de probarlo en un sistema Vista, el programa seguía sin funcionar; asi que me dedique a buscar en Internet todo lo que pude relacionado al tema de instalación. Para resumir, he aquí 3 puntos:

1) Instalacion de los controles desactivado la UAC de Windows Vista mediante el panel de control. (Difícil).

2) Instalación de los controles usando archivos Manifest (como indica su blog Calvin Hsia (Medio)

3) Instalación de los controles con derechos de administrador (Facilísmo tan a la vista que no me lo creo!).

Bien. La cuestión es que Windows Vista y su seguridad no permiten el registro automático. El primer punto, seria necesario que la PC donde trabajemos tuviera desactivado esto, pero lo menciono como difícil porque yo daré mi aplicación a un usuario y no creo que el quiera esta apagando el UAC si se encuentra con una maquina Vista.

El segundo método, me llevo a leer en el blog de Calvin su método para crear archivos Manifest que se pegan al EXE para que se autoregistren los controles. Ademas, en la propia ayuda de DBITech, informan sobre lo que se tiene que hacer para registrar sus controles. En este explica como se debe ejecutar el programa REGSVR32 como administrador y en otro tema como hacer los archivos MANIFIEST para los controles DBI.

Utilizando las herramientas de Markus Winhard quien creo Manifest Tools, pude crear mi archivo Manifest y anexarlo a mi .EXE.

La idea es crear un archivo aplicacion.exe.manifest (xml) con las instrucciones de registro. Luego, "agregar" este archivo al ejecutable.

En la aplicacion ejemplo que utilice uso dos controles DBI: ctDropDate que es un control para desplegar un calendario y poner fechas y el ctToolBar que es una barra de herramientas con iconos.
<?xml version="1.0" encoding="utf-8"?>
<assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0"
 xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"
 xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns="urn:schemas-microsoft-com:asm.v1"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance“>
<assemblyIdentity 
        name="monitortc2.exe" 
        version="1.0.0.0" 
        type="win32" 
/>
<file name="ctDropDate.ocx" asmv2:size="139264">
    <typelib tlbid="{29F50288-212F-11D5-A85D-0080C8DFC881}" 
        version="2.0" 
        helpdir="" 
        resourceid="0" 
        flags="HASDISKIMAGE"  
     />
    <comClass clsid="{29F50288-212F-11D5-A85D-0080C8DFC881}" 
        threadingModel="Apartment" 
        tlbid="{29F50288-212F-11D5-A85D-0080C8DFC881}" 
        progid="ctDropDate.ctDropDateCtrl.2" 
        description="ctDropDate Control" 
     />
</file>

<file name="ctToolbar.ocx" asmv2:size="167936">
    <typelib tlbid="{83A59732-3096-4711-A41B-8CF8FF98326E}" 
        version="3.0" 
        helpdir="" 
        resourceid="0" 
        flags="HASDISKIMAGE"  
     />
    <comClass clsid="{43A5A355-2366-438A-AA33-1C30DA9E3686}" 
        threadingModel="Apartment" 
        tlbid="{83A59732-3096-4711-A41B-8CF8FF98326E}" 
        progid="ctTOOLBAR.ctToolBarCtrl.3" 
        description="ctToolBar Control 3" 
     />
</file>

</assembly>
Como se puede apreciar, tengo 2 controles que registrar para este ejecutable. Los datos de typelib y Class ID fueron tomados usando la herramienta OLEVIEW (que esta en Microsoft pero tambien en Download.com).

Luego, usando la herramienta de Importar Manifest al EXE como mencione antes, queda listo mi ejecutable. Lo probamos y … wala! Registrado.

Ahora bien, como tercer punto, creo que es mucho mas sencillo el ultimo. Porque sin crear ningún Manifest, cuando ponemos nuestro USB y le damos a ejecutar nuestro programa, en lugar de usar el doble click con el botón izquierdo damos click al botón derecho. En el menú, le damos click en EJECUTAR COMO ADMINISTRADOR. Y listo! El programa registra los controles igual que en XP sin crear ningun archivo ni paso extra (porque, oiga usted, esta medio engorroso andar buscando los archivos OCX con sus clases y demás del registro para hacer el dichoso archivo XML).

Si le digo a mi usuario, dale doble click para usar el programa desde el usb portable y si ves que usas Vista, dale un click derecho y ejecútalo como administrador, pues hasta aquí llegamos.

Estoy buscando crear una rutina para identificar el OS que se trate y lanzar un mensaje de advertencia. O simplemente dejar el manifest y listo. Dependerá del tiempo que tenga en preparar mi aplicación para ser portable.

RESUMEN

Hasta aquí, creo que hacer portable una aplicación VFP no lleva mas que copiar todos los archivos necesarios a una carpeta junto con su RUNTIME. En el caso de controles extra, implica el problema de registrarlos debidamente. No probé esto con versiones anteriores de Windows y por supuesto en otros ambientes.

Otra desventaja es que la aplicación portable siempre estara del orden de 5 a 10MB. Por que? Porque los runtimes casi ocupan unos 5MB (o un poquito mas) mas el EXE y los archivos necesarios (ico, bmp, log, mem, ini, etc, etc).

La ultima cosa es trabajar con los datos. Tener nuestras rutinas claras para la conexión con los datos, ya sea de manera local (sistemas sencillos) o de manera remota (para LAN, WAN o internet).

Seguiré probando este método para ver que tan bien trabaja. Deseeme suerte.

21 de mayo de 2008

Haciendo aplicaciones portables en VFP - Parte 2 de 3

Autor: Sergio Hugo Sanchez

Continuando con lo de la creacion de sistemas portables, como llevo tiempo que utilizo algunos de los controles DBI-Tech, pues si que es un problema. (Malo malo, al no usar VFP puro verdad?). Bueno, ni modo.

Y aqui viene la parte dificil. Determinar si el control esta registrado, registrarlo en su caso y al finalizar desregistrarlo. Asi que he creado una pequena rutina:
*
* CHECKOCX.PRG
* Programa para checar si estan registrados los controles OCX necesarios
* Junio, 22, 2004.
*
*
* Necesitamos tener una variable en alguna parte para saber si ya esta registrado o no…
* MOD: 14,Mayo,2008… Agregamos una funcion para detectar primero si el OCX esta registrado.
*       En caso de no estarlo debemos hacerlo.
*
* Checando si los controles ActiveX necesarios para el sistema estan registrados
*
* Listado con todos los con todos los controles OCX necesarios. 
* Poner en el array tantos OCX como necesite el sistema. Este numero debera ser pasado
* mas adelante para el proceso.
*
* El array necesita 2 dimensiones o columnas, ya que la segunda correspondera al 
* archivo fisico OCX y en donde se encuentra si es que esta en una subcarpeta.
* Ejs.
*  lstOCX(1,1) = "ctdropdate.ctdropdatectrl.2"
*  lstOCX(1,2) = "ctdropdate.ocx"
*   o
*  lstOCX(1,2) = "OCX\ctdropdate.ocx"
*
* Tambien usaremos una variable de parametro para hacer 1 de 3 cosas:
* 1) Check = Checar si el control esta registrado o no y registrarlo en su caso
* 2) RegALL = Registrar el control aunque ya estuviera registrado
* 3) UnRegALL = Desregistrar el control.
* 
* La razon de estas opciones es que para aplicaciones portables necesitaremos desregistrar el control
* para que no quede huella en el sistema Host.
*
PARAMETERS cOpcion
IF PARAMETERS() = 0
 RETURN
ENDIF
DIMENSION lstOCX(2,2)
lstOCX(1,1) = "ctdropdate.ctdropdatectrl.2"
lstOCX(1,2) = "ctdropdate.ocx"
lstOCX(2,1) = "cttoolbar.cttoolbarctrl.3"
lstOCX(2,2) = "cttoolbar.ocx"

* Hacer un ciclo con el ultimo numero que corresponde a la cantidad de OCX necesarios
* No calculamos porque nosotros le damos la cantidad
FOR i = 1 TO 2
 DO CASE
  CASE cOpcion = "Check" 
   WAIT WINDOW "Checando controles"
   lCheck = OcxRegistrado( lstOCX( i, 1 ) ) && Llamamos la funcion que checa si esta registrado o no: (.T./.F.)
   IF lCheck = .F.
    * Si el control necesario no esta registrado en la PC, registremoslo
    * Debemos pasarle a esta funcion el archivo OCX a registrar
    lRegistrado = OCXCmdReg( lstOCX( i, 2) )
    *  ? "Archivo " + lstOCX( i,2) +" Ya NO ESTABA registrado. Pero ahora si lo esta"
   ELSE
    *  ? "Archivo " + lstOCX( i,2)+ " Ya estaba registrado"
   ENDIF 
  CASE cOpcion = "RegALL"
   lRegistrado = OCXCmdReg( lstOCX( i, 2) )
  CASE cOpcion = "UnRegALL" 
   WAIT WINDOW "Desregistrando controles" 
   lRegistrado = OCXCmdUnReg( lstOCX( i, 2) )

 ENDCASE
NEXT
RETURN

* Funcion Como saber si un activex ya fue registrado
* JCVisual. 19/Dic/2007
*
* http://www.portalfox.com/article.php?sid=2543
*
FUNCTION OcxRegistrado(cClase)
    Declare Integer RegOpenKey In Win32API ;
        Integer nHKey, String @cSubKey, Integer @nResult
    Declare Integer RegCloseKey In Win32API ;
        Integer nHKey
    nPos = 0
    lEsta = RegOpenKey(-2147483648, cClase, @nPos) = 0
 
    If lEsta
        RegCloseKey(nPos)
    Endif

    Return lEsta
Endfunc

* Funcion: Registrar y Desregistrar un OCX
* Jorge Mota. 16/Agosto/2002
*
* http://www.portalfox.com/article.php?sid=506
*
FUNCTION OCXRegistrar(cActiveX)
DECLARE INTEGER DLLSelfRegister IN "vb6stkit.DLL” STRING lpDllName
* Debemos de poner una lista de los controles
lcFileOCX = SYS(5) + CURDIR() + cActiveX
*lcFileOCX = "C:\DBITECH\Toolbox6\DBITech\Component Toolbox 6.0\Components\ctlist.ocx”
liRet = DLLSelfRegister( lcFileOCX )
IF liRet = 0
 SelfRegisterDLL = .T.
* MESSAGEBOX( "Registrado OCX” )
ELSE
 SelfRegisterDLL = .F.
 MESSAGEBOX( "Error - No registrado OCX” )
ENDIF
ENDFUNC


* Funcion: Registrar OCX con comando
* 
* JCVisual. 19/Dic/2007
*
* http://www.portalfox.com/article.php?sid=2543
*
FUNCTION OCXCmdReg(cActiveX)
 cRun=”REGSVR32 /s ” + cActiveX
 RUN /N &cRun
ENDFUNC

* Funcion: Des-registrar OCX con comando cuando termine
FUNCTION OCXCmdUnReg(cActiveX)
 cRun=”REGSVR32 /u /s ” + cActiveX
 RUN /N &cRun
ENDFUNC
Como pueden observar he usado una función publicada en PortalFox para checar si el control esta registrado en el sistema. Si no, registrarlo.

Cabe notar que la funcion de Jorge Mota no funciona. Busque en Internet otra sin exito por lo que segui el metodo tradicional de REGSVR32. En la pagina http://support.microsoft.com/kb/146219/es de Microsoft, incluso indican que se puede distribuir este archivo con nuestra aplicacion, asi que lo pondre en mi directorio.

Como nota, aclaro que el programa tiene un listado de mis controles que utilizo en un arreglo bidimensional. En la primer columna esta la referencia al registro que se obtiene de la forma y la propiedad OleClass. La segunda columna corresponde al archivo fisico OCX.

Ahora, desde mi programa principal, antes de iniciar el programa mando llamar la rutina:
*
* Checar si los controles OCX necesarios para el sistema se encuentran
* registrados, en caso contrario deben registrarse. Es util para sistemas portables
* o PCs donde no estan instaladas todas las librerias necesarias.
*
WAIT WINDOW "Checando OCX"
DO CheckOCX WITH "Check"
*_Screen.Visible = .f.
DO FORM tc_consulta
READ EVENTS
* Al terminar no olvidar desregistrar si es portable
WAIT WINDOW "Si es portable todos fuera"
DO CheckOCX WITH "UnRegALL"
Y listo. Ahora a ejecutar el sistema desde un USB Disk. Funcionara? Vamos a ver…
Pues si. Funciona. Con WindowsXP. Compile mi programa, lo pase a mi USB y lo ejecute en una maquina que no tiene nada de activeX y VFP. El programa detecta que los controles no estan registrados y procede a hacerlo. De momento como demo, he dejado unas cuantas lineas visibles y wait windows para ver que estaba haciendo. Despues, el programa se ejecuto sin problemas. Al cerrarlo, desregistro los controles y aqui no paso nada.

Ahora viene otro problema. El registro en Windows Vista. Eso lo veremos en el siguiente tema.

Se aceptan comentarios.

19 de mayo de 2008

Haciendo aplicaciones portables en VFP - Parte 1 de 3

Autor: Sergio Hugo Sanchez

Hoy en dia parece que vuelve una fiebre por las aplicaciones portables. Como si fuera la gran novedad de estos tiempos. Aun recuerdo como si fuera ayer cuando TODAS las aplicaciones eran portables.

Asi es, antes de Windows, en el mundo de MSDOS todas las aplicaciones eran portables. Creabas el directorio (entonces no se conocia el concepto de "carpeta"). copiabas los archivos y voila!. Ya esta. Nada de instalar en la carpeta de archivos de programas, o de windows o de system32, o del registro. En fin, eran otros tiempos.

Ahora me ha surgido un proyecto en el cual tengo que hacer una de tres cosas:

1) Instalar la aplicacion en un servidor y ejecutarla desde ahi a ciertos usuarios para que esten disponibles atraves de una WAN.

2) Instalar la aplicacion en cada computadora de la WAN con acceso a los datos del servidor (cliente/servidor), crear unas cuentas de usuario para que solo los autorizados utilicen dicha aplicacion.

3) Hacer una aplicacion portable que se ejecute en un disco USB de tal forma que solo los usuarios autorizados lleven su llave y la ejecuten en cualquier computadora de la WAN. Ojo, los datos no estaran en el USB, sino que quiza residan en el servidor de la empresa.

Pues bien. Se podra?

La cuestion es ... seguridad. Las personas autorizadas podran desplazarse a cualquier punto de la WAN y usar alguna de las PC para conectarse a la base de datos central. Sin embargo, dicha aplicacion no deberia residir en la PC local. Aunque se creen cuentas de usuario y se apliquen restricciones de uso, o se escondan las carpetas, o se use un programa con contrasena de carpetas o aplicaciones, la idea es que simplemente no aparezca.

Que tal si hay un robo, o un accidente, o desastre y la PC de alguna forma se ve afectada? Los datos no deben residir en ella, y de preferencia tampoco la aplicacion.

COMO CREAR UNA APLICACION PORTABLE EN VFP.

Se que los gurus y viejos programadores de VFP saben esto desde hace años, ya que Fox siempre ha sido portable desde DOS. Bastaba con poner los runtimes en la misma carpeta y ya.

Paso 1.

Para comenzar, he copiado todos los archivos del sistema/programa a una carpeta del USB. DBFs locales, archivos ini, iconos, ejecutables, etc.

Paso 2.

En segundo lugar, he copiado los archivos runtime que indica la ayuda de Visual Fox. A su saber:
vfp9r.dll
vfp9renu.dll
msvcr71.dll
Hasta aqui va funcionando. Ahora, como mi aplicacion utiliza un par de archivos OCX, tambien debemos incluirlos. Estos se copian a la carpeta raiz del mismo sistema. Todo junto.

Listo. Si su aplicacion es VFP puro sin librerias extras. Creo que hasta aqui esta resuelto el problema.

El problema vendra si tiene archivos OCX o DLL que registrar, asi como si va a trabajar con Windows Vista y su sistema tiene que usar estos registros.

No estoy hablando de la parte de los datos. Los datos pueden residir incluso en el servidor o en la misma carpeta donde esta el sistema. Eso es otro tema.

Cuando tiene controles ActiveX a registrar necesitamos hacer una pequena rutina que podamos ejecutar para que se autoregistren. Eso lo veremos en el proximo tema.

12 de mayo de 2008

Buscar el nombre de un campo en todas las tablas de una DBC

Estas dos maneras de buscar el nombre de un campo en todas las tablas de una base de datos, son respuestas a una pregunta de un usuario del foro de UT, enviadas por por Sergey Berezniker y Luis María Guayán respectivamente.

*** 1 ***
lcDBC = HOME(2) + "NORTHWIND\NORTHWIND.DBC"
USE (lcDbc) ALIAS MyDbc
*-- Campo a buscar
lcFieldName = "City"
SELECT LEFT(db1.ObjectName,50) AS TableName, LEFT(db2.ObjectName,50) AS FieldName ;
  FROM mydbc db1 JOIN mydbc db2 ON db1.ObjectId = db2.ParentId ;
  WHERE db1.ObjectType = "Table" ;
  AND db2.ObjectType = "Field" AND db2.ObjectName = LOWER(lcFieldName) 
USE IN SELECT("MyDbc")

*** 2 ***
lcDBC = HOME(2) + "NORTHWIND\NORTHWIND.DBC"
OPEN DATABASE (lcDbc)
lnTables = ADBOBJECTS(laTables, "TABLE")
*-- Campo a buscar
lcFieldName = "City"
FOR lnI = 1 TO lnTables
  USE (ADDBS(JUSTPATH(lcDBC)) + laTables(lnI)) ALIAS "Tmp"
  lnFields = AFIELDS(laFields, "Tmp")
  IF ASCAN(laFields,lcFieldName,1,ALEN(laFields,1),1,9) > 0
    ? DBF("Tmp")
  ENDIF
  USE IN SELECT("Tmp")
ENDFOR
CLOSE DATABASES

2 de mayo de 2008

Comprobar, Iniciar y Parar Servicios desde VFP

Con estas funciones podemos comprobar si un Servicio se está ejecutando en Windows, y podemos Iniciarlo o Pararlo si disponemos de los permisos adecuados.

Tomamos como ejemplo el servicio Themes de Windows XP.

1. Consultar si el servicio Themes se esta ejecutando:
oShell = CREATEOBJECT("Shell.Application") 
? oShell.IsServiceRunning("Themes") 
oShell = Null
2. Consultar si se puede iniciar o detener el servicio Themes:
oShell = CREATEOBJECT("Shell.Application") 
? oShell.CanStartStopService("Themes")
oShell = Null
3. Iniciar el servicio Themes:
oShell = CREATEOBJECT("Shell.Application") 
? oShell.ServiceStart("Themes", .F.) 
oShell = Null
4. Detener el servicio Themes:
oShell = CREATEOBJECT("Shell.Application") 
? oShell.ServiceStop("Themes", .T.) 
oShell = Null

Luis María Guayán