Recomendaciones

De ClarionWiki
Ir a la navegación Ir a la búsqueda

Recomendaciones y Trucos que nos pueden evitar dolores de cabeza

Versión Demo (básica)

Poner este embed global en un par de tablas importantes

Global Embeds
   Abc Objects
       File Managers
           File Manager for YourFile
               Insert - Before Parent Call
IF RECORDS(SELF.FILE)> 10
 MESSAGE('Demo Version')
 RETURN LEVEL:FATAL
END

Fernando Cerini

Obtener un total general (al final) en un Reporte

Lo mas facil es armar un break group sobre una variable que no cambie nunca,
(puede ser cualquier variable global, por ejemplo)
Luego pones los totales en un group footer de este break y listo.
Como la variable nunca cambia (puede ser una global por ejemplo) solo se
imprime el group footer al final del reporte.
Tambien podria usarse el Group Header de ese break como "caratula" del reporte

Fernando Cerini

Usar DLLs no creadas en Clarion

Para Usar DLLs no creadas en Clarion, hay que seguir los siguientes pasos:

- Crear los prototipos equivalentes en Clarion
para esto hay que averiguar cuales son los prototipos originales (en c, por ej.)
y ver la equivalencia de tipos entre Clarion, C y  o C++
- Usar el Libmaker para la creacion de una Clarion Library (.LIB File) para la DLL
- Incluir el .LIB File al proyecto

Para mas detalles, ver .\docs\FAQsTips&Tricks.pdf el tema "How to Use Windows DLLs NOT Created in Clarion".

Fernando Cerini

Modificar código en múltiples embeds, o varias tablas del DCT

Se puede exportar la aplicación o el DCT a Texto (app.TXA) modificar el texto con un buen editor (yo recomiendo el textpad) y luego importar de nuevo.

Obviamente, hacer un backup primero porque esto es una opración "peligrosa"

En la sección de Downloads el documento de migración de TPS a SQL se basa en esta técnica para modificar el DCT.

Fernando Cerini

Deshabilitar el PopUp en un control TEXT

Simplemente asignar la Key MouseRight al control TEXT

Desplegar una opción de menú desde un botón

Pónle un hotkey al menu, por ejemplo:

Menu Text: &Browse

Luego en el botón

PRESSKEY(AltB)

Fernando Cerini

Obtener los diferentes elementos del path

Global Embeds - Inside Global Map

Include('clib.clw')

En el embed:

X# = FnSplit(LOC:Path,LOC:Drive,LOC:Dir,LOC:File,LOC:Ext)
DISPLAY

Las variables locales son todas CSTRING(256) Por ejemplo para saber solo el nombre del archivo se usaria LOC:File & LOC:Ext

Fernando Cerini

Conversión de Archivos

1) Tenés que tener un diccionario aparte, en principio vacio. Yo lo llamo conversiones.dct.

2) Abrís ambos DCT. Te parás en la definición que querés convertir y apretás "copiar". Te parás en el conversiones.dct y apretás "pegar". A este archivo pegado lo renombrás con la fecha (por ejemplo, si mi archivo a convertir se llama CLIENTES, en conversiones lo pego como CLIENTES_ 050328).

3) Hacés todos los cambios que necesites en la definición de tu archivos CLIENTES. Acá ya podés recompilar todo tu sistema.

4) Cerrá tu diccionario, dejá abierto Conversiones.dct. Seleccioná "convert" el archivo, te pide el path al archivo y primero te hace un "browse" sobre el mismo, de ahí seleccionás el menú "File", "Convert File" y te abre una ventanita.

5) Acá, en target dictionary elegís tu DCT, el que tiene la definición ahora modificada (Acordate que estás en el conversiones.dct).

6) En target structure, buscás en tu DCT el nombre del archivo convertido.

7) En Generated source, tipeá algo que tenga sentido: yo voy numerando mis programas conversores, así que voy poniendo algo como CONV0001, CONV0002, etc. Fijate bien el path donde lo metés.

8) Si no salió solo, salí del browser, cerrá todos los DCT.

9) Andá por "File", "Change directory" y cambiate al directorio donde grabaste el fuente del conversor. Yo uso un subdirectorio "pasa".

10) Andá a "File", "Open", elegí "Clarion source", y abrí el programa conversor.

11) Andá a "Project", "Set" y seleccioná el archivo de proyecto que tiene el mismo nombre que el fuente del conversor.

12) Editale lo que quieras al programa conversor. Acá por ejemplo a veces cambio el directorio de salida, o ajusto la ventana de display, le pongo mensajes o hago cualquier asignación que sea necesaria.

13) Compilá, correlo y listo. Te queda un ejecutable autónomo que podés correr en el cliente llamándolo a mano, desde un instalador o desde tu propia aplicación.

14) Ventaja adicional: Si tu cliente guarda backups de los archivos, y por la circunstancia que sea tiene que reprocesar información de un archivo viejo, se pueden correr todos los programas de conversión que corresponden a ese archivo, en secuencia, para llevar su formato al actual.

15) Otra ventaja: este método sirve para cualquier base ISAM, para SQL hay que hacer algunos pasos más pero básicamente se puede hacer lo mismo.

Finalmente, te digo que esto lleva muchísimo más tiempo explicarlo y aprenderlo que hacerlo. Cuando te digo que todo esto lleva 1 minuto, es en serio, es lo que lleva cuando ya sabés cómo hacerlo de memoria.

Una acotación, con este método te van quedando en el conversiones.dct todas las definiciones históricas de tus archivos, algo que puede llegar a ser muy últil, mientras que en tu dct de "trabajo" sólo está la definición "actual" y no hay nada que haga ruido.

Saludos, Jorge A. Lavera

Encriptación de TPS

Sobre el Owner Name y su seguridad o capacidad de encriptación depende mas que nada del planeamiento que a la herramienta en sí misma

Caso 1 : Owner Name : " Tu palabra clave "

Este caso es el mas vulnerable de todos no se demora mas de una hora en buscarlo y encontrarlo a simple vista , con la tecnología adecuada son solo minutos.


Caso 2 : Owner Name : " ©}êô?Ñé? " ( Carcateres ALT + xxxx)

Este caso ya comienza a complicar el nivel de búsqueda dentro del exe porque no se encuentra relación y lleva a la confusión con los otros caracteres dentro del exe y todos los hexadecimales, pero es vulnerable.


Caso 3 : Owner Name : " VGLO:xxxxxxxxxxxxxxx"

Aca el valor del Owler Name está dentro de una variable global, esa variable global puede traer la informacion desde otro lado , archivo , otra variable,etc. Este método es el mas complicado de todos para aquel que quiera romper el TPS.

Para mi el mejor caso de seguridad en TPS's es el 3, por el nivel de dificultad que presenta y ante un intento de vulnerabilidad por fuerza bruta siempre devuelve un valor 0. Es muy importante tener bien documentado lo realizado utilizando el caso 3 porque de perder la información, el diseño, etc. el mismo desarrollador puede quedar preso y victima de su propia seguridad

Estos son lo niveles que se pueden manejar desde el IDE de desarrollo de Clarion, pero tambien se le pueden agregar elementos externos.

Por ejemplo utilizando el Caso 3 + algún compactador de exes ( ASPACK ) realmente se hace casi imposible encontrar las cabeceras de definición de las tablas , etc.etc. .

Remarco de lo CASI , porque se puede vulnerar, pero ahi ya comienzan a medir otros parámetros como ser el costo/beneficio de invertirle horas y horas o días y días a ese intento de poder vulnerar el archivo , etc, etc, etc. el cual también está relacionado al hardware y su capacidad de procesamiento , etc.

Espero que les sirva.

Ing. Fabian Coria

Consultor Seguridad Informatica, Capital Soft

Diferir la carga de un browse ABC

Los browse ABC se cargan automáticamente al abrir la ventana. Pero muchas veces se desea controlar el momento de la carga porque se utilizan filtros que deben ser ingresados por el usuario (por ejemplo, un rango de fechas). Es un tema particularmente importante cuando se trata de browses “file loaded”, o cuando se trabaja sobre MySQL (al utilizar este motor todos los browse son “file loaded”).

Para diferir la carga de un browse ABC hay dos posiblidades.


A. Mediante BrowseClass.ActiveInvisible

Esta técnica consiste en “esconder” el ListBox mediante HIDE y apagar la propiedad BrowseClass.ActiveInvisible. Cuando se quiere cargar el browse se quita la propiedad HIDE al ListBox.

Los pasos a seguir son los siguientes:

1. En el Window Formatter encender la propiedad HIDE (o utilizar el comando HIDE en el código del procedimiento) para el control correspondiente al ListBox del browse.

2. En WindowManager.Init, con prioridad 8150, agregar el siguiente código:

BRW1.ActiveInvisible = False	!BRW1 es el nombre del objeto Browse

3. Cuando se quiera cargar y mostrar el browse, agregar el siguiente código:

?Browse:1{PROP:Hide} = False	!?Browse:1 es el control del ListBox
ThisWindow.Reset(True)


B. Derivando la BrowseClass

Esta técnica tiene la ventaja de que no es necesario que el ListBox esté oculto. Consiste en agregar una nueva propiedad a la clase BrowseClass que controle cuándo se desea cargar el browse. Según el estado de esta propiedad se omite o se ejecuta el método BrowseClass.ResetQueue.

Los pasos a seguir son los siguientes:

1. En BrowseBoxBehavior, en la solapa Classes, encender la opción “Derive?”

2. En New Class Properties agregar una propiedad de tipo BYTE. Para seguir con la nomenclatura en inglés del resto de la clase, la llamaremos “WaitToFill”

Property Name: 	WaitToFill
Property Type: 	BYTE

3. En WindowManager.Init, con prioridad 8150, agregar el siguiente código:

BRW1.WaitToFill = True	!BRW1 es el nombre del objeto Browse

4. En BrowseClass.ResetQueue, con prioridad 2500 (antes del ParentCall), agregar el siguiente código:

IF SELF.WaitToFill
   RETURN
END

5. Cuando se quiera cargar y mostrar el browse, agregar el siguiente código:

BRW1.WaitToFill = False	!BRW1 es el nombre del objeto browse
ThisWindow.Reset(True)

Si bien esto mismo se podría lograr con una variable, con una nueva propiedad se puede administrar más de un browse en el mismo procedimiento, aunque tengan diferente comportamiento.


Otras posibilidades

Como solución elemental esta técnica es suficiente. Una solución más completa sería derivar directamente la clase BrowseClass y utilizar la clase derivada en lugar de la clase original. El método ResetQueue de la nueva clase haría su propia omisión en base al valor de la propiedad WaitToFill. También se podría agregar un método para encender y apagar la propiedad WaitToFill.

La solución final consistiría en escribir un template que se encargue de implementar la técnica.



Daniel Ruzo (amazingGUI)

Identificación ÚNICA

En ocasiones el campo de identificación de nuestros archivos no son suficiente con que éste sea autonumerado de forma secuencial. Por ejemplo tener un pedido en un archivo y los productos del pedido en otro archivo, podría ocurrir que ambos archivos sean reemplazados por backups de diferentes fechas (ej. pedidos mas viejo que item) asi cuando abrimos un nuevo pedido éste ya poseerá productos. Otro ejemplo es que tengamos varias bases distribuidas en diferentes máquinas (no en red) y éstas se vuelcan sobre un solo grupo de archivos via internet (o cualquier otro medio) si son autonumeradas existirán mas de una ID con el mismo número. Para solucionar esto pruebe lo siguiente:

En el Campo del archivo que sirve de Identificación Ej. ClienteNumero
colocar en INITIAL VALUE colocar la función GeneraID()

Crear la función GeneraID

 loc:IDU   STRING(20)
 CODE
 !Identificación Única
 loc:IDU = FORMAT(TODAY(),@d11) & FORMAT(CLOCK(),@t5) & FORMAT(RANDOM(0,999),@n03)
 RETURN loc:IDU

un saludo DIPS

Parámetros en procedimientos llamados en threads nuevos

Si quiero mandar parámetros a un procedimiento llamado con START (es decir, en un thread nuevo), puedo mandar solo hasta 3 parámetros y todos deben estar definidos como string en el prototipo del procedimiento. Internamente puedo convertir esos parámetros al tipo que necesito en variables locales al procedimiento.

Meses, días, botones en Español

En el main, en el embed "Initialize the procedure" colocar:

LOCALE('CLAMONTH','Enero,Febrero,Marzo,Abril,Mayo,Junio,Julio,Agosto,Septiembre,Octubre,Noviembre,Diciembre')
LOCALE('CLAMON','Ene,Feb,Mar,Abr,May,Jun,Jul,Ago,Sep,Oct,Nov,Dic')
LOCALE('CLACOLSEQ','AÁÄÅÆaàáâäåæBbCÇcçDdEÉeèéêëFfGgHhIÍiìíîïJjKkLlMmNnÑñOÓÖoòóôöPpQqRrSsßTtUÚÜuùúûüVvWwXxYyÿZz')
LOCALE('CLABUTTON','Aceptar,&Sí,&No,&Abortar,&Reintentar,&Ignorar,Cancelar,&Ayuda')
LOCALE('CLACASE','ÄÁÅÆÇÉÍÑÓÖÚÜ,äáåæçéíñóöúü')
LOCALE('CLAAMPM','a.m.,p.m.')

Consultas SQL que en el Motor corren rapido pero en Clarion NO

Un truco fácil pero funcional (sólo válido para SQL Server).

Suponiendo que la instrucción original sea:

 Select P.Codigo, P.Descripcion, K.Entradas, K.Salidas, K.Valor_Entradas, K.Valor_Salidas
 From Productos P
 Inner Join Kardex K On P.Id_Producto = K.Id_Producto
 Where P.Codigo = 'AAA001' And K.Fecha = '01-10-2006'
 Order By K.Fecha, P.Codigo

Como CLARION genera un cursor puede que en ocaciones dicho query sea muy pesado y el cursor tarde mucho en resolverlo, para ello simplemente agrega lo siguiente:

 Select P.Codigo, P.Descripcion, K.Entradas, K.Salidas, K.Valor_Entradas, K.Valor_Salidas
 Into #TempTable
 From Productos P
 Inner Join Kardex K On P.Id_Producto = K.Id_Producto
 Where P.Codigo = 'AAA001' And K.Fecha = '01-10-2006'
 Order By K.Fecha, P.Codigo
 Select * From #TempTable


De esta manera todo se realiza en el servidor y solo va a devolver los datos ya procesados en el #TempTable


Este truco se lo agradezco a mi amigo Juan Manuel Medina.


Javier A. Junca Barreto. (SICyA Software Ltda.)

Exportar fechas a Excel

Para exportar campos fecha a Excel hay que mandarlos con format(campo, @d01b), de lo contrario Excel interpreta aleatoriamente meses como días, días como meses, etc.

De todas maneras, que el mecanismo comentado arriba funcione depende de la configuración de Excel. Otra opción es usar la función “date” de Excel, y enviar por separado año, mes y día. Por ejemplo:

 MSExcel1.ExecFunction('A1', '=date(' & year(FechaClarion) & ',' & month(FechaClarion) & ',' & day(FechaClarion) & ')')