26 de julio de 2009

Usando formatos de archivos de Excel 2007 en VFP 9.0

Artículo original: Using Excel 2007 File Formats in VFP 9.0
http://www.sweetpotatosoftware.com/SPSBlog/PermaLink,guid,6b9d4c6f-76bb-4444-8d5b-9e321c605534.aspx
Autor: Craig Boyd
Traducido por: Luis María Guayán

Los Problemas

Los comandos APPEND FROM y COPY TO de Visual FoxPro 9.0 son incompatibles con los nuevos formatos de archivo de Excel 2007 (XLS, XLSX, xlsb, y XLSM). Habrá notado que incluí XLS y usted cree que es soportado, pero sólo intente guardar su libro XLS Excel 97-2003 en el modo de compatibilidad de Excel 2007 y, a continuación, utilice el comando APPEND FROM sobre éste. VFP se quejará de la validez del archivo, en resumen, no funciona. Y, el comando COPY TO esta simplemente atascado en los oscuros años del XLS y XL5 disponibles. Bien, entonces todo esto es el primer problema.

El segundo problema con los comandos APPEND FROM y COPY TO es que no permiten especificar las columnas o los campos; una cláusula WHERE o FOR; o especificar un rango o área de impresión. Estamos bastante atascados con especificar un libro, APPEND FROM permite un parámetro opcional para la Hoja, pero eso no es adecuado ni super atractivo.

El tercer problema es que la solución habitualmente sugerida para los problemas de arriba, es utilizar automatización, y la automatización es super lenta debido al consumo efectuado por COM. Esto también introduce una dependencia adicional de tener instalado Excel 2007 en la máquina en la que el código se ejecuta. Lo "lento" nunca es aceptable (las aplicaciones VFP puede tener sus cosas, pero la lentitud no es una de ellas) y la dependencia adicional, aceptable en algunos casos, es un freno si se están ejecutando estos tipos de operaciones sobre un servidor web que no tiene instalado Excel 2007.

La Solución

Yo estaba trabajando en una solución para un cliente mío, que requería que consuma y cree archivos XLS, XLSX, xLSB y XLSM, sin que el usuario necesite tener instalado Excel 2007. Después de lanzar unas pruebas conjuntamente, yo incluí la ayuda de mi amigo Bo Durban. El código proporcionado en un archivo .ZIP al final de este artículo, es lo que surgió después de un par de intensas sesiones de codificación. Un agradecimiento especial a mi cliente por permitir compartirlo.

El Código

El código incluye 2 funciones principales: AppendFromExcel() y CopyToExcel(). También hay 4 funciones de ayuda: AWorkSheets(), AWorkSheetColumns(), CreateExcelTemplate(), y EmptyFieldToNull() que le puede resultar útil o interesante. El código:
  • No requiere de Office 2007
  • Pueden añadir de formatos de archivo xls, xlsx, xlsm, y xlsb
  • Puede crear y copiar a los formatos de archivo xls, xlsx, xlsm, y xlsb
  • Soporta todas las tablas de Excel (hojas de trabajo, rangos, áreas de impresión)
  • Permite que las columnas de la hoja de cálculo y los campos de las tablas puedan ser especificados
  • Permite que la cabecera de la fila en la hoja de trabajo pueda contener espacios en los nombres de las columnas, encerrándolos entre corchetes, por ejemplo [Mi Columna # 1]
  • Proporciona soporte para expresiones con cláusulas WHERE de SQL o cláusula FOR de VFP
  • Es super rápido
  • Probablemente hace algunas otras cosas que no estoy pensando ahora :)
De todas formas, descargue y descomprima el PRG, descomente y/o modifique el código del ejemplo de uso de la parte superior del PRG a su gusto, y diviértase!

Originalmente incluí el código en la parte inferior de este artículo, pero el código hacía locuras con el ancho de este blog, porque contiene algunas líneas muy largas con binarios. Por lo tanto, aquí está el vínculo para la descarga del archivo PRG que contiene las cosas que necesita:

Nota: Adicionalmente una reestructuración del código (refactoring) es necesaria, y definitivamente esto clama por una clase que se deba construir, pero el código funciona completamente como está.
Si tienen algún problema ejecutando el código (especialmente si aparece el diálogo de Seleccionar un origen de datos) es posible que deba comprobar el proveedor OLEDB y observar las alternativas proporcionadas en el código.

11 de julio de 2009

¿Es una variable o propiedad un array?

Entrada original: Is variable or property an array?
http://www.berezniker.com/content/pages/visual-foxpro/-varable-or-property-array
Autor: Sergey Berezniker
Traductor: Luis María Guayán

Cuando validamos un parámetro en una función o procedimiento, a veces es necesario determinar si el parámetro pasado es una matriz (array). Hasta VFP 9.0 no había una manera evidente de hacerlo.

En VFP 9.0 la función TYPE() acepta un segundo parámetro adicional: 1, para determinar si se la expresión pasada es una matriz, una colección o ninguno de los dos. En versiones anteriores de VFP podemos usar la función TYPE() en combinación con la función ALEN().

Este es el código de ejemplo:

&& VFP 9.0
IF TYPE("AlgunaVariableOrPropiedad",1) = "A"
  && Es un array
ENDIF
 
&& VFP 8.0 y anteriores
IF TYPE("ALEN(AlgunaVariableOrPropiedad)")) = "N"
  && Es un array
ENDIF

Comprobar que TYPE("AlgunaVariableOrPropiedad[1]") = "N" no funciona para propiedades intrínsecas de VFP. Retornando "N" para estas.

loForm = CreateObject ( "Form") 
? TYPE("loForm.Top[1]") && Retorna N

9 de julio de 2009

Como generar el numero de nuestros comprobantes sin morir en el intento

Artículo de Maximiliano Accotto (Buenos Aires, Argentina) publicado en su Blog.

Como generar el numero de nuestros comprobantes sin morir en el intento

Maxi AccottoEn muchas de nuestras aplicaciones (por no decir en casi todas) nos vemos en la necesidad de generar números auto incrementales, por ejemplo para la numeración de facturas, remitos, órdenes de compra u otro tipo de documentos.

La solución a esta tarea se puede hacer de distintas maneras. La idea de este artículo es mostrar en SQL Server cómo podemos resolver este problema, y poder analizar sus pros y contras...

Para leer el artículo completo haz click Aquí

5 de julio de 2009

Conocer si tu aplicación ya está en ejecución

He visto algunos ejemplos usando la API de Windows para determinar si tu aplicación ya está en ejecución. Muchos de esos ejemplos utilizan el "caption" de una ventana y con ello puedes tomar acciones en cuanto a permitir la ejecución simultánea de varias instancias o no.

En lugar de permitir varias instancias, mejor permítele hacer varias tareas a la vez dentro de la aplicación.

El ejemplo que les expongo hace uso de WMI de Windows y es bastante sencillo.

En esencia es una consulta sql que nos devuelve los nombres de los procesos que están en ejecución. He dejado los comentarios para explicar que en mi caso muy particular, yo pregunto si la cantidad de procesos es mayor que o igual a 3 (y usando un like en el sql) porque hago uso de dos programas: uno que será el "launcher" el cual buscará el ejecutable compilado más reciente y éste será el que se ejecute (Así no tengo que interrumpir las labores de otros usuarios cuando tenga que hacer alguna correción en otras pantallas)

FUNCTION MyProcessName  
 *.* Determina si el programa de recibo bodega ya está en ejecución.
 *.* De ser así, devuelve un valor lógico que será usado para terminar o continuar con el programa.
 *-* Se debe tomar en cuenta que ReciboBodega es ejecutado usando dos programas: 
 *-* recibo_bodega.exe y recibo_bodegannn.exe
 LOCAL lcComputer as String

 lcComputer = '.'
 loWMIService = Getobject('winmgmts:'+'{impersonationLevel=impersonate}!\\' + lcComputer + '\root\cimv2')
 colProcessList = loWMIService.ExecQuery('Select * from Win32_Process where name like "recibo_bodega%"')
 IF colProcessList.count>=3
  RETURN .T.
 ELSE
  RETURN .F. 
 ENDIF 
ENDFUNC 

Pero en su caso puede preguntar directamente por el nombre de su ejecutable y cuando "count" sea igual a uno, su función regresará un valor Cierto.

Por último, en el su prg principal, en sus primeras líneas deberás poner:

IF MyProcessName()
   RETURN .F.
ENDIF  

Saludos,

Jorge Luis Vejerano, Panamá

17 de mayo de 2009

Ampliar la vista de un formulario

Después de activar la casilla correspondiente, la parte del formulario bajo el cursor del mouse, se re dibuja en la ventana principal de foxpro como visto con una lupa.


*-- Aquí el código:
LOCAL oForm
oForm = CREATEOBJECT("Tform")
oForm.SHOW(1)

DEFINE CLASS Tform AS FORM
  WIDTH = 350
  HEIGHT = 160
  BORDERSTYLE = 2
  MAXBUTTON = .F.
  MINBUTTON = .F.
  AUTOCENTER = .T.
  CAPTION = "Ampliar vista"
  hForm = 0
  hDC = 0

  ADD OBJECT chMagnify AS CHECKBOX WITH VALUE=.F.,;
    LEFT=20, TOP=20, AUTOSIZE=.T., CAPTION="Activar"

  ADD OBJECT chInvert AS CHECKBOX WITH VALUE=.F.,;
    LEFT=20, TOP=56, AUTOSIZE=.T., CAPTION="Invertir colores"

  ADD OBJECT lbl1 AS LABEL WITH;
    LEFT=190, TOP=20, AUTOSIZE=.T., CAPTION="Escala:"

  ADD OBJECT cmbScale AS COMBOBOX WITH STYLE=2,;
    LEFT=240, TOP=20, WIDTH=70, HEIGHT=21

  ADD OBJECT cmdClose AS COMMANDBUTTON WITH CANCEL=.T.,;
    LEFT=140, TOP=112, WIDTH=70, HEIGHT=27, CAPTION="Cerrar"

  PROCEDURE INIT
    THIS.DECLARE

  PROCEDURE ACTIVATE
    IF THIS.hForm = 0
      THIS.hForm = GetFocus()
      THIS.hDC = GetDC(THIS.hForm)
    ENDIF

  PROCEDURE DESTROY
    IF THIS.hDC <> 0
      = ReleaseDC(THIS.hForm, THIS.hDC)
    ENDIF

  PROCEDURE cmdClose.CLICK
    THISFORM.RELEASE

  PROCEDURE cmbScale.INIT
    WITH THIS
      .ADDITEM("Normal")
      .ADDITEM("x 2")
      .ADDITEM("x 3")
      .ADDITEM("x 4")
      .LISTINDEX=3
    ENDWITH

  PROCEDURE MOUSEMOVE
    LPARAMETERS nButton, nShift, nXCoord, nYCoord
    THIS.Magnify

  PROCEDURE chMagnify.MOUSEMOVE
    LPARAMETERS nButton, nShift, nXCoord, nYCoord
    THISFORM.Magnify

  PROCEDURE chInvert.MOUSEMOVE
    LPARAMETERS nButton, nShift, nXCoord, nYCoord
    THISFORM.Magnify

  PROCEDURE lbl1.MOUSEMOVE
    LPARAMETERS nButton, nShift, nXCoord, nYCoord
    THISFORM.Magnify

  PROCEDURE cmbScale.MOUSEMOVE
    LPARAMETERS nButton, nShift, nXCoord, nYCoord
    THISFORM.Magnify

  PROCEDURE cmdClose.MOUSEMOVE
    LPARAMETERS nButton, nShift, nXCoord, nYCoord
    THISFORM.Magnify

  PROCEDURE Magnify
    #DEFINE SRCCOPY 0xCC0020
    #DEFINE NOTSRCCOPY 0x00330008
    #DEFINE cnDstWidth 600
    #DEFINE cnDstHeight 300

    IF THIS.chMagnify.VALUE
      LOCAL cBuffer, nX, nY, hDstWin, hDstDC,;
        nMode, nSrcWidth, nSrcHeight, nScale

      hDstWin = GetActiveWindow()
      hDstDC = GetWindowDC(hDstWin)

      cBuffer = REPLICATE(CHR(0), 8)
      = GetCursorPos(@cBuffer)
      = ScreenToClient(THIS.hForm, @cBuffer)

      nX = buf2dword(SUBSTR(cBuffer, 1,4))
      nY = buf2dword(SUBSTR(cBuffer, 5,4))

      nScale = THIS.cmbScale.LISTINDEX
      nSrcWidth = INT(cnDstWidth/nScale)
      nSrcHeight = INT(cnDstHeight/nScale)

      nMode = IIF(THIS.chInvert.VALUE,;
        NOTSRCCOPY, SRCCOPY)

      = StretchBlt(hDstDC, 10, 100,;
        cnDstWidth, cnDstHeight, THIS.hDC,;
        nX-nSrcWidth/2, nY-nSrcHeight/2,;
        nSrcWidth, nSrcHeight, nMode)

      = ReleaseDC(hDstWin, hDstDC)
    ENDIF

  PROCEDURE DECLARE
    DECLARE INTEGER GetActiveWindow IN user32
    DECLARE INTEGER GetCursorPos IN user32 STRING @lpPoint
    DECLARE INTEGER GetWindowDC IN user32 INTEGER HWND
    DECLARE INTEGER GetDC IN user32 INTEGER HWND
    DECLARE INTEGER GetFocus IN user32

    DECLARE INTEGER ReleaseDC IN user32;
      INTEGER hWindow, INTEGER hdc

    DECLARE INTEGER ScreenToClient IN user32;
      INTEGER hWindow, STRING @lpPoint

    DECLARE INTEGER StretchBlt IN gdi32;
      INTEGER hdcDest, INTEGER nXOriginDest,;
      INTEGER nYOriginDest, INTEGER nWidthDest,;
      INTEGER nHeightDest, INTEGER hdcSrc,;
      INTEGER nXOriginSrc, INTEGER nYOriginSrc,;
      INTEGER nWidthSrc, INTEGER nHeightSrc,;
      INTEGER dwRop

ENDDEFINE

FUNCTION buf2dword(lcBuffer)
  RETURN ASC(SUBSTR(lcBuffer, 1,1)) + ;
    BITLSHIFT(ASC(SUBSTR(lcBuffer, 2,1)), 8) +;
    BITLSHIFT(ASC(SUBSTR(lcBuffer, 3,1)), 16) +;
    BITLSHIFT(ASC(SUBSTR(lcBuffer, 4,1)), 24)

Saludos.

Jesus Caro V