12 de abril de 2018

SQLXMLBulkLoad funciona

Artículo original: SQLXMLBulkLoad Rocks!
http://doughennig.blogspot.com/2006/07/sqlxmlbulkload-rocks.html
Autor: Doug Hennig
Traducido por: Ana María Bisbé York


Por razones que serán obvias en septiembre, he estado trabajando últimamente en actualizar los datos a SQL Server 2005. Existen varios mecanismos que se pueden utilizar para hacerlo; pero todos tienen sus pequeños inconvenientes bajo ciertas condiciones. Por ejemplo, es lento al utilizar una serie de instrucciones INSERT, mientras que insertar un volumen grande no trabaja con campos memo. Al buscar otra cosa en los "SQL Server Books Online", pasé por un tema sobre carga masiva a XML. Luego de jugar un poco, parecía fácil de hacer, hice algunas pruebas en un archivo relativamente grande (365,741 registros) que contiene archivos memo (lo que significa que no podía usar INSERT masivo). Utilizando otros mecanismos, me tomó más de dos horas cargar los datos a una tabla SQL Server. Utilizando la carga masiva XML, me tomó 11 minutos. Eso es un 90 % de mejora en la velocidad. ¡ Me encanta hacer las cosas más rápido !

Utilizar la carga masiva XML es sencillo: instancie SQLXMLBulkLoad.SQLXMLBulkload.4.0, configure su propiedad ConnectionString a una cadena de conexión OLE DB, establezca adecuadamente algunas propiedades (por ejemplo, KeepNulls = .T. para insertar nulos en lugar de los valores predeterminados para los valores de las columnas), y llame a Execute, pasando el nombre del archivo de esquema y el nombre para el archivo XML. Execute lanza un error si hay algo mal con alguno de los archivos, y los errores al importar se guardan en un archivo cuyo nombre se almacena en la propiedad ErrorLogFile.

Hay algo un poco complejo al trabajar con datos VFP: mientras la función CURSORTOXML() puede crear el archivo de esquema, necesita ser afinado cuando se trabaja con carga masiva XML. Además, los campos DateTime tienen que ser señalizados con el atributo sql:datatype="dateTime" (esto último fue documentado, lo demás me costó solucionarlo tras algunas pruebas y errores).

He aquí el programa genérico que hace la carga desde un cursor VFP abierto a una tabla SQL abierta.

*==============================================================================
* Función: BulkXMLLoad
* Objetivo: Realizar una carga masiva de XML a SQL Server
* Autor: Doug Hennig
* Última revisión: 07/06/2006
* Parámetros: tcAlias - alias del cursor a exportar
* tcTable - nombre de la tabla hacia la que se importa
* tcDatabase - base de datos a la que pertenece la tabla
* tcServer - nombre del servidor SQL 
* tcUserName - nombre de usuario para la conexión (opcional:
* si no se especifica, se utiliza, la Seguridad Integrada de Windows 
* tcPassword - palabra clave para la conexión (opcional:
* si no se especifica, se utiliza, la Seguridad Integrada de Windows 
* Devuelve: cadena vacía si la carga masiva ha sido exitosa 
* o texto con un mensaje de error si ha fallado
* Entorno de entrada: debe estar abierto el alias especificada en tcAlias 
* deben existir la base de datos y tabla especificadas
* el servidor especificado debe estar accesible
* debe existir espacio de disco suficiente  para los archivos XML 
* Entorno de salida: si es devuelta una cadena vacía, los datos fueron 
* importados a la tabla especificada 
*==============================================================================
lparameters tcAlias, ;
  tcTable, ;
  tcDatabase, ;
  tcServer, ;
  tcUserName, ;
  tcPassword
  local lnSelect, ;
  lcSchema, ;
  lcData, ;
  lcReturn, ;
  loException as Exception, ;
  lcXSD, ;
  loBulkLoad
* Crea los archivos de dato y de esquema para el XML.
lnSelect = select()
select (tcAlias)
lcSchema = forceext(tcTable, 'xsd')
lcData = forceext(tcTable, 'xml')
try
  cursortoxml(alias(), lcData, 1, 512 + 8, 0, lcSchema)
  lcReturn = ''
catch to loException
  lcReturn = loException.Message
endtry

* Convierte el formato XSD en un formato aceptable por SQL Server.
* Añade el espacio de nombre SQL,
* convierte las etiquetas inicio y fin ,
* utiliza el atributo sql:datatype pra campos DateTime fields,
* y especifica la tabla importada con el atributo sql:relation.
if empty(lcReturn)
  lcXSD = filetostr(lcSchema)
  lcXSD = strtran(lcXSD, ':xml-msdata">', ;
    ':xml-msdata" xmlns:sql="urn:schemas-microsoft-com:mapping-schema">')
  lcXSD = strtran(lcXSD, 'IsDataSet="true">', ;
    'IsDataSet="true" sql:is-constant="1">')
  lcXSD = strtran(lcXSD, '<xsd:choice maxOccurs="unbounded">', ;
    '<xsd:sequence>')
  lcXSD = strtran(lcXSD, '</xsd:choice>', ;
    '</xsd:sequence>')
  lcXSD = strtran(lcXSD, 'type="xsd:dateTime"', ;
    'type="xsd:dateTime" sql:datatype="dateTime"')
  lcXSD = strtran(lcXSD, 'minOccurs="0"', ;
    'sql:relation="' + lower(tcTable) + '" minOccurs="0"')
  strtofile(lcXSD, lcSchema)

  * Instancia el objeto SQLXMLBulkLoad, configura su propiedad ConnectionString
  * y otras propiedades, y llama a Execute para realizar la importación masiva.
  try
    loBulkLoad = createobject('SQLXMLBulkLoad.SQLXMLBulkload.4.0')
    lcConnString = 'Provider=SQLOLEDB.1;Initial Catalog=' + tcDatabase + ;
      ';Data Source=' + tcServer + ';Persist Security Info=False;'
    if empty(tcUserName)
      lcConnString = lcConnString + 'Integrated Security=SSPI'
    else
      lcConnString = lcConnString + 'User ID=' + tcUserName + ;
        ';Password=' + tcPassword
    endif empty(tcUserName)
    loBulkLoad.ConnectionString = lcConnString
    *** Puede configurar la propiedad ErrorLogFile con el nombre 
    *** del archivo para escribir errores de importación
    loBulkLoad.KeepNulls = .T.
    loBulkLoad.Execute(lcSchema, lcData)
    lcReturn = ''
  catch to loException
    lcReturn = loException.Message
  endtry
  * Código de limpieza y cierre.
  erase (lcSchema)
  erase (lcData)
endif empty(lcReturn)
select (lnSelect)
return lcReturn

8 de abril de 2018

Seguimiento de los archivos gráficos de su aplicación

Artículo original: Keep track of your applications graphic files
http://www.ml-consult.co.uk/foxst-41.htm
Autor: Mike Lewis
Traducido por: Ana María Bisbé York


Una sencilla utilidad para ayudarle a controlar los iconos, bitmaps y otros archivos gráficos que utiliza en su aplicación.

Una de las tareas más difíciles en el desarrollo de proyectos en Visual FoxPro es seguir la pista de muchos iconos, bitmaps y otros gráficos que utiliza en la aplicación. En un proyecto típico, utiliza probablemente cientos, incluso miles, de esos archivos. Los emplea para la propiedad Icon de sus formularios, para la propiedad Picture de los botones de comando, y en otros muchos lugares de su interfaz, donde desee tener efecto visual. Algunos de estos archivos serán tipo ICOs, otros, BMPs, GIFs, JPGs u otro de los 17 formatos gráficos que soporta nativamente VFP.

Controlar estos datos es un dolor de cabeza. En nuestro propio proyecto, típicamente verificamos diferentes tipos de iconos para una función particular, entonces olvidamos borrar los que hemos rechazado, resultando un archivo no deseado que desordena la carpeta. Al trabajar con un equipo de programadores, encontramos con frecuencia que cada programador escoge sus propios gráficos, por tanto, al final tenemos diferentes imágenes que serán empleadas para lo mismo. Y si el archivo gráfico se ha perdido, a veces sólo nos damos cuenta cuando el usuario se queja.

Para ayudar a solucionar este y otros problemas de este estilo, hemos creado una sencilla utilidad Review Bitmaps que compara el archivo gráfico en su carpeta de proyecto con aquellos que en realidad se emplean en la aplicación. Es una herramienta excelente que nos ayuda a controlar los archivos no utilizados que desorganizan la carpeta de archivos "bitmaps". Es también muy útil para detectar los archivos perdidos y para no perder de vista qué archivo bitmap pertenece a qué imagen. (Empleamos el término "bitmap" para referirnos a cualquier icono o archivo de imagen. No es exactamente relativo al formato de Windows Bitmap [BMP].)

Como todas las utilidades y componentes de Foxstuff, la herramienta Review Bitmaps es totalmente gratis. Solamente siga el enlace que aparece más abajo, para la descarga. Review Bitmaps requiere VFP 8.0 ó superior.

Para comenzar

La instalación de Review Bitmaps es trivial. Sólo hay que descomprimir el archivo descargado en una carpeta necesaria del PC de desarrollo. Una vez hecho esto, haga lo siguiente:

  1. Lance VFP (8.0 ó superior).
  2. Establezca el directorio predeterminado o la ruta de búsqueda al directorio en que descomprimió el archivo descargado.
  3. En la ventana de comandos escriba, DO FORM Review_Bitmaps. Aparecerá el formulario mostrado en la Figura 1.


Figura 1: Formulario principal de la utilidad

Utilizar Referencias de código

Para que Review Bitmaps pueda hacer algo útil, necesita saber qué archivos gráficos se referencian desde el código de la aplicación. En lugar de buscar los programas, formularios y clases por sí mismo para obtener esta información, parar hacer este trabajo, confía en la herramienta de VFP Referencias de Código.

Entonces, el primer paso en el uso de Review Bitmaps es lanzar la herramienta Referencia de códigos. Se puede hacer desde el menú Herramientas de VFP; pero es más fácil de hacer si se hace clic sobre el enlace que se encuentra cerca del borde superior del formulario (Launch Code Reference = Cargar Referencias de Código). Cuando lo haga , Review Bitmaps introducirá automáticamente la cadena de búsqueda necesaria en el cuadro Look for (Buscar por), como se muestra en la Figura 2.


Figura 2: La ventana de búsqueda de la herramienta Referencia de Código, con el cuadro de texto Look for ya relleno.

Luego, configure la herramienta Referencia de Código de la siguiente manera:

  1. Especifique el proyecto o carpeta de su aplicación..
  2. Establezca File types (Tipos de archivos) igual a Common (Comunes) o All Source (Todas las fuentes).
  3. Seleccione Use regular expressions  (Empleo de expresiones regulares) (esto es importante).
  4. No seleccione Match Case o Match Whole Word. (Coincidir mayúsculas/minúsculas o Coincidir con palabra completa)
  5. Establezca Comments (Comentarios) igual a Ignore Comments (Ignorar comentarios).

Si ha lanzado la referencia de código desde el menú Herramientas, entonces además tendrá que entrar la cadena de búsqueda que es la siguiente:

(\.ani)|(\.bmp)|(\.cur)|(\.dib)|(\.emf)|(\.exif)|
(\.gif)|(\.gfa)|(\.ico)|(\.jpg)|(\.jpeg)|(\.jpe)|
(\.jfif)|(\.png)|(\.tif)|(\.tiff)|(\.wmf)

Esto es una expresión regular que localiza nombres de archivos con cualquiera de estas extensiones aplicables a archivos gráficos. Vea que, la cadena de búsqueda se ha cortado en tres líneas para hacerla más legible: Debe entrarla como una única cadena al cuadro de texto Look for (Buscar por).

Ya puede seguir adelante con la búsqueda.

Cuando finaliza la búsqueda, exporte los resultados al archivo DBF que desee. Para hacer esto, haga clic en el botón Export (Exportar) de la barra de herramientas de la Referencia de Código, como tipo de salida, seleccione Visual FoxPro Table (DBF) y especifique el nombre del archivo para guardar los resultados exportados.

En este punto, puede cerrar la herramienta Referencias de código

Volviendo al formulario Review Bitmaps, introduzca la ruta y el nombre de archivo del archivo exportado que acaba de crear, y la ruta del directorio que contiene los archivos gráficos de su aplicación. Si se aplica, marque la casilla "Include subfolders" (Incluir Subcarpetas).

Seleccione la salida

Review Bitmaps ofrece tres posibles salidas:

  1. Una lista de archivos gráficos inutilizados, es decir, aquellos archivos que están en su carpeta de gráficos; pero no están referenciados desde una aplicación. Estos archivos son candidatos a ser eliminados.
  2. Una lista de archivos gráficos, es decir, aquellos ficheros que son referenciados en la aplicación pero que no están en la carpeta de archivos gráficos. Esta lista le ayudará a asegurarse de que todos los archivos requeridos están disponibles en tiempo de compilación.
  3. Una lista de todos los archivos en la carpeta, mostrando su imagen. Este informe es una herramienta excelente para determinar exactamente las imágenes disponibles. También es útil para determinar archivos con imágenes duplicadas en diferentes sub carpetas (Vea que esta opción no requiere el archivo exportado de Referencias de código, si lo único que desea es este informe, puede saltarse el paso de búsqueda y exportación de la Referencias de código.

Estas tres salidas están disponibles como Informes de VFP, los que se pueden visualizar o imprimir directamente desde la formulario Review Bitmaps. Las dos primeras salidas se pueden ver como listas que se pueden copiar al portapapeles o a un archivo.

Para obtener cualquiera de estas salidas, presione el botón adecuado del formulario.

Interprete con cuidado

Hemos visto que Review Bitmaps nos brinda una ayuda muy valiosa para nuestros proyectos; pero no es perfecto. En algunos casos, la referencia de códigos falla al identificar los archivos gráficos - frecuentemente debido a que su nombre se construye dinámicamente en el código de programa. En aquellos casos, Review Bitmaps, informará incorrectamente de que el archivo en cuestión no se utiliza.

La Referencia de Código puede también indicar lo que cree que es un archivo gráfico. Por ejemplo, si su código se refiere a un objeto llamado Report que tiene una propiedad llamada BMP, cualquier referencia a esa propiedad como Report.BMP se va a tratar como un nombre de archivo. Entonces, Review Bitmaps va a informar erróneamente sobre un archivo inexistente.

Por estas razones, debe interpretar las salidas desde Review Bitmaps con mucho cuidado. En particular, tenga cuidado de no eliminar permanentemente ningún archivo a menos de que esté seguro de que no lo necesita.

¡ A por ello !

Para descargar la utilidad, simplemente haga clic en un enlace:

Descarge ahora - http://www.ml-consult.co.uk/download/review_bitmaps.zip

El archivo descargado se llama review_bitmaps.zip, y ocupa 26 KB.

Mike Lewis Consultants Ltd. Agosto 2006.

4 de abril de 2018

Interfaces para objetos COM

En ciertas ocasiones, cuando se diseña con Orientación a Objetos, es útil separar las responsabilidades de una clase en dos o más clases distintas. Con esto se logra que una entidad exponga los lineamientos a seguir, pero, pueda desligarse de la forma de llevarlos a cabo, delegando esas responsabilidades en otras clases. Es más, existe un Patrón de Diseño que así lo aconseja: El Puente.

Otro caso típico, por ejemplo, se da con los servidores COM. Un objeto compilado en una DLL puede realizar sus funciones básicas o capturar sus eventos, pero, es útil que otro objeto encapsule las implementaciones de su funcionamiento para adaptar esa funcionalidad a los proyectos clientes de ese servicio, a la plataforma donde corre, etc. Luego, el objeto COM debe permitir delegar cierta funcionalidad a un objeto externo (no compilado en la misma DLL).

Para llevar esto adelante es necesario que se cumplan ciertas condiciones: Un contrato entre los objetos en cuestión. Esto se logra mediante las interfaces. En Programación Orientada a Objetos (POO), las interfaces son tipos de datos abstractos (clases abstractas), que exponen o publican los miembros públicos que deben contener otras clases para implementar dicha interfaz. Es decir, las interfaces son como listas de mensajes a los cuales debe responder un objeto de determinada clase, sin indicar cómo responder (no implementan funcionalidad).

Por ejemplo, se puede contar con una clase que encapsule un Array de objetos, a la cual podríamos llamar Arreglo, y con una interfaz llamada Pila que expone los métodos push (apilar) y pop (retirar o desapilar). Luego, podríamos querer definir una nueva clase basada en Arreglo: ArregloPila. Si a esta nueva clase se le definen los métodos push y pop, luego, podemos decir que ArregloPila "implementa" la interfaz Pila.

En el ejemplo anterior, se muestra como una de las funcionalidades de las interfaces es la de simular herencia múltiple, aspecto que algunos lenguajes, entre ellos VFP, no soportan. ArregloPila pareciera ser una clase heredera de Arreglo y de Pila, y en realidad es solamente heredera de la primera. Por otro lado, las interfaces sirven para validar que un objeto de una clase implemente lo necesario para cumplir con determinada funcionalidad. Por ejemplo, si se desea administrar las acciones que se vayan realizando en un programa para dar la posibilidad de deshacer esas acciones (siempre comenzando desde la última realizada), es conveniente que la estructura donde se almacenen esas acciones sea una pila. Es decir, será necesario verificar que un posible objeto llamado oAcciones sea de una clase que "implemente" la interfaz Pila.

VFP no soporta el uso de interfaces de forma nativa. Es un buen momento para aclarar que esta premisa se refiere a las interfaces puras de la POO, definidas con la misma categoría que las clases. En realidad, cada vez que se está definiendo una clase, todos aquellos miembros públicos son la interfaz de ella. Sin embargo, las interfaces declaradas como clases abstractas pueden ser útiles a la hora de documentar para especificar determinado comportamiento o clasificación de clases.

El uso de interfaces en VFP se puede simular mediante el análisis de la estructura interna de las clases, como lo ha hecho Víctor Espina, en un artículo publicado recientemente: Interfaces en VFP

Existe una excepción a estas restricciones que estamos nombrando: Los objetos COM. VFP incluye una serie de métodos y cláusulas que permiten implementar interfaces definidas en objetos COM, dependiendo de la forma en que se haya diseñado. Un ejemplo de esto se encuentra en Visual FoxPro Callback Design (Microsoft Visual FoxPro and Advanced COM)

La idea es definir 3 clases:

  • La clase principal del objeto COM.
  • Una clase que funcione como interfaz de los métodos delegados por la clase principal. Debe definir, sin implementar, los métodos que se delegarán.
  • Una clase externa que implemente los métodos que publica la interfaz.

A continuación se muestra un ejemplo implementado como dos PRG, de los cuales, el primero debe incluirse en un proyecto y compilarlo como DLL, mientras que el segundo PRG puede ejecutarse de cualquier forma posible. El alcance de este artículo no contempla la compilación de proyectos como DLL, por lo que si necesita ayuda con ese paso, se recomienda el siguiente artículo: Crear Servidores de Automatización

*------- Primer PRG: Incluir en un proyecto y compilarlo como DLL -------*
*************************
******* Clase COM *******
*************************

DEFINE CLASS ClaseOLE AS Session OLEPUBLIC
HIDDEN oImplementador
HIDDEN cPropiedad as String

PROCEDURE Init()
 this.oImplementador = .Null.
ENDPROC

*** Métodos para setear y recuperar la propiedad ***
PROCEDURE setPropiedad(cProp as Variant)
 IF ISNULL(this.oImplementador) ;
  OR (!ISNULL(this.oImplementador) AND this.oImplementador.ValidarAntesDeSetear(cProp))

  this.cPropiedad = cProp
 ELSE
  COMRETURNERROR("Valor incorrecto", "El valor ingresado no cumple con la validación")
 ENDIF
ENDPROC

PROCEDURE getPropiedad() as Variant
 IF !ISNULL(this.oImplementador)
  this.oImplementador.mostrar(this.cPropiedad)
 ENDIF

 RETURN this.cPropiedad
ENDPROC

*** Asociación del objeto COM con un objeto externo que implementa la interfaz InterfazImplementador ***
PROCEDURE setImplementador(oImplementadorExterno as Variant)
this.oImplementador = GETINTERFACE(oImplementadorExterno, "iInterfazImplementador", "interfacesCOM.ClaseOLE")
ENDPROC

ENDDEFINE


************************************************
******* Clase Interfaz con el objeto COM *******
************************************************
DEFINE CLASS InterfazImplementador as session OLEPUBLIC

PROCEDURE mostrar(cProp as String)
ENDPROC

PROCEDURE validarAntesDeSetear(cProp as Variant) as Boolean
ENDPROC

ENDDEFINE

*------- Segundo PRG: Cliente del objeto COM -------*
* Verificar, de ser necesario, las rutas.

LOCAL loImp as ImplementadorExterno
LOCAL loObjetoCOM as ClaseOLE

loImp = CREATEOBJECT("ImplementadorExterno")
loObjetoCOM = CREATEOBJECT("interfacesCOM.ClaseOLE")
loObjetoCOM.setImplementador(loImp)

TRY
 * Ejecución exitosa. Muestra "Hola Mundo"
 loObjetoCOM.setPropiedad("Hola Mundo")
 loObjetoCOM.getPropiedad()

 * Ejecución errónea. Muestra un error.
 loObjetoCOM.setPropiedad(DATE())
 loObjetoCOM.getPropiedad()
 
CATCH TO loError
 MESSAGEBOX(loError.message, 16, "Error")
ENDTRY

RETURN


********************************************************************************************************************************
******* Objeto que implementa parte de la funcionalidad de la clase ClaseOLE (Implementa la interfaz InterfazImplementador) *******
********************************************************************************************************************************
DEFINE CLASS ImplementadorExterno AS Custom
IMPLEMENTS iInterfazImplementador IN "interfacesCOM.dll"

*** Método validarAntesDeSetear ***
PROCEDURE iInterfazImplementador_validarAntesDeSetear(cProp as Variant) as Boolean
 RETURN VARTYPE(cProp) $ "CN"  && La propiedad debe ser caracter o numérica.
ENDPROC

*** Método mostrar ***
PROCEDURE iInterfazImplementador_mostrar(cProp as Variant)
 MESSAGEBOX(cProp)
ENDPROC

ENDDEFINE

Como se puede ver, la clase ClaseOLE está diseñada para delegar parte de su funcionamiento a un objeto externo, de la clase ImplementadorExterno en este caso, que implementa los métodos definidos por la interfaz InterfazImplementador. Con todo esto se logran ciertas ventajas:

  • Es fácil extender la funcionalidad y utilización de ClaseOLE. En el ejemplo, el procedimiento validarAntesDeSetear() permite cambiar las validaciones sin tener que recompilar la DLL. Además, los objetos externos pueden ser escritos en cualquier lenguaje, lo que permitiría que dichas validaciones se ajusten al lenguaje.
  • Se logra adaptar el funcionamiento del objeto COM a la plataforma que lo consume. En el caso del ejemplo, la función MESSAGEBOX() generaría un error si se intentara ejecutar desde la DLL. Luego, esa parte se delega.
  • Es útil este modelo para procesar eventos que captura el objeto COM.

Ahora, algunas preguntas y respuestas rápidas sobre el modelo:

  1. ¿Se puede omitir la escritura de alguno de los métodos que expone la interfaz en la clase que la implementa? No, deben escribirse todos los métodos publicados (aunque se dejen vacíos en su implementación). La cláusula IMPLEMENTS obliga a cumplir con el protocolo de la interfaz. Si alguno de los métodos se omite, al intentar realizar la creación del objeto externo (CREATEOBJECT("ImplementadorExterno") en este caso), se lanzaría un error indicando los métodos faltantes.
  2. ¿Qué pasa si no se incluye la cláusula IMPLEMENTS en la definición de la clase externa? La invocación a loObjetoCOM.setImplementador() arrojaría error (al llegar a la función GETINTERFACE()) ya que se valida que el objeto implemente la interfaz solicitada de forma explícita.
  3. Luego, ¿se podría omitir el uso de IMPLEMENTS y GETINTERFACE(), y hacer la asociación de los objetos directamente? Sí, y parecería, incluso, mucho más sencillo de implementar porque no sería necesario explicitar ciertas líneas de código y se podría desarrollar una clase menos. Sin embargo, incluyendo este manejo de interfaces se logra mayor orden:
    • Por un lado, se incluye implícitamente una validación de que el objeto externo cumple con el protocolo impuesto por la interfaz. Esto asegura que los métodos llamados por el objeto COM existen y no se va a generar un error.
    • Por otro lado, es la forma más eficiente de indicar qué métodos se deben escribir. En el ejemplo se ve que los métodos a escribir no precisamente concuerdan con los nombres de los métodos de ClaseOLE.

Pablo Lissa


Artículos relacionados:


31 de marzo de 2018

EditSource()

La nueva función EditSource() en VFP7 y superior es particularmente práctica en por lo menos 3 escenarios.

1) Comunicandose con otro desarrollador con respecto a un código en una localización particular de un método o PRG. Por ejemplo, al reportar un "bug" al desarrollador, en lugar de escribir algo parecido a esto al describir donde está el problema:

MODI CLASS AlgunaClase OF AlgunVCX METHOD AlgunMetodo
*-- ir a la línea 17
*-- or 
*-- ir a la línea IF VARTYPE(AlgunaVariable) = 'U'

Ud. solo puede escribir:

EditSource("AlgunVCX.VCX",17,"AlgunaClase","AlgunMetodo")

y a continuación describir el problema.

2) Escribir a un desarrollador que abra el programa, clase, o formulario en una línea específica del código.

3) Insertando referencias de EditSource() en comentarios de código, así que cualquier lectura del desarrollador, puede simplemente seleccionar el texto del EditSource() y hacer Click Derecho y ejecutar la selección desde el menú contextual.

Clases:

EditSource("MyLib.VCX",0,"ClassName")

... equivalente a MODI CLASS ClassName OF MyLib

Métodos de clases:

EditSource("MyLib.VCX",2,"ClassName","Init")

... equivalente a MODI CLASS ClassName OF MyLib METHOD INIT, pero con la ayuda agregada de especificar el número de la línea donde se coloca el cursor.

Métodos de clases miembros:

EditSource("MyLib.VCX",2,"ClassName","Member.Init")

... equivalente a MODI CLASS ClassName OF MyLib METHOD Member.Init, pero con la ayuda agregada de especificar el número de la línea donde se coloca el cursor.

Formularios

EditSource("MyForm.SCX")

... equivalente a MODI FORM MyForm

Métodos de formularios:

EditSource("MyForm.SCX",6,"","Form1.Load")

... equivalente a MODI FORM MyForm METHOD LOAD, pero con la ayuda agregada de especificar el número de la línea donde se coloca el cursor.

Metodos miembros de formularios:

EditSource("MyForm.SCX",13,"","Form1.Member.Init")

... equivalente a MODI FORM MyForm METHOD Member.Init, pero con la ayuda agregada de especificar el número de la línea donde se coloca el cursor.

Programas:

EditSource("MyPRG.PRG")

... equivalente a MODI COMM MyPRG

Programas:

EditSource("MyPRG.PRG",10)

... equivalente a MODI COMM MyPRG, pero con la ayuda agregada de especificar el número de la línea donde se coloca el cursor.

Como el número exacto de la línea puede cambiar, al llamar EditSource() desde el código de un desarrollador que cambio el código fuente por modificaciones o la inserción de comentarios, usted puede determinar programaticamente el número de la línea deseada antes de llamar EditSource(). Ud. puede usar un PRG como el ejemplo:

local laLines[1], lnLine, xx
alines(laLines,filetostr("Test.PRG"))
FOR xx = 1 to alen(laLines,1)
  IF laLines[xx] = 
    lnLine = xx
    exit
  ENDIF
ENDFOR
EditSource("Test.PRG",lnLine)

EditSource() dispara un NOWAIT implícito de modo que usted pueda tomar la acción de seguimiento en el código del desarrollador que ejecuta EditSource(). Por ejemplo, para destacar una línea específica del código, ejecute:

keyboard "{Shift+End}" 

ó

keyboard "{Shift+DnArrow}"

inmediatamente después del comando EditSource().

Copie y pegue el código siguiente en la ventana de comandos, y después lo ejecuta para ver el efecto:

text to lcString noshow
* test.prg
aa = 100
bb = 200
cc = 300
return aa + bb + cc
endtext
strtofile(lcString,"test.prg")
text to lcString noshow
* test1.prg
editsource("test.prg",3)
keyboard "{Shift+DnArrow}"
endtext
strtofile(lcString,"test1.prg")
do test1

[025] VFP Tips & Tricks - Drew Speedie

27 de marzo de 2018

Función ATAGINFO()

VFP 7.0 agregó una la nueva función ATAGINFO() para llenar un Array con la información sobre las etiquetas contenidas en un archivo de indice CDX.

use <AlgunaTabla> in 0<
ataginfo(laTemp,"","AlgunaTabla")
display memory like laTemp

[035] VFP Tips & Tricks - Drew Speedie