<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="es">
	<id>https://clarionwiki.com.ar/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Carlos+Barroso</id>
	<title>ClarionWiki - Contribuciones del usuario [es]</title>
	<link rel="self" type="application/atom+xml" href="https://clarionwiki.com.ar/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Carlos+Barroso"/>
	<link rel="alternate" type="text/html" href="https://clarionwiki.com.ar/index.php/Especial:Contribuciones/Carlos_Barroso"/>
	<updated>2026-04-21T11:00:01Z</updated>
	<subtitle>Contribuciones del usuario</subtitle>
	<generator>MediaWiki 1.43.1</generator>
	<entry>
		<id>https://clarionwiki.com.ar/index.php?title=Codigo_Util&amp;diff=88</id>
		<title>Codigo Util</title>
		<link rel="alternate" type="text/html" href="https://clarionwiki.com.ar/index.php?title=Codigo_Util&amp;diff=88"/>
		<updated>2015-12-15T15:25:45Z</updated>

		<summary type="html">&lt;p&gt;Carlos Barroso: /* Transacciones, TCF, Logout, Commit, Rollback */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;En esta sección se incluyen rutinas de código que nos resultaron útiles en más de una ocasión.&lt;br /&gt;
&lt;br /&gt;
== PDF con cualquier versión de Clarion ==&lt;br /&gt;
Después de mucho rebuscar algo gratis que haga esto, hoy pude componer algo:&lt;br /&gt;
1º) Aporte de Fernando Cerini para &amp;quot;capturar&amp;quot; los .wmf de los reportes de Clarion:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En el embed PrintPreview.Open, despues de Parent call&lt;br /&gt;
 &lt;br /&gt;
 get(SELF.ImageQueue,POINTER(SELF.ImageQueue))&lt;br /&gt;
 LOOP a# = 1 to RECORDS(SELF.ImageQueue)&lt;br /&gt;
          get(SELF.ImageQueue,a#)&lt;br /&gt;
          COPY(SELF.ImageQueue, &#039;c:\temp\Pagina&#039; &amp;amp; a# &amp;amp;&#039;.WMF&#039;)&lt;br /&gt;
          IF ERRORCODE() THEN MESSAGE(ERROR()).&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
En mi caso, con Clarion 5.0 ABC el embed point fue Previewer.Open, después del Parent call&lt;br /&gt;
&lt;br /&gt;
2º) Instalar OpenOffice y seguir las instrucciones de&lt;br /&gt;
http://www.xml.com/pub/a/2006/01/11/from-microsoft-to-openoffice.html&lt;br /&gt;
para crear la macro.&lt;br /&gt;
La verdad, no pude cambiar el nombre de Module1 a Conversiones o algo así, pero no importó mucho.&lt;br /&gt;
&lt;br /&gt;
3º) Cree un batch con la línea&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;C:\Archivos de Programa\OpenOffice.org 2.3\program\soffice&amp;quot; -invisible macro:///Standard.Module1.SaveAsPDF(c:\temp\pagina8.wmf)&lt;br /&gt;
&lt;br /&gt;
Es lo que dice el instructivo mencionado en el punto 2º) acomodado a mi instalación de OpenOffice, lo corrí y funcionó a la primera prueba.&lt;br /&gt;
&lt;br /&gt;
4º) Sólo queda hacer la llamada mediante RUN en el código de Clarion y santo remedio.&lt;br /&gt;
&lt;br /&gt;
Espero sea de ayuda para alguien.&lt;br /&gt;
Saludos&lt;br /&gt;
Ezequiel&lt;br /&gt;
&lt;br /&gt;
== Como filtrar un Tree ==&lt;br /&gt;
Una tecnica de Saul Perez Quezada para filtrar Arboles (generados con el template Relacion Tree), con filtros en runtime que son complejos, y que no podemos hacer con un simple SetFilter().&lt;br /&gt;
&lt;br /&gt;
Alguien ha posteado una pregunta sobre como Filtrar arboles, yo en lo personal he dejado de usar arboles, por que son bastante lentos y deficientes, pero en algunas aplicaciones es bastante util, de manera que comparto la experiencia y les subo una pequeña explicacion de como filtrar Arboles (generados con el template Relacion Tree), con filtros en runtime que son complejos, y que no podemos hacer con un simple SetFilter(), ya que no existe esto en la propiedad.&lt;br /&gt;
 &lt;br /&gt;
Saludos y espero les sirva.&lt;br /&gt;
 &lt;br /&gt;
Como filtrar un Tree con solamente tablas:&lt;br /&gt;
 &lt;br /&gt;
Dentro del Template para armar los arboles, tiene dentro de Primary y Secondary files la opcion de filtrar, agregando un filtro, pero muchas veces dicho filtro no es tan simple de armar, o necesitamos estarlo cambiando en runtime.&lt;br /&gt;
 &lt;br /&gt;
Aqui les presento un caso y como su solucion, esperando que les sirva...&lt;br /&gt;
 &lt;br /&gt;
Tengo tres tablas:&lt;br /&gt;
 &lt;br /&gt;
Abuelos&lt;br /&gt;
Padres&lt;br /&gt;
Hijos&lt;br /&gt;
 &lt;br /&gt;
Y en la tabla hijos, tengo un campo que define en que nivel de escuela en que va cada hijo: Pre-escolar, Primaria, Secundaria, Bachillerato, Universidad, PostGrado, Maestria y Doctorado.&lt;br /&gt;
 &lt;br /&gt;
En la ventana requiero que el usuario pueda filtrar por el nivel escolar, pero con las siguientes condiciones:&lt;br /&gt;
 &lt;br /&gt;
1.- El usuario puede seleccionar si quiere ver cualquier combinacion de estudios, es decir, que pueda ver los de primaria y universidad solamente o todos, o ninguno...&lt;br /&gt;
2.- Si selecciona un filtro donde resulte que un abuelo o padre no tiene hijos que cumplan la condicion, entonces no debe mostrarnos a esos abuelos y padres...&lt;br /&gt;
 &lt;br /&gt;
Considerando que el Tree template, muestra siempre los niveles de acuerdo al filtro que se arma, pues podria convertirse algo engorroso, estar cambiando el filtro, ya que no es una propiedad del relacion tree, tal como sucede con el browse class, sino que el tree filtra a base de If&#039;s interpuestos al llenar el queue...&lt;br /&gt;
 &lt;br /&gt;
Usando el template y solo poniendo los filtros de NIVELESCOLAR = LOC:MINIVEL, solo mostraria los hijos de un nivel, asi que este metodo no nos servira...&lt;br /&gt;
 &lt;br /&gt;
La idea seria entonces que como filtro usaremos una condicion matematica, si la condicion es Verdadera, entonces mostrara el hijo, de lo contrario no lo mostrara.&lt;br /&gt;
 &lt;br /&gt;
Asi que con el comando EVALUATE, evaluaremos un filtro, dentro del secondary files, opcion filter, si el filtro es bueno (igual a 1), entonces nos mostrara la info de lo contrario no...&lt;br /&gt;
 &lt;br /&gt;
Por ejemplo:&lt;br /&gt;
 &lt;br /&gt;
En el secondary files en buscamos la tabla de Hijos y le escribimos en el filtro:&lt;br /&gt;
 &lt;br /&gt;
0&amp;lt;EVALUATE(Filtro) ! Si el filtro es valido, entonces nos mostrara el resultado, de lo contrario no...&lt;br /&gt;
 &lt;br /&gt;
Declaramos un check box por cada cada condicion, con su respectiva variable: Preescolar, Primaria, Secundaria, etc... que sea tipo byte, con valor true = 1 y valor false = 0&lt;br /&gt;
 &lt;br /&gt;
! En el acepted del check box Preescolar... y cada condicion...&lt;br /&gt;
Do Filtrar&lt;br /&gt;
Display&lt;br /&gt;
 &lt;br /&gt;
! En procedure routines&lt;br /&gt;
Filtrar Routine&lt;br /&gt;
  Clear(Filtro) ! Variable para filtrar&lt;br /&gt;
 &lt;br /&gt;
  If Preescolar Then Filtro = &#039;HIJ:NIVELESCOLAR=&#039;&#039;Preescolar&#039;&#039;&#039; ..&lt;br /&gt;
  &lt;br /&gt;
  If Filtro And Primaria&lt;br /&gt;
    Filtro = Clip(Filtro) &amp;amp; &#039; And HIJ:NIVELESCOLAR=&#039;&#039;Primaria&#039;&#039;&#039;&lt;br /&gt;
  ElsIf Filtro And Primaria&lt;br /&gt;
    Filtro = &#039;HIJ:NIVELESCOLAR=&#039;&#039;Primaria&#039;&#039;&#039;&lt;br /&gt;
  End&lt;br /&gt;
  ! Esto se repite con todas las condiciones...&lt;br /&gt;
  ...&lt;br /&gt;
  ...&lt;br /&gt;
  &lt;br /&gt;
  ! Despues de armar el filtro, refrescar el browse...&lt;br /&gt;
 &lt;br /&gt;
  DO REL4::RefreshTree ! Esta rutina la genera el template, se debe buscar en los modulos su nombre correcto.&lt;br /&gt;
  POST(EVENT:NewSelection,?RelTree)&lt;br /&gt;
 &lt;br /&gt;
 Exit  &lt;br /&gt;
 &lt;br /&gt;
Hasta ahora, lo que se ha logrado es que cuando el usuario seleccione un tipo nivel escolar, nos mostrara o no los hijos, pero resulta, que ahora lo que necesito es que solo me muestre los abuelos que tienen nietos, y los padres que tienen hijos, es entonces por que no tiene caso que el arbol se llene con Mil abuelos, si solo 30 de ellos tienen nietos...&lt;br /&gt;
 &lt;br /&gt;
Para hacer debemos entender como se llena el queue del tree, basicamente, el tree, tantas vueltas como combinaciones de nuestra estructura tengamos, es decir, suponiendo que tenemos 3 abuelos, 3 padres, con 3 hijos cada uno, lo que hace el template es:&lt;br /&gt;
 &lt;br /&gt;
Por cada Abuelo Barre (con Loop) la tabla Padres por completo 1 vez y selecciono los que me corresponden y por cada padre barro la tabla hijos (con Loop) por completo y selecciono los que me corresponden...&lt;br /&gt;
 &lt;br /&gt;
Entonces esta estructura daria 3 x 3 x 3 = 27 vueltas. (Ahora imaginen lo que tarda en entre tablas de 10000 registros...)&lt;br /&gt;
 &lt;br /&gt;
Bueno, siguiendo esta idea, entonces lo que necesitamos, es interceptar cada vuelta en el punto donde no queremos que se ingrese al queue y hacer un break.&lt;br /&gt;
 &lt;br /&gt;
Conociendo esto buscamos en nuestros embeds, embedido llamado:&lt;br /&gt;
 &lt;br /&gt;
RelacionTree After Next Primary File y RelationTree After Next Secondary File...&lt;br /&gt;
 &lt;br /&gt;
Aqui buscamos el archivo que requerimos excluir en algunos casos, que es Padres e Hijos, le escribimos en el caso de Padres:&lt;br /&gt;
 &lt;br /&gt;
PAD:AbueloId = ABU:AbueloId&lt;br /&gt;
If Access:Padres.Fetch(FK AbueloId)  ! Si no existen padres que sean dependientes de este abuelo&lt;br /&gt;
  Break          ! hacer el break al Loop del armado del tree...&lt;br /&gt;
End&lt;br /&gt;
 &lt;br /&gt;
Y el caso de los hijos repetimos la condicion:&lt;br /&gt;
 &lt;br /&gt;
HIJ:PadreId = PAD:PadreId&lt;br /&gt;
If Access:Hijos.Fetch(FK PadreId)  ! Si no existen hijos que sean dependientes de este abuelo&lt;br /&gt;
  Break          ! hacer el break al Loop del armado del tree...&lt;br /&gt;
End&lt;br /&gt;
 &lt;br /&gt;
El efecto sera que no nos mostrara aquellos padres y abuelos que no tengan hijos.&lt;br /&gt;
 &lt;br /&gt;
Si requerimos algo mas complejo podemos usar una vista, en vez de una Fetch a una tabla, pero obviamente se alentara esto cada vez mas y mas al llenar el Arbol.&lt;br /&gt;
 &lt;br /&gt;
Nota&lt;br /&gt;
Si usamos un motor de SQL, una alternativa mucho mas eficiente es declarar un Vista en nuestro motor y diccionario que sirva de base para llenar el Arbol, con esto aumentaria considerablemente la velocidad.&lt;br /&gt;
&lt;br /&gt;
== Reemplazar caracteres en un string ==&lt;br /&gt;
Una rutina generica para reemplazar todas las instancias de un string dentro de otro&lt;br /&gt;
&lt;br /&gt;
 Replace       PROCEDURE(string find,string replace,*cstring into)&lt;br /&gt;
 Locate LONG,AUTO&lt;br /&gt;
  CODE&lt;br /&gt;
    IF UPPER(find)&amp;lt;&amp;gt;UPPER(replace)&lt;br /&gt;
       LOOP&lt;br /&gt;
         Locate = INSTRING(UPPER(find),UPPER(into),1,1)&lt;br /&gt;
         IF ~Locate THEN RETURN .&lt;br /&gt;
         into = SUB(into,1,Locate-1) &amp;amp; replace &amp;amp;SUB(into,Locate+LEN(find),LEN(into))&lt;br /&gt;
       END&lt;br /&gt;
    END&lt;br /&gt;
&lt;br /&gt;
Para llamarlo:&lt;br /&gt;
&lt;br /&gt;
Replace(&#039;Busacr&#039;,&#039;Reemplazar&#039;,Loc:miString)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Contar los threads abiertos ==&lt;br /&gt;
Este código fue posteado por Jeff Slarve&lt;br /&gt;
 ChildThreadCount Procedure!,Long&lt;br /&gt;
 Ndx   Long&lt;br /&gt;
 Count Long&lt;br /&gt;
 T     &amp;amp;Window&lt;br /&gt;
 W     &amp;amp;Window&lt;br /&gt;
  Code&lt;br /&gt;
&lt;br /&gt;
  Count = 0&lt;br /&gt;
  SetTarget&lt;br /&gt;
  T &amp;amp;= System{PROP:Target}&lt;br /&gt;
  Loop Ndx = 1 to MaxThreads&lt;br /&gt;
     SetTarget(,Ndx)&lt;br /&gt;
     W &amp;amp;= System{PROP:Target}&lt;br /&gt;
     If NOT W &amp;amp;= T&lt;br /&gt;
       Count += 1&lt;br /&gt;
     end&lt;br /&gt;
  end&lt;br /&gt;
  SetTarget&lt;br /&gt;
  Return Count&lt;br /&gt;
&lt;br /&gt;
== Permitir solo una instancia del EXE en ejecucion ==&lt;br /&gt;
En Inside the global map:&lt;br /&gt;
&lt;br /&gt;
 INCLUDE(&#039;CWUTIL.INC&#039;),ONCE&lt;br /&gt;
&lt;br /&gt;
En Global Program Setup:&lt;br /&gt;
&lt;br /&gt;
 IF NOT BeginUnique(&#039;MiAPP.exe&#039;)&lt;br /&gt;
    BEEP(BEEP:SystemExclamation)&lt;br /&gt;
    YIELD()&lt;br /&gt;
    CASE MESSAGE(&#039;El programa ya esta ejecutando..&#039;,&#039;Ho, ho...&#039;,ICON:Asterisk,BUTTON:OK,BUTTON:OK,0)&lt;br /&gt;
    OF BUTTON:OK&lt;br /&gt;
       HALT()&lt;br /&gt;
    END&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Cambiar los atributos de un archivo ==&lt;br /&gt;
(Del FAQ de SoftVelocity)&lt;br /&gt;
1. En Global Embeds&lt;br /&gt;
&lt;br /&gt;
A. Before Global Includes:&lt;br /&gt;
&lt;br /&gt;
 LPCSTR EQUATE(CSTRING)&lt;br /&gt;
 DWORD EQUATE(ULONG)&lt;br /&gt;
&lt;br /&gt;
B. Global Data: Equates de los artributos&lt;br /&gt;
&lt;br /&gt;
 FILE_ATTRIBUTE_READONLY EQUATE(00000001h)&lt;br /&gt;
 FILE_ATTRIBUTE_HIDDEN EQUATE(00000002h)&lt;br /&gt;
 FILE_ATTRIBUTE_SYSTEM EQUATE(00000004h)&lt;br /&gt;
 FILE_ATTRIBUTE_ARCHIVE EQUATE(00000020h)&lt;br /&gt;
 FILE_ATTRIBUTE_NORMAL EQUATE(00000080h)&lt;br /&gt;
 FILE_ATTRIBUTE_TEMPORARY EQUATE(00000100h)&lt;br /&gt;
&lt;br /&gt;
C. Inside Global Map:&lt;br /&gt;
&lt;br /&gt;
 Module(&#039;Win32.lib&#039;)&lt;br /&gt;
 SetFileAttributes(*CSTRING, ULONG), BOOL, RAW, PASCAL, NAME(&#039;SetFileAttributesA&#039;)&lt;br /&gt;
 GetFileAttributes(*CSTRING), ULONG, RAW, PASCAL, NAME(&#039;GetFileAttributesA&#039;)&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
2. En el codigo:&lt;br /&gt;
&lt;br /&gt;
 filename CSTRING(50)&lt;br /&gt;
&lt;br /&gt;
 filename = &#039;test.txt&#039;&lt;br /&gt;
 y# = GetFileAttributes(filename) ! Leer atributos&lt;br /&gt;
 y# = SetFileAttributes(filename, FILE_ATTRIBUTE_READONLY)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Crear directorios ==&lt;br /&gt;
Hay 2 opciones:&lt;br /&gt;
&lt;br /&gt;
1- Con Clarion (no sé si estarán estas funciones en versiones anteriores de Clarion)&lt;br /&gt;
&lt;br /&gt;
En &amp;quot;Inside the Global map&amp;quot;&lt;br /&gt;
 Include(&#039;clib.clw&#039;)&lt;br /&gt;
&lt;br /&gt;
En Local Data&lt;br /&gt;
 MiDir Cstring(256)&lt;br /&gt;
 Ret     Long&lt;br /&gt;
&lt;br /&gt;
En tu embed&lt;br /&gt;
 MiDir = &#039;Dirtest&#039;&lt;br /&gt;
 Ret = MkDir(MiDir)&lt;br /&gt;
&lt;br /&gt;
2- Con API&lt;br /&gt;
En Global Embeds - inside global map:&lt;br /&gt;
&lt;br /&gt;
 MODULE(&#039;&#039;)&lt;br /&gt;
  DirAccess(*CSTRING,SHORT=0),SHORT,RAW,NAME(&#039;_access&#039;),PROC&lt;br /&gt;
  MkDir(*CSTRING),SHORT,RAW,NAME(&#039;_mkdir&#039;),PROC&lt;br /&gt;
  DirRename(*CSTRING, *CSTRING), SHORT, RAW, NAME(&#039;_rename&#039;),PROC&lt;br /&gt;
  RmDir(*CSTRING),SHORT,RAW,NAME(&#039;_rmdir&#039;),PROC&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
Y en tu embebido podés poner lo siguiente:&lt;br /&gt;
&lt;br /&gt;
 Var=&#039;O:\test&#039;      ! Ojo tiene que ser Cstring&lt;br /&gt;
 IF DirAccess(Var)&amp;lt;&amp;gt;0        !Si no existe&lt;br /&gt;
          MkDir(Var)                    !Crearlo&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
También se puede usar DirRename y RmDir para renombrar o borrar&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Modificar la posicion de un informe ==&lt;br /&gt;
&lt;br /&gt;
Este código permite modificar la posición inicial vertical y horizontal de un informe. Es útil cuando trabajamos con un formulario preimpreso o una hoja membretada y necesitamos desplazar la impresión sin necesidad de reconfigurar el programa.&lt;br /&gt;
&lt;br /&gt;
El punto embebido donde se ubica es After Open the Report.&lt;br /&gt;
&lt;br /&gt;
 !=====================================================&lt;br /&gt;
 ! MODIFICAR LA POSICION DEL INFORME&lt;br /&gt;
 ! El pie de página modificarlo sólo si se usa: &lt;br /&gt;
 ! Por ejemplo se pone ahí el Nro de página o algo así.&lt;br /&gt;
 ! No es necesario cambiar los atributos de las bandas &lt;br /&gt;
 ! de detalle adicionales que se usan en el informe.&lt;br /&gt;
 !-----------------------------------------------------&lt;br /&gt;
 DesplazamientoX =  getini(&#039;Parametros&#039;,|&lt;br /&gt;
                    &#039;DesplazamientoX&#039;,&#039;&#039;,&#039;.\prog.ini&#039;)&lt;br /&gt;
 DesplazamientoY = getini(&#039;Parametros&#039;,|&lt;br /&gt;
                    &#039;DesplazamientoY&#039;,&#039;&#039;,&#039;.\prog.ini&#039;)&lt;br /&gt;
 &lt;br /&gt;
 if DesplazamientoX or DesplazamientoY then&lt;br /&gt;
    SETTARGET(Report)&lt;br /&gt;
    x# = report{prop:Xpos}&lt;br /&gt;
    y# = report{prop:Ypos}&lt;br /&gt;
    x# += DesplazamientoX&lt;br /&gt;
    y# += DesplazamientoY&lt;br /&gt;
    target{prop:Xpos} = x#&lt;br /&gt;
    target{prop:Ypos} = y#&lt;br /&gt;
    settarget&lt;br /&gt;
 &lt;br /&gt;
    SETTARGET(Report,?Encabezado)&lt;br /&gt;
    x# = ?Encabezado{prop:Xpos}&lt;br /&gt;
    y# = ?Encabezado{prop:Ypos}&lt;br /&gt;
    x# += DesplazamientoX&lt;br /&gt;
    y# += DesplazamientoY&lt;br /&gt;
    ?Encabezado{prop:Xpos} = x#&lt;br /&gt;
    ?Encabezado{prop:Ypos} = y#&lt;br /&gt;
    settarget&lt;br /&gt;
 &lt;br /&gt;
    SETTARGET(Report,?PiePagina)&lt;br /&gt;
    x# = ?PiePagina{prop:Xpos}&lt;br /&gt;
    y# = ?PiePagina{prop:Ypos}&lt;br /&gt;
    x# += DesplazamientoX&lt;br /&gt;
    y# += DesplazamientoY&lt;br /&gt;
    ?PiePagina{prop:Xpos} = x#&lt;br /&gt;
    ?PiePagina{prop:Ypos} = y#&lt;br /&gt;
    SETTARGET&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== Generar un informe a partir de una cola en memoria ==&lt;br /&gt;
&lt;br /&gt;
(Sin pasar por el template)&lt;br /&gt;
&lt;br /&gt;
Se puede definir en el módulo en la parte de DATA el reporte necesario y mandarlo a imprimir.&lt;br /&gt;
&lt;br /&gt;
En DATA SECTION&lt;br /&gt;
&lt;br /&gt;
 WMFQue        QUEUE&lt;br /&gt;
 PageImage       STRING(64)&lt;br /&gt;
               END&lt;br /&gt;
 ReportRunDate LONG&lt;br /&gt;
 ReportRunTime LONG&lt;br /&gt;
 !!&amp;gt; Report (portrait) &lt;br /&gt;
 Report  REPORT,AT(1000,2000,6000,7000),THOUS,PRE(RPT),FONT(&#039;Arial&#039;,10)&lt;br /&gt;
         HEADER,AT(1000,1000,6000,1000)&lt;br /&gt;
         END&lt;br /&gt;
 Detail   DETAIL&lt;br /&gt;
         END&lt;br /&gt;
         FOOTER,AT(1000,9000,6000,1000)&lt;br /&gt;
         END&lt;br /&gt;
         FORM,AT(1000,1000,6000,9000)&lt;br /&gt;
         END&lt;br /&gt;
       END&lt;br /&gt;
&lt;br /&gt;
       &lt;br /&gt;
Luego, en el botón que imprime,  poner el siguiente código:&lt;br /&gt;
&lt;br /&gt;
 ReportRunDate = today()&lt;br /&gt;
 ReportRunDate = today()&lt;br /&gt;
 ReportRunTime = clock()&lt;br /&gt;
 OPEN(Report)&lt;br /&gt;
 LOOP x# = 1 to records(ColaError)&lt;br /&gt;
    get(ColaError,x#)&lt;br /&gt;
    PRINT(rpt:DetalleUno)&lt;br /&gt;
 END&lt;br /&gt;
 ENDPAGE(Report)&lt;br /&gt;
 ReportPreview(WMFQue)&lt;br /&gt;
 IF GlobalResponse = RequestCompleted&lt;br /&gt;
    Report{PROP:FlushPreview} = True&lt;br /&gt;
 END&lt;br /&gt;
 CLOSE(Report)&lt;br /&gt;
 FREE(WMFQue)&lt;br /&gt;
&lt;br /&gt;
Este proceso usa el Print Preview de Clarion para visualizar el informe, por consiguiente, la apariencia es totalmente normal para el usuario&lt;br /&gt;
&lt;br /&gt;
== Proceso BATCH que funcione sincronizado con el TIMER de la ventana. ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En un proceso BATCH hecho a mano se puede operar en forma similar al report o al proccess, para eso hay que definir una pantalla que tenga el atributo TIMER (un valor de 1 es suficiente). El código fuente quedaría de la siguiente manera:&lt;br /&gt;
&lt;br /&gt;
 open(PantaTrabaja)&lt;br /&gt;
 Accept&lt;br /&gt;
 case event()&lt;br /&gt;
   of event:openwindow&lt;br /&gt;
      Display()&lt;br /&gt;
      ! Abrir pantalla, Abrir archivos, hacer el set &lt;br /&gt;
      ! Inicial&lt;br /&gt;
   of event:timer&lt;br /&gt;
      ?Mensaje{prop:text} = &#039;Procesando...&#039;&lt;br /&gt;
      display(?Mensaje)&lt;br /&gt;
      loop 2 times&lt;br /&gt;
         next(archivo)&lt;br /&gt;
         if errorcode() then &lt;br /&gt;
            FinArchivo = true&lt;br /&gt;
            Break&lt;br /&gt;
         end&lt;br /&gt;
         ! HACER AQUÍ ALGÚN PROCESO..&lt;br /&gt;
      end&lt;br /&gt;
   of event:closewindow&lt;br /&gt;
      Setcursor()&lt;br /&gt;
      Close(PantaTrabaja)&lt;br /&gt;
      Break&lt;br /&gt;
   End!case&lt;br /&gt;
 &lt;br /&gt;
 case field()&lt;br /&gt;
   of ?BotonCancelar&lt;br /&gt;
      if event() = event:accepted then&lt;br /&gt;
         message(&#039;Proceso cancelado por el usuario.&#039;)&lt;br /&gt;
         post(event:closewindow)&lt;br /&gt;
      end&lt;br /&gt;
   End&lt;br /&gt;
 end!accept&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Drop Combo a mano, con opción a todos y recuerdo de último elegido ==&lt;br /&gt;
&lt;br /&gt;
En muchos de mis informes, cuando pido parámetros asumo lo siguiente, por ejemplo: Si el código de cliente es igual a 0 (cero) se imprimen todos los clientes, sino, se imprime uno solo. Este código permite armar lo mismo con un DropCombo, agregando la opción TODOS LOS XXXX (código Cero).&lt;br /&gt;
&lt;br /&gt;
1. Definir una cola con la siguiente estructura ( por ejemplo ):&lt;br /&gt;
&lt;br /&gt;
 Cola     QUEUE,PRE(col)&lt;br /&gt;
 descri     STRING(20)&lt;br /&gt;
 codigo     SHORT&lt;br /&gt;
          END&lt;br /&gt;
	&lt;br /&gt;
2. Definir una variable local llamada por ejemplo&lt;br /&gt;
&lt;br /&gt;
 Combo1     STRING(20)&lt;br /&gt;
	&lt;br /&gt;
3.	En la pantalla definir una drop combo a mano, en el campo FROM poner el nombre de la COLA, y en el USE poner Combo1 ( no poner ?Combo1 )&lt;br /&gt;
	&lt;br /&gt;
4.	Cargar la cola según corresponda después de abrir archivos, poniendo como último dato el código 0 y el texto &#039;TODOS LOS REGISTROS&#039; y hacer un ADD(Cola,1) para que quede al principio.&lt;br /&gt;
	&lt;br /&gt;
5.	Definir una variable global ( o local estática) que va a guardar el resultado elegido:&lt;br /&gt;
&lt;br /&gt;
 Glo:codigo    short     &lt;br /&gt;
	&lt;br /&gt;
6.	En el evento open window, ANTES de abrir la ventana va el siguiente código&lt;br /&gt;
&lt;br /&gt;
 loop x# = 1 to records(Cola)&lt;br /&gt;
   get(Cola,x#)&lt;br /&gt;
   if col:codigo = glo:codigo then break.&lt;br /&gt;
 end&lt;br /&gt;
 Combo1 = col:descri&lt;br /&gt;
 ?Combo1{prop:selected} = x#&lt;br /&gt;
	&lt;br /&gt;
7.	Al hacer esto, el combo se abre posicionado en el último elemento elegido o en TODOS LOS REGISTROS  si es la primera vez.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== En un report, poner los totales en otra página ==&lt;br /&gt;
&lt;br /&gt;
En algún informe, se puede solicitar como parámetro la opción de imprimir los totales correspondientes en una página nueva. Para ello, después de abrir el report:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 if glo:totpag then&lt;br /&gt;
    settarget(report)&lt;br /&gt;
    ?TituloTotalCategoria{prop:pagebefore} = 1&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
== En un report, cambiar el color de un campo según alguna condición ==&lt;br /&gt;
&lt;br /&gt;
Si durante la impresión de un informe se desea cambiar algún atributo (tal como el color) de un campo puede hacer lo siguiente (antes de imprimir el detalle):&lt;br /&gt;
&lt;br /&gt;
 if cl:impotota &amp;lt;&amp;gt; cl:totacalc&lt;br /&gt;
    settarget(report)&lt;br /&gt;
    ?ARC:importe{PROP:FONTCOLOR} = COLOR:RED&lt;br /&gt;
    settarget(ProgressWindow)&lt;br /&gt;
 else&lt;br /&gt;
    settarget(report)&lt;br /&gt;
    ?ARC:importe{PROP:FONTCOLOR} = COLOR:NONE&lt;br /&gt;
    settarget(ProgressWindow)&lt;br /&gt;
 end&lt;br /&gt;
 print(rpt:Detalle)  ! Imprimir la línea de detalle&lt;br /&gt;
&lt;br /&gt;
== Cerrar todas las ventanas abiertas ==&lt;br /&gt;
 LOOP Thrd# = 2 TO 64 !1 es el Frame&lt;br /&gt;
     POST(Event:CloseWindow,,Thrd#)&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Abrir cualquier archivo con ShellExecute ==&lt;br /&gt;
Hay varios templates gratis que implementan Shellexecute, por ejemplo:&lt;br /&gt;
http://www.sterlingdata.com/shellex.htm&lt;br /&gt;
&lt;br /&gt;
Para hacerlo con codigo:&lt;br /&gt;
&lt;br /&gt;
En Global-embed &#039;Inside the Global Map&#039;:&lt;br /&gt;
 Module(&#039;Win32.lib&#039;)&lt;br /&gt;
 ShellExecute(Long,*CString,*CString,*CString,*CString,Short),UShort,PASCAL,RAW,NAME(&#039;ShellExecuteA&#039;)&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
En Local Data&lt;br /&gt;
 LOC:Handle LONG&lt;br /&gt;
 LOC:Op     CSTRING (255)&lt;br /&gt;
 LOC:File   CSTRING (255)&lt;br /&gt;
 LOC:Path   CSTRING (255)&lt;br /&gt;
 LOC:Param  CSTRING (255)&lt;br /&gt;
 LOC:Show   LONG&lt;br /&gt;
 LOC:RetHandle LONG&lt;br /&gt;
&lt;br /&gt;
En el embed&lt;br /&gt;
 LOC:Handle = 0{PROP:Handle}&lt;br /&gt;
 LOC:Op     = &#039;Open&#039;&lt;br /&gt;
 LOC:File   = &#039;C:\TEST.TXT&#039;&lt;br /&gt;
 LOC:Path   = PATH()&lt;br /&gt;
 LOC:Param  = &#039; &#039;&lt;br /&gt;
 LOC:Show   = 1&lt;br /&gt;
 LOC:RetHandle =  ShellExecute(LOC:Handle,LOC:Op,LOC:File,LOC:Param,LOC:Path,LOC:Show)&lt;br /&gt;
 If LOC:Rethandle &amp;lt;&amp;gt; 0 Then&lt;br /&gt;
   Message(&#039;Error&#039;,&#039;Error&#039;,Icon:Exclamation)&lt;br /&gt;
 End&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Abrir una pagina WEB con ShellExecute ==&lt;br /&gt;
(Ver declaraciones del API y variables en el ejemplo anterior)&lt;br /&gt;
&lt;br /&gt;
En el embed&lt;br /&gt;
 LOC:Handle = 0{PROP:Handle}&lt;br /&gt;
 LOC:Op     = &#039;Open&#039;&lt;br /&gt;
 LOC:File   = &#039;http://www.templatesclarion.com.ar&#039;&lt;br /&gt;
 LOC:Path   = &#039; &#039;&lt;br /&gt;
 LOC:Param  = &#039; &#039;&lt;br /&gt;
 LOC:Show   = 1&lt;br /&gt;
 LOC:RetHandle = ShellExecute(LOC:Handle,LOC:Op,LOC:File,LOC:Param,LOC:Path,LOC:Show)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Convertir un LONG a un String Binario ==&lt;br /&gt;
 binario=&#039;&#039; !binario es CSTRING&lt;br /&gt;
 LOOP F# = 1 TO 32&lt;br /&gt;
    IF BAND(abinario, 1) !abinario es LONG&lt;br /&gt;
        binario =  &#039;1&#039; &amp;amp; binario&lt;br /&gt;
    ELSE&lt;br /&gt;
        binario = &#039;0&#039; &amp;amp; binario&lt;br /&gt;
    END&lt;br /&gt;
    abinario = abinario / 2&lt;br /&gt;
    if abinario = 0 then break.&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Convertir un string binario a LONG ==&lt;br /&gt;
Para volver del CSTRING al LONG seria simplemente&lt;br /&gt;
 X# = EVALUATE (binario &amp;amp; &#039;b&#039;)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Copiar la QUEUE (solo lo que se esta viendo) de un browse a Excel ==&lt;br /&gt;
 Copiar=&#039;&#039; !Cstring de 4.000.000&lt;br /&gt;
 LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
 !Primero una fila con los titulos&lt;br /&gt;
     Copiar= Copiar &amp;amp; ?Browse:1{PropList:Header,C#} &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
&lt;br /&gt;
 LOOP F# = 1 TO RECORDS(BRW1.Q)&lt;br /&gt;
    LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
        GET(BRW1.Q, F#)&lt;br /&gt;
        Copiar= Copiar &amp;amp; FORMAT(WHAT(BRW1.Q, C#), ?Browse:1{PropList:Picture,C#}) &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
    END&lt;br /&gt;
    Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 SETCLIPBOARD(Copiar)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Copiar el VIEW (cuidado: se lee todo) de un browse a Excel ==&lt;br /&gt;
 Copiar=&#039;&#039; !CSTRNG de 4.000.000&lt;br /&gt;
 LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
     Copiar= Copiar &amp;amp; ?Browse:1{PropList:Header,C#} &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
 &lt;br /&gt;
 SET (BRW1.View)&lt;br /&gt;
 LOOP&lt;br /&gt;
    NEXT(BRW1.View)&lt;br /&gt;
    IF ERRORCODE() THEN BREAK.&lt;br /&gt;
    BRW1.SetQueueRecord&lt;br /&gt;
    LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
        Copiar= Copiar &amp;amp; FORMAT(WHAT(BRW1.Q, C#), ?Browse:1{PropList:Picture,C#}) &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
    END&lt;br /&gt;
    Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 SETCLIPBOARD(Copiar)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
Para que no lea todo el contenido del archivo, sino que procese estrictamente lo mismo que muestra el browse, basta con agregar la antes del BRW1.SetQueueRecord el llamado a BRW1.ValidateRecord().&lt;br /&gt;
  ...&lt;br /&gt;
  LOOP&lt;br /&gt;
    NEXT(BRW1.View)&lt;br /&gt;
    IF ERRORCODE() THEN BREAK.&lt;br /&gt;
    IF BRW1.ValidateRecord() THEN CYCLE.&lt;br /&gt;
    BRW1.SetQueueRecrod&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
Daniel Ruzo&lt;br /&gt;
&lt;br /&gt;
== Mostrar iconos en un listbox desde una queue ==&lt;br /&gt;
Ejemplo: marcar en una cola de memoria si un empleado tiene entrada y/o salida&lt;br /&gt;
&lt;br /&gt;
Primero agregar los iconos al Project&lt;br /&gt;
&lt;br /&gt;
En el Codigo:&lt;br /&gt;
&lt;br /&gt;
 cola_empleados    queue, pre(que)&lt;br /&gt;
 nombre        string(30)&lt;br /&gt;
 entro          long&lt;br /&gt;
 entro_ico    long&lt;br /&gt;
 salio          long&lt;br /&gt;
 salio_ico    long&lt;br /&gt;
                         end&lt;br /&gt;
&lt;br /&gt;
En el list:&lt;br /&gt;
&lt;br /&gt;
primer campo: nombre&lt;br /&gt;
&lt;br /&gt;
segundo campo: entro, picture @p p, iconized, transparente&lt;br /&gt;
&lt;br /&gt;
tercer campo: salio, picture @p p, iconized, transparente&lt;br /&gt;
&lt;br /&gt;
En el init de la pantalla:&lt;br /&gt;
&lt;br /&gt;
 ?list{prop:iconlist,1} = &#039;~no.ico&#039;&lt;br /&gt;
 ?list{prop:iconlist,2} = &#039;~si.ico&#039;&lt;br /&gt;
&lt;br /&gt;
Donde cargo la cola y la muestro:&lt;br /&gt;
&lt;br /&gt;
 if condicion  (si NO hay entrada)&lt;br /&gt;
     que:entro_ico = 1    ! icono de no&lt;br /&gt;
 else&lt;br /&gt;
     que:entro_ico = 2    !icono de si&lt;br /&gt;
 end&lt;br /&gt;
 if condicion  (si NO hay salida)&lt;br /&gt;
     que:salio_ico = 1    ! icono de no&lt;br /&gt;
 else&lt;br /&gt;
     que:salio_ico = 2    !icono de si&lt;br /&gt;
 end&lt;br /&gt;
 ! otras asignaciones&lt;br /&gt;
 add(cola_empleados)&lt;br /&gt;
 !&lt;br /&gt;
 display(?list)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Calcular Días Hábiles ==&lt;br /&gt;
&lt;br /&gt;
Ante todo necesitas una tabla de feriados, con al menos un campo llamado,&lt;br /&gt;
por ej., diaferiado y una clave por dicho campo.&lt;br /&gt;
&lt;br /&gt;
Luego podés hacer lo siguiente&lt;br /&gt;
&lt;br /&gt;
 habiles# = 0&lt;br /&gt;
 loop dia# = FechaInicial to FechaFinal&lt;br /&gt;
  if (Dia# % 7) = 0 then cycle. ! porque es domingo&lt;br /&gt;
  if (Dia# % 7) = 6 then cycle. ! porque es sabado&lt;br /&gt;
  !&lt;br /&gt;
  ! busco si es feriado&lt;br /&gt;
  !&lt;br /&gt;
  clear(feriado:record)&lt;br /&gt;
  feriado:diaferiado = dia#&lt;br /&gt;
  if access:feriado.fetch(Feriado:PorDia) = level:benign then cycle. !porque es feriado&lt;br /&gt;
  !&lt;br /&gt;
  habiles# += 1&lt;br /&gt;
 end!loop&lt;br /&gt;
 corridos# = fechafinal - fechainicial&lt;br /&gt;
&lt;br /&gt;
Si los cálculos que vas a realizar son muchos y continuos, sería conveniente&lt;br /&gt;
que la tabla de feriados la cargues en una queue y realices las búsquedas&lt;br /&gt;
sobre ella.&lt;br /&gt;
&lt;br /&gt;
Adrian Gallegos - Mega Sistemas S.R.L.&lt;br /&gt;
&lt;br /&gt;
Otra opción: Días Hábiles del mes - Rutina que quita los días sábados y domingos del mes&lt;br /&gt;
&lt;br /&gt;
CONTAR_DIAS ROUTINE&lt;br /&gt;
    LOC:DIAS = 0&lt;br /&gt;
    tope# = 1&lt;br /&gt;
    LOOP UNTIL DAY(LOC:FECHA) = tope#&lt;br /&gt;
    CASE  LOC:FECHA % 7&lt;br /&gt;
      of  0   ! Domingo&lt;br /&gt;
       LOC:FECHA = LOC:FECHA - 1&lt;br /&gt;
       CYCLE&lt;br /&gt;
      of  6    ! sabado&lt;br /&gt;
       LOC:FECHA = LOC:FECHA - 1&lt;br /&gt;
       CYCLE&lt;br /&gt;
      ELSE&lt;br /&gt;
       LOC:DIAS += 1&lt;br /&gt;
       LOC:FECHA = LOC:FECHA - 1&lt;br /&gt;
     END !CASE&lt;br /&gt;
   END !LOOP&lt;br /&gt;
   IF  LOC:FECHA % 7 = 0 or  LOC:FECHA % 7 = 6 THEN  !si el primero es feriado&lt;br /&gt;
         ! nada&lt;br /&gt;
          else&lt;br /&gt;
         LOC:DIAS  += 1&lt;br /&gt;
   END !IF&lt;br /&gt;
&lt;br /&gt;
Julio César Britez&lt;br /&gt;
&lt;br /&gt;
== Último día del mes y cantidad de días (Lunes, Martes, etc) entre 2 Fechas ==&lt;br /&gt;
Incluye el truco de saber el último día del mes: en la parte &amp;quot; Date(4,1,2005)-1 &amp;quot; significa que le resto 1 al primer día del mes siguiente, lo cual es una forma de obtener el último día del mes actual...&lt;br /&gt;
&lt;br /&gt;
 Loop Fecha# = Date(3,1,2005) TO (Date(4,1,2005)-1)&lt;br /&gt;
    EXECUTE (Fecha# % 7) + 1&lt;br /&gt;
          Domingo# +=1&lt;br /&gt;
          Lunes# +=1&lt;br /&gt;
          Martes# +=1&lt;br /&gt;
          Miercoles# +=1&lt;br /&gt;
          Jueves# +=1&lt;br /&gt;
          Viernes# +=1&lt;br /&gt;
          Sabado# +=1&lt;br /&gt;
    END&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Fecha en Español (por ejemplo en el Frame)==&lt;br /&gt;
Si quieres que funcione independientemente de como este configurado windows,&lt;br /&gt;
lo mejor es poner este embed, al final de WindowManager.Init&lt;br /&gt;
&lt;br /&gt;
 EXECUTE (TODAY() % 7) + 1&lt;br /&gt;
 Dia&amp;quot;= &#039;Domingo&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Lunes&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Martes&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Miercoles&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Jueves&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Viernes&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Sabado&#039;&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
 EXECUTE (MONTH(TODAY()))&lt;br /&gt;
 Mes&amp;quot; = &#039;Enero&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Febrero&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Marzo&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Abril&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Mayo&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Junio&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Julio&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Agosto&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Septiembre&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Octubre&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Noviembre&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Diciembre&#039;&lt;br /&gt;
 END&lt;br /&gt;
 AppFrame{Prop:StatusText,1} = CLIP(Dia&amp;quot;) &amp;amp; &#039; &#039; &amp;amp; DAY(TODAY()) &amp;amp; &#039; de &#039; &amp;amp;&lt;br /&gt;
 CLIP(Mes&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
 Dia&amp;quot; = Choose(((TODAY() % 7) + 1),&#039;Domingo&#039;,&#039;Lunes&#039;,&#039;Martes&#039;,&#039;Miercoles&#039;,&#039;Jueves&#039;,&#039;Viernes&#039;,&#039;Sabado&#039;)&lt;br /&gt;
&lt;br /&gt;
Javier A. Junca Barreto [http://www.sicya.com (SICyA Software - Colombia)]&lt;br /&gt;
&lt;br /&gt;
== Obtener la fecha del server ==&lt;br /&gt;
Este código lee la fecha del servidor, usando el truco de crear un archivo en el servidor y leer la fecha y hora de los atributos:&lt;br /&gt;
&lt;br /&gt;
 !Data&lt;br /&gt;
 LOC:TMP STRING(254),STATIC&lt;br /&gt;
 TMP FILE,DRIVER(&#039;Ascii&#039;),CREATE,NAME(LOC:TMP)&lt;br /&gt;
 RECORD  RECORD&lt;br /&gt;
 LIN STRING(1)&lt;br /&gt;
    .&lt;br /&gt;
    .&lt;br /&gt;
 FILS   QUEUE(File:queue),PRE(FIL)&lt;br /&gt;
       END&lt;br /&gt;
&lt;br /&gt;
 CODE&lt;br /&gt;
  LOC:TMP = PATH()&amp;amp;&#039;\TMP&#039;&amp;amp;RANDOM(10000,99999)&amp;amp;&#039;.TMP&#039;&lt;br /&gt;
  CREATE(TMP)&lt;br /&gt;
  IF NOT ERRORCODE()&lt;br /&gt;
    DIRECTORY(FILS,LOC:TMP,0)&lt;br /&gt;
    REMOVE(TMP)&lt;br /&gt;
    GET(FILS,1)&lt;br /&gt;
    IF TODAY() &amp;lt;&amp;gt; FIL:DATE OR ABS(CLOCK()-FIL:TIME) &amp;gt; 100&lt;br /&gt;
 !FECHA DIFERENTE O 1 SEGUNDO DE DESFASE&lt;br /&gt;
      SETTODAY(FIL:DATE)&lt;br /&gt;
      SETCLOCK(FIL:TIME)&lt;br /&gt;
    END&lt;br /&gt;
  ELSE&lt;br /&gt;
    REMOVE(TMP)&lt;br /&gt;
  END&lt;br /&gt;
&lt;br /&gt;
Carlos Gutierrez&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Con SQL&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La sugerencia de Carlos es muy buena. Si estás usando SQL o drivers ODBC, la otra opción es preguntarle la fecha al motor de base de datos.&lt;br /&gt;
&lt;br /&gt;
La forma genérica de hacerlo es:&lt;br /&gt;
 temp{prop:sql}=&#039;SELECT {fn curdate() }&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Con NET TIME&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Posteado por Diego Sánchez al foro.&lt;br /&gt;
 Run(&#039;NET TIME \\Server_Name /SET /Y&#039;)&lt;br /&gt;
Reemplazar  &amp;quot;Server_Name&amp;quot;  por el nombre del servidor o equipo del cual se desea obtener la hora&lt;br /&gt;
Fue posteado originalmente por un NICOLAS VEILLEUX nveilleux@nbautomation.com, en el foro comp.lang.clarion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Llamar a un Stored Procedure ==&lt;br /&gt;
El código sería mas o menos asi:&lt;br /&gt;
 L:Query = &#039;CALL NombreDelStored (&#039;&#039;&#039; &amp;amp; FORMAT(ParamFecha,@D12) &amp;amp; &#039;&#039;&#039;, &#039;&#039;&#039; &amp;amp;&lt;br /&gt;
 FORMAT(OtraFecha,@D12) &amp;amp; &#039;&#039;&#039;, &#039; &amp;amp; OtroParam1 &amp;amp;&#039;, &#039; &amp;amp; OtroParam2 &amp;amp;&#039;, &#039; &amp;amp;&lt;br /&gt;
 OtroParam3 &amp;amp;&#039;  )&#039;&lt;br /&gt;
&lt;br /&gt;
 ResSQL{prop:sql} = L:Query&lt;br /&gt;
 Loop Until Access:ResSql.Next()&lt;br /&gt;
    MiVariable = R:Campo1&lt;br /&gt;
    etc     = R:Campo2&lt;br /&gt;
 ....&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
En la tabla auxiliar ResSQL obtienes el resultado del último SELECT que&lt;br /&gt;
tenga el Stored Procedure.&lt;br /&gt;
&lt;br /&gt;
Para mas detalles ver el documento sobre SQL Embebido [http://templatesclarion.com.ar/downloads/ (Templates Clarion)]&lt;br /&gt;
&lt;br /&gt;
También recomiendo leer el help &amp;quot;MSSQL Accelerator Calling a Stored&lt;br /&gt;
Procedure&amp;quot;. Ahi está explicado además el uso de valores de retorno y parámetros de salida.&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Encriptación básica ==&lt;br /&gt;
Encriptación / desencriptación básica de un campo usando el metodo XOR.&lt;br /&gt;
 !la primera vez encripta&lt;br /&gt;
 !al volver a aplicar el algoritmo con la misma&lt;br /&gt;
 !Clave de encriptado: desencripta&lt;br /&gt;
 X# = 1&lt;br /&gt;
 loop Y# = 1 to Len(Campo)&lt;br /&gt;
   Campo [Y#] = chr(bxor(val(Campo[Y#]), val(ClaveEncriptado[X#])))&lt;br /&gt;
   X# += 1&lt;br /&gt;
   if X# &amp;gt; len (ClaveEncriptado) then X# = 1.&lt;br /&gt;
 end&lt;br /&gt;
 display&lt;br /&gt;
&lt;br /&gt;
 !Campo y ClaveEncriptado son campos CString&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Generar un archivo de Texto a máxima velocidad==&lt;br /&gt;
Estas son las APIs para generar un archivo de texto sin necesidad de declararlo en el Diccionario.&lt;br /&gt;
&lt;br /&gt;
Además es muy rápido, ideal para exportaciones.&lt;br /&gt;
&lt;br /&gt;
En Global - Inside Global map:&lt;br /&gt;
&lt;br /&gt;
 MODULE(&#039;Windows API&#039;)&lt;br /&gt;
  _lcreat(*CSTRING,SIGNED),SIGNED,PASCAL,RAW&lt;br /&gt;
  _hwrite(SIGNED,*CSTRING,LONG),LONG,PASCAL,RAW&lt;br /&gt;
  _lclose(SIGNED),SIGNED,PASCAL&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Por ejemplo para guardar el contenido de un control Text&lt;br /&gt;
&lt;br /&gt;
 IF NOT FILEDIALOG(&#039;Guardar como&#039;,FileName,&#039;Text|*.TXT|Source|*.CLW&#039;,FILE:Save + FILE:LongName)&lt;br /&gt;
    CYCLE&lt;br /&gt;
 END&lt;br /&gt;
 F# = _lcreat(FileName,0)&lt;br /&gt;
 X# = _hwrite(F#,Texto,LEN(Texto))&lt;br /&gt;
 X# = _lclose(F#)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Efecto BLINK en un campo ==&lt;br /&gt;
Tienes que crear un timer en la ventana, para eso ponle cada cuanto se va a&lt;br /&gt;
ejecutar en la propiedad timer de la ventana. Son centesimas de seg, asi que&lt;br /&gt;
si le pones 50 por ejemplo tu campo va a titilar 2 veces por segundo.&lt;br /&gt;
&lt;br /&gt;
Luego cierra la ventana, vuelve a entrar y vas a encontrar un evento timer&lt;br /&gt;
de la ventana en los embeds.&lt;br /&gt;
&lt;br /&gt;
Window Events --&amp;gt; Timer&lt;br /&gt;
 if ?campo{prop:background} = COLOR:WHITE&lt;br /&gt;
    ?campo{prop:background} = COLOR:SILVER&lt;br /&gt;
 else&lt;br /&gt;
    ?campo{prop:background} = COLOR:WHITE&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Función para calcular dígito verificador en CUIT ==&lt;br /&gt;
- La siguiente función devuelve el numero de CUIT con el dígito verificador&lt;br /&gt;
correcto.&lt;br /&gt;
- El parámetro que recibe es el numero de CUIT a revisar incluyendo el&lt;br /&gt;
dígito verificador.&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
  CuitCliente=&#039;20-15433984-6&#039;&lt;br /&gt;
  IF Cuit(CuitCliente)=CuitCliente THEN&lt;br /&gt;
    MESSAGE(&#039;Digito verificador correcto&#039;)&lt;br /&gt;
  ELSE&lt;br /&gt;
    MESSAGE(&#039;Digito verificador incorrecto&#039;)&lt;br /&gt;
  END&lt;br /&gt;
  ...&lt;br /&gt;
 &lt;br /&gt;
 Cuit         PROCEDURE(cuit1)&lt;br /&gt;
 cuit2        STRING(255)&lt;br /&gt;
 digver       LONG&lt;br /&gt;
 lon          LONG&lt;br /&gt;
 fac          LONG&lt;br /&gt;
 car          STRING(1)&lt;br /&gt;
   &lt;br /&gt;
  CODE&lt;br /&gt;
  cuit2=cuit1&lt;br /&gt;
  digver=0&lt;br /&gt;
  fac=2&lt;br /&gt;
  lon=LEN(CLIP(cuit2))&lt;br /&gt;
  LOOP i#=lon-1 TO 1 BY -1&lt;br /&gt;
    car=SUB(cuit2,i#,1)&lt;br /&gt;
    IF car&amp;lt;&#039;0&#039; OR car&amp;gt;&#039;9&#039; THEN&lt;br /&gt;
      CYCLE&lt;br /&gt;
    .&lt;br /&gt;
    digver=digver+(car*fac)&lt;br /&gt;
    fac+=1&lt;br /&gt;
    IF fac&amp;gt;7 THEN&lt;br /&gt;
      fac=2&lt;br /&gt;
    .&lt;br /&gt;
  .&lt;br /&gt;
  digver=11-(digver%11)&lt;br /&gt;
  IF digver&amp;gt;9 THEN&lt;br /&gt;
    digver=0&lt;br /&gt;
  .&lt;br /&gt;
  cuit2=SUB(cuit2,1,lon-1) &amp;amp; FORMAT(digver,@n01)&lt;br /&gt;
  RETURN(cuit2)&lt;br /&gt;
&lt;br /&gt;
Este codigo esta en la documentacion del template de Impresoras Fiscales&lt;br /&gt;
(BIGSYS TEMPLATES) del amigo Juan Carlos Rodríguez&lt;br /&gt;
&lt;br /&gt;
== Validar Email ==&lt;br /&gt;
Puedes hacerlo con MATCH, el cual devuelve 1 o 0 si el mail no es valido.&lt;br /&gt;
Ejemplo:&lt;br /&gt;
&lt;br /&gt;
 X# =  MATCH(UPPER(CLIP(locemail)),|&lt;br /&gt;
 &#039;^[-A-Z0-9._]+@{{[-A-Z0-9._]+.}+[A-Z][A-Z][A-Z]?[A-Z]?$&#039;, Match:Regular)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Restar Horas==&lt;br /&gt;
Para sacar la diferencia entre horas es simplemente:&lt;br /&gt;
 resultado = hora2  - hora + 1&lt;br /&gt;
&lt;br /&gt;
El +1 es porque sino que faltaria un segundo cuando muestres el resultado (en formato @T6, por ej)&lt;br /&gt;
&lt;br /&gt;
Si Hora2 es del dia siguiente, la cuenta seria:&lt;br /&gt;
 resultado = (hora2 +(100*60*60*24)) - hora + 1&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Digito Verificador para 5 digitos ==&lt;br /&gt;
&lt;br /&gt;
Si tenes un numero de 5 digitos y deses verificar que el mismo es ingresado correctamente podes usar este codigo que genera un digito verificador&lt;br /&gt;
&lt;br /&gt;
Crea una funcion que tenga un parametro (numero a verificar) y retorne el digito verificador correspondiente ejem: DigitoV5(LONG xNumero),BYTE&lt;br /&gt;
&lt;br /&gt;
  loc:Numero = xNumero&lt;br /&gt;
 &lt;br /&gt;
  loc:Valor = (loc:Numero[1]*5) + |&lt;br /&gt;
              (loc:Numero[2]*4) + |&lt;br /&gt;
              (loc:Numero[3]*3) + |&lt;br /&gt;
              (loc:Numero[4]*2) + |&lt;br /&gt;
              (loc:Numero[5]*7)&lt;br /&gt;
 &lt;br /&gt;
  IF (loc:Valor%5) + 1 = 0 OR (loc:Valor%5) + 1 = 1&lt;br /&gt;
    loc:Digito = 0&lt;br /&gt;
  ELSE&lt;br /&gt;
    loc:Digito = 6 - ((loc:Valor%5) + 1)&lt;br /&gt;
  END&lt;br /&gt;
 &lt;br /&gt;
  RETURN loc:Digito&lt;br /&gt;
&lt;br /&gt;
Ruben Garcia [http://www.dipsarg.com (DiPS)]&lt;br /&gt;
&lt;br /&gt;
== Digito Verificador para Cualquier Longitud ==&lt;br /&gt;
&lt;br /&gt;
Si no sabes que longitud puede tener el numero a verificar podes probar verificarla con este codigo&lt;br /&gt;
&lt;br /&gt;
Crea una funcion cuyo parametro es el numero a verificar y retorne el digito verificador. Ej. DigitoV(STRING xNumero),BYTE&lt;br /&gt;
&lt;br /&gt;
  !Inicializa&lt;br /&gt;
  loc:Numero   = xNumero&lt;br /&gt;
  loc:Valor    = 0&lt;br /&gt;
  loc:Multiplo = 1&lt;br /&gt;
 &lt;br /&gt;
  !Barrido y calculo&lt;br /&gt;
  LOOP loc:Posicion = LEN(CLIP(loc:Numero)) TO 1 BY -1&lt;br /&gt;
    loc:Multiplo += 1&lt;br /&gt;
    IF loc:Multiplo &amp;gt; 7&lt;br /&gt;
      loc:Multiplo = 2&lt;br /&gt;
    END&lt;br /&gt;
    loc:Valor += loc:Numero[loc:Posicion] * loc:Multiplo&lt;br /&gt;
  END&lt;br /&gt;
 &lt;br /&gt;
  loc:Digito = loc:Valor % 11&lt;br /&gt;
 &lt;br /&gt;
  IF loc:Digito = 10&lt;br /&gt;
    loc:Digito = 0&lt;br /&gt;
  END&lt;br /&gt;
 &lt;br /&gt;
  RETURN loc:Digito&lt;br /&gt;
&lt;br /&gt;
Ruben Garcia [http://www.dipsarg.com (DiPS)]&lt;br /&gt;
&lt;br /&gt;
== Autoincremento Manual == &lt;br /&gt;
&lt;br /&gt;
Esto se utiliza cuando tenemos una tabla en la cual queremos manejar la clave de auto incremento &lt;br /&gt;
&lt;br /&gt;
 CLEAR(MASTER)&lt;br /&gt;
 SET(MAS:ClavePorID, MAS:ClavePorID)&lt;br /&gt;
 ACCES:MASTER.Previous()&lt;br /&gt;
 IF MAS:Id = 0 THEN &lt;br /&gt;
     MAS:Id = 1&lt;br /&gt;
 ELSE &lt;br /&gt;
     MAS:Id = MAS:Id +1 &lt;br /&gt;
 END &lt;br /&gt;
&lt;br /&gt;
!!!MAS:Id TENDRÍA EL VALOR DEL PROXIMO NUMERO.- &lt;br /&gt;
&lt;br /&gt;
Gracias [mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
Nota: El mecanismo descrito es &amp;quot;confiable&amp;quot; sólo para aplicaciones que usan TPS y en modo single-user. Para Multiusuario y SQL hay que tener cuidado con las lecturas simultáneas del último valor. &lt;br /&gt;
También puede simplificarse el &amp;quot;if ... then ... else ...&amp;quot; como &amp;quot;MAS:Id = MAS:Id +1&amp;quot; (si es cero, será 1, no hace falta chequearlo)&lt;br /&gt;
&lt;br /&gt;
== Desabilitar menu desde cualquier procedimiento ==&lt;br /&gt;
Una opcion seria usando NOTIFY.&lt;br /&gt;
En el Frame&lt;br /&gt;
 Window Events&lt;br /&gt;
    Notify&lt;br /&gt;
        DISABLE = VariableGlobal&lt;br /&gt;
&lt;br /&gt;
En cualquier procedimiento que se necesite deshabilitar un menu&lt;br /&gt;
&lt;br /&gt;
 VariableGlobal = Nro de Use del Menu&lt;br /&gt;
 NOTIFY (999, 1)&lt;br /&gt;
&lt;br /&gt;
Puede ser 999 o cualquier cosa ya que no estoy usando ese parametro.&lt;br /&gt;
(bueno, en realidad podria usar ese parametro en lugar de la global...)&lt;br /&gt;
1 es el Tread del Frame&lt;br /&gt;
&lt;br /&gt;
Como desde los otros procedimientos no existen los use del los items de menu&lt;br /&gt;
(o sea ?menuitem) lo que hay que hacer es ponerles un numero a cada uno.&lt;br /&gt;
Esto se logra poniendolos de esta manera ?use, numero en la definicion del&lt;br /&gt;
menu.&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
==Formatear fechas en SQL ==&lt;br /&gt;
Les paso una función que utilizo para trabajar con las fechas en el SQL parecido al format de clarion. Es muy útil.&lt;br /&gt;
  &lt;br /&gt;
  CREATE FUNCTION dbo.FormatDateTime&lt;br /&gt;
  (&lt;br /&gt;
      @dt DATETIME,&lt;br /&gt;
      @format VARCHAR(16)&lt;br /&gt;
  )&lt;br /&gt;
  RETURNS VARCHAR(64)&lt;br /&gt;
  AS&lt;br /&gt;
  BEGIN&lt;br /&gt;
      DECLARE @dtVC VARCHAR(64)&lt;br /&gt;
      SELECT @dtVC = CASE @format&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;LONGDATE&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          DATENAME(dw, @dt)&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + DATENAME(m, @dt)&lt;br /&gt;
          + SPACE(1) + CAST(DAY(@dt) AS VARCHAR(2))&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + CAST(YEAR(@dt) AS CHAR(4))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;LONGDATEANDTIME&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          DATENAME(dw, @dt)&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + DATENAME(m, @dt)&lt;br /&gt;
          + SPACE(1) + CAST(DAY(@dt) AS VARCHAR(2))&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + CAST(YEAR(@dt) AS CHAR(4))&lt;br /&gt;
          + SPACE(1) + RIGHT(CONVERT(CHAR(20),&lt;br /&gt;
          @dt - CONVERT(DATETIME, CONVERT(CHAR(8),&lt;br /&gt;
          @dt, 112)), 22), 11)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;SHORTDATE&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LEFT(CONVERT(CHAR(19), @dt, 0), 11)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;SHORTDATEANDTIME&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(REPLACE(CONVERT(CHAR(19), @dt, 0),&lt;br /&gt;
              &#039;AM&#039;, &#039; AM&#039;), &#039;PM&#039;, &#039; PM&#039;)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;UNIXTIMESTAMP&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CAST(DATEDIFF(SECOND, &#039;19700101&#039;, @dt)&lt;br /&gt;
          AS VARCHAR(64))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YYYYMMDD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 112)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YYYY-MM-DD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(10), @dt, 23)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YYMMDD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(VARCHAR(8), @dt, 12)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YY-MM-DD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          STUFF(STUFF(CONVERT(VARCHAR(8), @dt, 12),&lt;br /&gt;
          5, 0, &#039;-&#039;), 3, 0, &#039;-&#039;)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MMDDYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(CONVERT(CHAR(8), @dt, 10), &#039;-&#039;, SPACE(0))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MM-DD-YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 10)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MM/DD/YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 1)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MM/DD/YYYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(10), @dt, 101)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DDMMYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(CONVERT(CHAR(8), @dt, 3), &#039;/&#039;, SPACE(0))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DD-MM-YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(CONVERT(CHAR(8), @dt, 3), &#039;/&#039;, &#039;-&#039;)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DD/MM/YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 3)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DD/MM/YYYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(10), @dt, 103)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM:SS 24&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 8)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM 24&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LEFT(CONVERT(VARCHAR(8), @dt, 8), 5)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM:SS 12&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LTRIM(RIGHT(CONVERT(VARCHAR(20), @dt, 22), 11))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM 12&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LTRIM(SUBSTRING(CONVERT(&lt;br /&gt;
          VARCHAR(20), @dt, 22), 10, 5)&lt;br /&gt;
          + RIGHT(CONVERT(VARCHAR(20), @dt, 22), 3))&lt;br /&gt;
  &lt;br /&gt;
      ELSE&lt;br /&gt;
  &lt;br /&gt;
          &#039;Invalid format specified&#039;&lt;br /&gt;
  &lt;br /&gt;
      END&lt;br /&gt;
      RETURN @dtVC&lt;br /&gt;
  END&lt;br /&gt;
  GO&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  Ejemplos:&lt;br /&gt;
  &lt;br /&gt;
  DECLARE @now DATETIME&lt;br /&gt;
  SET @now = GETDATE()&lt;br /&gt;
  &lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;LONGDATE&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;LONGDATEANDTIME&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;SHORTDATE&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;SHORTDATEANDTIME&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;UNIXTIMESTAMP&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YYYYMMDD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YYYY-MM-DD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YYMMDD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YY-MM-DD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MMDDYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MM-DD-YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MM/DD/YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MM/DD/YYYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DDMMYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DD-MM-YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DD/MM/YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DD/MM/YYYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM:SS 24&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM 24&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM:SS 12&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM 12&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;goofy&#039;) &lt;br /&gt;
  &lt;br /&gt;
Posteado al foro por Omar Squiabro&lt;br /&gt;
&lt;br /&gt;
== Anular Tecla Escape ==&lt;br /&gt;
&lt;br /&gt;
Hay veces que se necesita que el usuario salga de una FORM o de una ventana solo cuando pulse un determinado boton o se halla completado alguna condicion. Y en estos casos puede suceder que si el usuario pulsa la tecla ESC cause algun problema&lt;br /&gt;
&lt;br /&gt;
Primero hay que activar la alerta de la tecla esc en el INIT de la ventana&lt;br /&gt;
&lt;br /&gt;
  ALERT(EscKey)&lt;br /&gt;
&lt;br /&gt;
Despues en el evento alertkey&lt;br /&gt;
&lt;br /&gt;
  IF KEYCODE() = EscKey&lt;br /&gt;
    SELECT(?UnDeterminadoCampo)  !1= al primero&lt;br /&gt;
    RETURN Level:Notify          &lt;br /&gt;
  END&lt;br /&gt;
&lt;br /&gt;
Si el codigo embebido es puesto despues del codigo generado por clarion reemplazar el RETURN por CYCLE&lt;br /&gt;
&lt;br /&gt;
Ruben Garcia [http://www.programaya.com (DiPS)]&lt;br /&gt;
&lt;br /&gt;
== Convertir Número Hexadecimal a Binario ==&lt;br /&gt;
A veces, y sobre todo al trabajar con estados de puertos de comunicaciones, necesitamos convertir numeros hexadecimales a binario para establecer errores o tratar envios y recepciones.&lt;br /&gt;
Esta función hace justamente esto de forma sencilla.&lt;br /&gt;
&lt;br /&gt;
 HexaABinario         PROCEDURE(PAR:Nhexa)   ! (String),String&lt;br /&gt;
 HBinario   String(4),dim(16) !Equivalencias&lt;br /&gt;
 RetuBina   String(100)       !Variable Retorno&lt;br /&gt;
&lt;br /&gt;
  CODE&lt;br /&gt;
   HBinario[01] = &#039;0000&#039; !     0h&lt;br /&gt;
   HBinario[02] = &#039;0001&#039; !     1h&lt;br /&gt;
   HBinario[03] = &#039;0010&#039; !     2h&lt;br /&gt;
   HBinario[04] = &#039;0011&#039; !     3h&lt;br /&gt;
   HBinario[05] = &#039;0100&#039; !     4h&lt;br /&gt;
   HBinario[06] = &#039;0101&#039; !     5h &lt;br /&gt;
   HBinario[07] = &#039;0110&#039; !     6h&lt;br /&gt;
   HBinario[08] = &#039;0111&#039; !     7h&lt;br /&gt;
   HBinario[09] = &#039;1000&#039; !     8h &lt;br /&gt;
   HBinario[10] = &#039;1001&#039; !     9h&lt;br /&gt;
   HBinario[11] = &#039;1010&#039; !     Ah  65&lt;br /&gt;
   HBinario[12] = &#039;1011&#039; !     Bh  66&lt;br /&gt;
   HBinario[13] = &#039;1100&#039; !     Ch  67&lt;br /&gt;
   HBinario[14] = &#039;1101&#039; !     Dh  68&lt;br /&gt;
   HBinario[15] = &#039;1110&#039; !     Eh  69&lt;br /&gt;
   HBinario[16] = &#039;1111&#039; !     Fh  70&lt;br /&gt;
   CLEAR(RetuBina)&lt;br /&gt;
   PAR:NHexa = UPPER(PAR:NHexa) !Aseguro letras en mayusculas&lt;br /&gt;
   LOOP I# = 1 to LEN(CLIP(PAR:NHexa))&lt;br /&gt;
     IF NUMERIC(PAR:NHexa[I#]) THEN  !Si es numero es &amp;lt;= 9&lt;br /&gt;
        !Asigno equivalente&lt;br /&gt;
        RetuBina = CLIP(RetuBina) &amp;amp; HBinario[PAR:NHexa[I#]+1]&lt;br /&gt;
     ELSE&lt;br /&gt;
       !Asigno equivalente tomando,por ejemplo, 65 - 54 = 11 para &amp;quot;A&amp;quot;&lt;br /&gt;
        RetuBina = CLIP(RetuBina) &amp;amp; HBinario[VAL(PAR:NHexa[I#])-54]&lt;br /&gt;
     END&lt;br /&gt;
   END&lt;br /&gt;
   !Retorno la cadena binaria sin ceros a la izquierda&lt;br /&gt;
   RETURN(SUB(RetuBina, INSTRING(&#039;1&#039;,RetuBina), LEN(CLIP(RetuBina)))) &lt;br /&gt;
&lt;br /&gt;
Bueno, espero les sea de utilidad!&lt;br /&gt;
&lt;br /&gt;
Mario A. Wojcik&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Transacciones, TCF, Logout, Commit, Rollback ==&lt;br /&gt;
&lt;br /&gt;
Solucion al tema del TCF, crearlo, verlo, etc.&lt;br /&gt;
&lt;br /&gt;
Paso 1&lt;br /&gt;
En el punto embebido del FRAME (windowsManager init (priority 6100) o sea antes que se abra ningun archivo, &lt;br /&gt;
Insertar el siguiente codigo:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GLO:Datos:TCF =  &#039;TCF=&#039; &amp;amp; CLIP(GLO:WorkDir) &amp;amp; &#039;\&#039; &amp;amp; &#039;AR.TCF&#039;&lt;br /&gt;
Send(CLIENTES,GLO:Datos:TCF)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nota 1:&lt;br /&gt;
No es necesario definir en ningun lado el archivo .TCF ni su estructura porque de todo se encarga el driver.&lt;br /&gt;
Nota 2: &lt;br /&gt;
no enviar (SEND) los datos para el .TCF  desde el diccionario, según los autores, esto no funciona.&lt;br /&gt;
Nota 3: Solo es necesario un archivo .TCF para toda la aplicación. &lt;br /&gt;
&lt;br /&gt;
Listo, el archivo de control de transacciones ya esta creado y sin dudas funcionara.&lt;br /&gt;
Para que todo quede completo deberiamos hacer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LOGOUT(1,CabeceraFactura,DetalleFactura)	!Comienza la transaccion&lt;br /&gt;
DO ErrHandler 				!Chequear errores&lt;br /&gt;
ADD(CabeceraFactura) 			!Agregar registro al padre&lt;br /&gt;
DO ErrHandler 				!&lt;br /&gt;
LOOP X# = 1 TO RECORDS(DetailQue) 	!Procesar los registros almacenados&lt;br /&gt;
GET(DetailQue,X#) 				!traer uno de la Cola&lt;br /&gt;
DO ErrHandler 				!&lt;br /&gt;
Det:Record = DetailQue 			!Asignar al record buffer&lt;br /&gt;
ADD(DetalleFactura) 			!Hacer el Add al archivo&lt;br /&gt;
DO ErrHandler 				!&lt;br /&gt;
END&lt;br /&gt;
COMMIT 					!Terminar la transaccion, todo OK&lt;br /&gt;
ASSERT(~ERRORCODE())&lt;br /&gt;
&lt;br /&gt;
ErrHandler ROUTINE 				!Runina de manejo de errores&lt;br /&gt;
IF NOT ERRORCODE() THEN EXIT. 		!Sale si no hay errores&lt;br /&gt;
Err&amp;quot; = ERROR() 				!Guarda el mensaje de error&lt;br /&gt;
ROLLBACK 					!Anula la transaccion&lt;br /&gt;
ASSERT(~ERRORCODE())&lt;br /&gt;
BEEP 						!Avisa al usuario&lt;br /&gt;
MESSAGE(&#039;Transaction Error - &#039; &amp;amp; Err&amp;quot;)&lt;br /&gt;
RETURN 					&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Lo anterior es copia de la ayuda del manual de Clarion 6.3&lt;br /&gt;
&lt;br /&gt;
Para verificar que exista realmente el archivo .TCF podemos usar en cualquier punto de la aplicación lo siguiente:&lt;br /&gt;
&lt;br /&gt;
Poner un string variable en pantalla y en un boton:&lt;br /&gt;
&lt;br /&gt;
Loc:Tcf = SEND(CLIENTES, &#039;TCF&#039;)&lt;br /&gt;
DISPLAY()&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Eso deberia ser todo. Por lo menos para mi funciona.&lt;br /&gt;
&lt;br /&gt;
Carlos Barroso&lt;br /&gt;
DBA Oracle&lt;br /&gt;
Programador Clarion &amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Carlos Barroso</name></author>
	</entry>
	<entry>
		<id>https://clarionwiki.com.ar/index.php?title=Codigo_Util&amp;diff=87</id>
		<title>Codigo Util</title>
		<link rel="alternate" type="text/html" href="https://clarionwiki.com.ar/index.php?title=Codigo_Util&amp;diff=87"/>
		<updated>2015-12-15T15:24:51Z</updated>

		<summary type="html">&lt;p&gt;Carlos Barroso: /* Transacciones, TCF, Logout, Commit, Rollback */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;En esta sección se incluyen rutinas de código que nos resultaron útiles en más de una ocasión.&lt;br /&gt;
&lt;br /&gt;
== PDF con cualquier versión de Clarion ==&lt;br /&gt;
Después de mucho rebuscar algo gratis que haga esto, hoy pude componer algo:&lt;br /&gt;
1º) Aporte de Fernando Cerini para &amp;quot;capturar&amp;quot; los .wmf de los reportes de Clarion:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En el embed PrintPreview.Open, despues de Parent call&lt;br /&gt;
 &lt;br /&gt;
 get(SELF.ImageQueue,POINTER(SELF.ImageQueue))&lt;br /&gt;
 LOOP a# = 1 to RECORDS(SELF.ImageQueue)&lt;br /&gt;
          get(SELF.ImageQueue,a#)&lt;br /&gt;
          COPY(SELF.ImageQueue, &#039;c:\temp\Pagina&#039; &amp;amp; a# &amp;amp;&#039;.WMF&#039;)&lt;br /&gt;
          IF ERRORCODE() THEN MESSAGE(ERROR()).&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
En mi caso, con Clarion 5.0 ABC el embed point fue Previewer.Open, después del Parent call&lt;br /&gt;
&lt;br /&gt;
2º) Instalar OpenOffice y seguir las instrucciones de&lt;br /&gt;
http://www.xml.com/pub/a/2006/01/11/from-microsoft-to-openoffice.html&lt;br /&gt;
para crear la macro.&lt;br /&gt;
La verdad, no pude cambiar el nombre de Module1 a Conversiones o algo así, pero no importó mucho.&lt;br /&gt;
&lt;br /&gt;
3º) Cree un batch con la línea&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;C:\Archivos de Programa\OpenOffice.org 2.3\program\soffice&amp;quot; -invisible macro:///Standard.Module1.SaveAsPDF(c:\temp\pagina8.wmf)&lt;br /&gt;
&lt;br /&gt;
Es lo que dice el instructivo mencionado en el punto 2º) acomodado a mi instalación de OpenOffice, lo corrí y funcionó a la primera prueba.&lt;br /&gt;
&lt;br /&gt;
4º) Sólo queda hacer la llamada mediante RUN en el código de Clarion y santo remedio.&lt;br /&gt;
&lt;br /&gt;
Espero sea de ayuda para alguien.&lt;br /&gt;
Saludos&lt;br /&gt;
Ezequiel&lt;br /&gt;
&lt;br /&gt;
== Como filtrar un Tree ==&lt;br /&gt;
Una tecnica de Saul Perez Quezada para filtrar Arboles (generados con el template Relacion Tree), con filtros en runtime que son complejos, y que no podemos hacer con un simple SetFilter().&lt;br /&gt;
&lt;br /&gt;
Alguien ha posteado una pregunta sobre como Filtrar arboles, yo en lo personal he dejado de usar arboles, por que son bastante lentos y deficientes, pero en algunas aplicaciones es bastante util, de manera que comparto la experiencia y les subo una pequeña explicacion de como filtrar Arboles (generados con el template Relacion Tree), con filtros en runtime que son complejos, y que no podemos hacer con un simple SetFilter(), ya que no existe esto en la propiedad.&lt;br /&gt;
 &lt;br /&gt;
Saludos y espero les sirva.&lt;br /&gt;
 &lt;br /&gt;
Como filtrar un Tree con solamente tablas:&lt;br /&gt;
 &lt;br /&gt;
Dentro del Template para armar los arboles, tiene dentro de Primary y Secondary files la opcion de filtrar, agregando un filtro, pero muchas veces dicho filtro no es tan simple de armar, o necesitamos estarlo cambiando en runtime.&lt;br /&gt;
 &lt;br /&gt;
Aqui les presento un caso y como su solucion, esperando que les sirva...&lt;br /&gt;
 &lt;br /&gt;
Tengo tres tablas:&lt;br /&gt;
 &lt;br /&gt;
Abuelos&lt;br /&gt;
Padres&lt;br /&gt;
Hijos&lt;br /&gt;
 &lt;br /&gt;
Y en la tabla hijos, tengo un campo que define en que nivel de escuela en que va cada hijo: Pre-escolar, Primaria, Secundaria, Bachillerato, Universidad, PostGrado, Maestria y Doctorado.&lt;br /&gt;
 &lt;br /&gt;
En la ventana requiero que el usuario pueda filtrar por el nivel escolar, pero con las siguientes condiciones:&lt;br /&gt;
 &lt;br /&gt;
1.- El usuario puede seleccionar si quiere ver cualquier combinacion de estudios, es decir, que pueda ver los de primaria y universidad solamente o todos, o ninguno...&lt;br /&gt;
2.- Si selecciona un filtro donde resulte que un abuelo o padre no tiene hijos que cumplan la condicion, entonces no debe mostrarnos a esos abuelos y padres...&lt;br /&gt;
 &lt;br /&gt;
Considerando que el Tree template, muestra siempre los niveles de acuerdo al filtro que se arma, pues podria convertirse algo engorroso, estar cambiando el filtro, ya que no es una propiedad del relacion tree, tal como sucede con el browse class, sino que el tree filtra a base de If&#039;s interpuestos al llenar el queue...&lt;br /&gt;
 &lt;br /&gt;
Usando el template y solo poniendo los filtros de NIVELESCOLAR = LOC:MINIVEL, solo mostraria los hijos de un nivel, asi que este metodo no nos servira...&lt;br /&gt;
 &lt;br /&gt;
La idea seria entonces que como filtro usaremos una condicion matematica, si la condicion es Verdadera, entonces mostrara el hijo, de lo contrario no lo mostrara.&lt;br /&gt;
 &lt;br /&gt;
Asi que con el comando EVALUATE, evaluaremos un filtro, dentro del secondary files, opcion filter, si el filtro es bueno (igual a 1), entonces nos mostrara la info de lo contrario no...&lt;br /&gt;
 &lt;br /&gt;
Por ejemplo:&lt;br /&gt;
 &lt;br /&gt;
En el secondary files en buscamos la tabla de Hijos y le escribimos en el filtro:&lt;br /&gt;
 &lt;br /&gt;
0&amp;lt;EVALUATE(Filtro) ! Si el filtro es valido, entonces nos mostrara el resultado, de lo contrario no...&lt;br /&gt;
 &lt;br /&gt;
Declaramos un check box por cada cada condicion, con su respectiva variable: Preescolar, Primaria, Secundaria, etc... que sea tipo byte, con valor true = 1 y valor false = 0&lt;br /&gt;
 &lt;br /&gt;
! En el acepted del check box Preescolar... y cada condicion...&lt;br /&gt;
Do Filtrar&lt;br /&gt;
Display&lt;br /&gt;
 &lt;br /&gt;
! En procedure routines&lt;br /&gt;
Filtrar Routine&lt;br /&gt;
  Clear(Filtro) ! Variable para filtrar&lt;br /&gt;
 &lt;br /&gt;
  If Preescolar Then Filtro = &#039;HIJ:NIVELESCOLAR=&#039;&#039;Preescolar&#039;&#039;&#039; ..&lt;br /&gt;
  &lt;br /&gt;
  If Filtro And Primaria&lt;br /&gt;
    Filtro = Clip(Filtro) &amp;amp; &#039; And HIJ:NIVELESCOLAR=&#039;&#039;Primaria&#039;&#039;&#039;&lt;br /&gt;
  ElsIf Filtro And Primaria&lt;br /&gt;
    Filtro = &#039;HIJ:NIVELESCOLAR=&#039;&#039;Primaria&#039;&#039;&#039;&lt;br /&gt;
  End&lt;br /&gt;
  ! Esto se repite con todas las condiciones...&lt;br /&gt;
  ...&lt;br /&gt;
  ...&lt;br /&gt;
  &lt;br /&gt;
  ! Despues de armar el filtro, refrescar el browse...&lt;br /&gt;
 &lt;br /&gt;
  DO REL4::RefreshTree ! Esta rutina la genera el template, se debe buscar en los modulos su nombre correcto.&lt;br /&gt;
  POST(EVENT:NewSelection,?RelTree)&lt;br /&gt;
 &lt;br /&gt;
 Exit  &lt;br /&gt;
 &lt;br /&gt;
Hasta ahora, lo que se ha logrado es que cuando el usuario seleccione un tipo nivel escolar, nos mostrara o no los hijos, pero resulta, que ahora lo que necesito es que solo me muestre los abuelos que tienen nietos, y los padres que tienen hijos, es entonces por que no tiene caso que el arbol se llene con Mil abuelos, si solo 30 de ellos tienen nietos...&lt;br /&gt;
 &lt;br /&gt;
Para hacer debemos entender como se llena el queue del tree, basicamente, el tree, tantas vueltas como combinaciones de nuestra estructura tengamos, es decir, suponiendo que tenemos 3 abuelos, 3 padres, con 3 hijos cada uno, lo que hace el template es:&lt;br /&gt;
 &lt;br /&gt;
Por cada Abuelo Barre (con Loop) la tabla Padres por completo 1 vez y selecciono los que me corresponden y por cada padre barro la tabla hijos (con Loop) por completo y selecciono los que me corresponden...&lt;br /&gt;
 &lt;br /&gt;
Entonces esta estructura daria 3 x 3 x 3 = 27 vueltas. (Ahora imaginen lo que tarda en entre tablas de 10000 registros...)&lt;br /&gt;
 &lt;br /&gt;
Bueno, siguiendo esta idea, entonces lo que necesitamos, es interceptar cada vuelta en el punto donde no queremos que se ingrese al queue y hacer un break.&lt;br /&gt;
 &lt;br /&gt;
Conociendo esto buscamos en nuestros embeds, embedido llamado:&lt;br /&gt;
 &lt;br /&gt;
RelacionTree After Next Primary File y RelationTree After Next Secondary File...&lt;br /&gt;
 &lt;br /&gt;
Aqui buscamos el archivo que requerimos excluir en algunos casos, que es Padres e Hijos, le escribimos en el caso de Padres:&lt;br /&gt;
 &lt;br /&gt;
PAD:AbueloId = ABU:AbueloId&lt;br /&gt;
If Access:Padres.Fetch(FK AbueloId)  ! Si no existen padres que sean dependientes de este abuelo&lt;br /&gt;
  Break          ! hacer el break al Loop del armado del tree...&lt;br /&gt;
End&lt;br /&gt;
 &lt;br /&gt;
Y el caso de los hijos repetimos la condicion:&lt;br /&gt;
 &lt;br /&gt;
HIJ:PadreId = PAD:PadreId&lt;br /&gt;
If Access:Hijos.Fetch(FK PadreId)  ! Si no existen hijos que sean dependientes de este abuelo&lt;br /&gt;
  Break          ! hacer el break al Loop del armado del tree...&lt;br /&gt;
End&lt;br /&gt;
 &lt;br /&gt;
El efecto sera que no nos mostrara aquellos padres y abuelos que no tengan hijos.&lt;br /&gt;
 &lt;br /&gt;
Si requerimos algo mas complejo podemos usar una vista, en vez de una Fetch a una tabla, pero obviamente se alentara esto cada vez mas y mas al llenar el Arbol.&lt;br /&gt;
 &lt;br /&gt;
Nota&lt;br /&gt;
Si usamos un motor de SQL, una alternativa mucho mas eficiente es declarar un Vista en nuestro motor y diccionario que sirva de base para llenar el Arbol, con esto aumentaria considerablemente la velocidad.&lt;br /&gt;
&lt;br /&gt;
== Reemplazar caracteres en un string ==&lt;br /&gt;
Una rutina generica para reemplazar todas las instancias de un string dentro de otro&lt;br /&gt;
&lt;br /&gt;
 Replace       PROCEDURE(string find,string replace,*cstring into)&lt;br /&gt;
 Locate LONG,AUTO&lt;br /&gt;
  CODE&lt;br /&gt;
    IF UPPER(find)&amp;lt;&amp;gt;UPPER(replace)&lt;br /&gt;
       LOOP&lt;br /&gt;
         Locate = INSTRING(UPPER(find),UPPER(into),1,1)&lt;br /&gt;
         IF ~Locate THEN RETURN .&lt;br /&gt;
         into = SUB(into,1,Locate-1) &amp;amp; replace &amp;amp;SUB(into,Locate+LEN(find),LEN(into))&lt;br /&gt;
       END&lt;br /&gt;
    END&lt;br /&gt;
&lt;br /&gt;
Para llamarlo:&lt;br /&gt;
&lt;br /&gt;
Replace(&#039;Busacr&#039;,&#039;Reemplazar&#039;,Loc:miString)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Contar los threads abiertos ==&lt;br /&gt;
Este código fue posteado por Jeff Slarve&lt;br /&gt;
 ChildThreadCount Procedure!,Long&lt;br /&gt;
 Ndx   Long&lt;br /&gt;
 Count Long&lt;br /&gt;
 T     &amp;amp;Window&lt;br /&gt;
 W     &amp;amp;Window&lt;br /&gt;
  Code&lt;br /&gt;
&lt;br /&gt;
  Count = 0&lt;br /&gt;
  SetTarget&lt;br /&gt;
  T &amp;amp;= System{PROP:Target}&lt;br /&gt;
  Loop Ndx = 1 to MaxThreads&lt;br /&gt;
     SetTarget(,Ndx)&lt;br /&gt;
     W &amp;amp;= System{PROP:Target}&lt;br /&gt;
     If NOT W &amp;amp;= T&lt;br /&gt;
       Count += 1&lt;br /&gt;
     end&lt;br /&gt;
  end&lt;br /&gt;
  SetTarget&lt;br /&gt;
  Return Count&lt;br /&gt;
&lt;br /&gt;
== Permitir solo una instancia del EXE en ejecucion ==&lt;br /&gt;
En Inside the global map:&lt;br /&gt;
&lt;br /&gt;
 INCLUDE(&#039;CWUTIL.INC&#039;),ONCE&lt;br /&gt;
&lt;br /&gt;
En Global Program Setup:&lt;br /&gt;
&lt;br /&gt;
 IF NOT BeginUnique(&#039;MiAPP.exe&#039;)&lt;br /&gt;
    BEEP(BEEP:SystemExclamation)&lt;br /&gt;
    YIELD()&lt;br /&gt;
    CASE MESSAGE(&#039;El programa ya esta ejecutando..&#039;,&#039;Ho, ho...&#039;,ICON:Asterisk,BUTTON:OK,BUTTON:OK,0)&lt;br /&gt;
    OF BUTTON:OK&lt;br /&gt;
       HALT()&lt;br /&gt;
    END&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Cambiar los atributos de un archivo ==&lt;br /&gt;
(Del FAQ de SoftVelocity)&lt;br /&gt;
1. En Global Embeds&lt;br /&gt;
&lt;br /&gt;
A. Before Global Includes:&lt;br /&gt;
&lt;br /&gt;
 LPCSTR EQUATE(CSTRING)&lt;br /&gt;
 DWORD EQUATE(ULONG)&lt;br /&gt;
&lt;br /&gt;
B. Global Data: Equates de los artributos&lt;br /&gt;
&lt;br /&gt;
 FILE_ATTRIBUTE_READONLY EQUATE(00000001h)&lt;br /&gt;
 FILE_ATTRIBUTE_HIDDEN EQUATE(00000002h)&lt;br /&gt;
 FILE_ATTRIBUTE_SYSTEM EQUATE(00000004h)&lt;br /&gt;
 FILE_ATTRIBUTE_ARCHIVE EQUATE(00000020h)&lt;br /&gt;
 FILE_ATTRIBUTE_NORMAL EQUATE(00000080h)&lt;br /&gt;
 FILE_ATTRIBUTE_TEMPORARY EQUATE(00000100h)&lt;br /&gt;
&lt;br /&gt;
C. Inside Global Map:&lt;br /&gt;
&lt;br /&gt;
 Module(&#039;Win32.lib&#039;)&lt;br /&gt;
 SetFileAttributes(*CSTRING, ULONG), BOOL, RAW, PASCAL, NAME(&#039;SetFileAttributesA&#039;)&lt;br /&gt;
 GetFileAttributes(*CSTRING), ULONG, RAW, PASCAL, NAME(&#039;GetFileAttributesA&#039;)&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
2. En el codigo:&lt;br /&gt;
&lt;br /&gt;
 filename CSTRING(50)&lt;br /&gt;
&lt;br /&gt;
 filename = &#039;test.txt&#039;&lt;br /&gt;
 y# = GetFileAttributes(filename) ! Leer atributos&lt;br /&gt;
 y# = SetFileAttributes(filename, FILE_ATTRIBUTE_READONLY)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Crear directorios ==&lt;br /&gt;
Hay 2 opciones:&lt;br /&gt;
&lt;br /&gt;
1- Con Clarion (no sé si estarán estas funciones en versiones anteriores de Clarion)&lt;br /&gt;
&lt;br /&gt;
En &amp;quot;Inside the Global map&amp;quot;&lt;br /&gt;
 Include(&#039;clib.clw&#039;)&lt;br /&gt;
&lt;br /&gt;
En Local Data&lt;br /&gt;
 MiDir Cstring(256)&lt;br /&gt;
 Ret     Long&lt;br /&gt;
&lt;br /&gt;
En tu embed&lt;br /&gt;
 MiDir = &#039;Dirtest&#039;&lt;br /&gt;
 Ret = MkDir(MiDir)&lt;br /&gt;
&lt;br /&gt;
2- Con API&lt;br /&gt;
En Global Embeds - inside global map:&lt;br /&gt;
&lt;br /&gt;
 MODULE(&#039;&#039;)&lt;br /&gt;
  DirAccess(*CSTRING,SHORT=0),SHORT,RAW,NAME(&#039;_access&#039;),PROC&lt;br /&gt;
  MkDir(*CSTRING),SHORT,RAW,NAME(&#039;_mkdir&#039;),PROC&lt;br /&gt;
  DirRename(*CSTRING, *CSTRING), SHORT, RAW, NAME(&#039;_rename&#039;),PROC&lt;br /&gt;
  RmDir(*CSTRING),SHORT,RAW,NAME(&#039;_rmdir&#039;),PROC&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
Y en tu embebido podés poner lo siguiente:&lt;br /&gt;
&lt;br /&gt;
 Var=&#039;O:\test&#039;      ! Ojo tiene que ser Cstring&lt;br /&gt;
 IF DirAccess(Var)&amp;lt;&amp;gt;0        !Si no existe&lt;br /&gt;
          MkDir(Var)                    !Crearlo&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
También se puede usar DirRename y RmDir para renombrar o borrar&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Modificar la posicion de un informe ==&lt;br /&gt;
&lt;br /&gt;
Este código permite modificar la posición inicial vertical y horizontal de un informe. Es útil cuando trabajamos con un formulario preimpreso o una hoja membretada y necesitamos desplazar la impresión sin necesidad de reconfigurar el programa.&lt;br /&gt;
&lt;br /&gt;
El punto embebido donde se ubica es After Open the Report.&lt;br /&gt;
&lt;br /&gt;
 !=====================================================&lt;br /&gt;
 ! MODIFICAR LA POSICION DEL INFORME&lt;br /&gt;
 ! El pie de página modificarlo sólo si se usa: &lt;br /&gt;
 ! Por ejemplo se pone ahí el Nro de página o algo así.&lt;br /&gt;
 ! No es necesario cambiar los atributos de las bandas &lt;br /&gt;
 ! de detalle adicionales que se usan en el informe.&lt;br /&gt;
 !-----------------------------------------------------&lt;br /&gt;
 DesplazamientoX =  getini(&#039;Parametros&#039;,|&lt;br /&gt;
                    &#039;DesplazamientoX&#039;,&#039;&#039;,&#039;.\prog.ini&#039;)&lt;br /&gt;
 DesplazamientoY = getini(&#039;Parametros&#039;,|&lt;br /&gt;
                    &#039;DesplazamientoY&#039;,&#039;&#039;,&#039;.\prog.ini&#039;)&lt;br /&gt;
 &lt;br /&gt;
 if DesplazamientoX or DesplazamientoY then&lt;br /&gt;
    SETTARGET(Report)&lt;br /&gt;
    x# = report{prop:Xpos}&lt;br /&gt;
    y# = report{prop:Ypos}&lt;br /&gt;
    x# += DesplazamientoX&lt;br /&gt;
    y# += DesplazamientoY&lt;br /&gt;
    target{prop:Xpos} = x#&lt;br /&gt;
    target{prop:Ypos} = y#&lt;br /&gt;
    settarget&lt;br /&gt;
 &lt;br /&gt;
    SETTARGET(Report,?Encabezado)&lt;br /&gt;
    x# = ?Encabezado{prop:Xpos}&lt;br /&gt;
    y# = ?Encabezado{prop:Ypos}&lt;br /&gt;
    x# += DesplazamientoX&lt;br /&gt;
    y# += DesplazamientoY&lt;br /&gt;
    ?Encabezado{prop:Xpos} = x#&lt;br /&gt;
    ?Encabezado{prop:Ypos} = y#&lt;br /&gt;
    settarget&lt;br /&gt;
 &lt;br /&gt;
    SETTARGET(Report,?PiePagina)&lt;br /&gt;
    x# = ?PiePagina{prop:Xpos}&lt;br /&gt;
    y# = ?PiePagina{prop:Ypos}&lt;br /&gt;
    x# += DesplazamientoX&lt;br /&gt;
    y# += DesplazamientoY&lt;br /&gt;
    ?PiePagina{prop:Xpos} = x#&lt;br /&gt;
    ?PiePagina{prop:Ypos} = y#&lt;br /&gt;
    SETTARGET&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== Generar un informe a partir de una cola en memoria ==&lt;br /&gt;
&lt;br /&gt;
(Sin pasar por el template)&lt;br /&gt;
&lt;br /&gt;
Se puede definir en el módulo en la parte de DATA el reporte necesario y mandarlo a imprimir.&lt;br /&gt;
&lt;br /&gt;
En DATA SECTION&lt;br /&gt;
&lt;br /&gt;
 WMFQue        QUEUE&lt;br /&gt;
 PageImage       STRING(64)&lt;br /&gt;
               END&lt;br /&gt;
 ReportRunDate LONG&lt;br /&gt;
 ReportRunTime LONG&lt;br /&gt;
 !!&amp;gt; Report (portrait) &lt;br /&gt;
 Report  REPORT,AT(1000,2000,6000,7000),THOUS,PRE(RPT),FONT(&#039;Arial&#039;,10)&lt;br /&gt;
         HEADER,AT(1000,1000,6000,1000)&lt;br /&gt;
         END&lt;br /&gt;
 Detail   DETAIL&lt;br /&gt;
         END&lt;br /&gt;
         FOOTER,AT(1000,9000,6000,1000)&lt;br /&gt;
         END&lt;br /&gt;
         FORM,AT(1000,1000,6000,9000)&lt;br /&gt;
         END&lt;br /&gt;
       END&lt;br /&gt;
&lt;br /&gt;
       &lt;br /&gt;
Luego, en el botón que imprime,  poner el siguiente código:&lt;br /&gt;
&lt;br /&gt;
 ReportRunDate = today()&lt;br /&gt;
 ReportRunDate = today()&lt;br /&gt;
 ReportRunTime = clock()&lt;br /&gt;
 OPEN(Report)&lt;br /&gt;
 LOOP x# = 1 to records(ColaError)&lt;br /&gt;
    get(ColaError,x#)&lt;br /&gt;
    PRINT(rpt:DetalleUno)&lt;br /&gt;
 END&lt;br /&gt;
 ENDPAGE(Report)&lt;br /&gt;
 ReportPreview(WMFQue)&lt;br /&gt;
 IF GlobalResponse = RequestCompleted&lt;br /&gt;
    Report{PROP:FlushPreview} = True&lt;br /&gt;
 END&lt;br /&gt;
 CLOSE(Report)&lt;br /&gt;
 FREE(WMFQue)&lt;br /&gt;
&lt;br /&gt;
Este proceso usa el Print Preview de Clarion para visualizar el informe, por consiguiente, la apariencia es totalmente normal para el usuario&lt;br /&gt;
&lt;br /&gt;
== Proceso BATCH que funcione sincronizado con el TIMER de la ventana. ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En un proceso BATCH hecho a mano se puede operar en forma similar al report o al proccess, para eso hay que definir una pantalla que tenga el atributo TIMER (un valor de 1 es suficiente). El código fuente quedaría de la siguiente manera:&lt;br /&gt;
&lt;br /&gt;
 open(PantaTrabaja)&lt;br /&gt;
 Accept&lt;br /&gt;
 case event()&lt;br /&gt;
   of event:openwindow&lt;br /&gt;
      Display()&lt;br /&gt;
      ! Abrir pantalla, Abrir archivos, hacer el set &lt;br /&gt;
      ! Inicial&lt;br /&gt;
   of event:timer&lt;br /&gt;
      ?Mensaje{prop:text} = &#039;Procesando...&#039;&lt;br /&gt;
      display(?Mensaje)&lt;br /&gt;
      loop 2 times&lt;br /&gt;
         next(archivo)&lt;br /&gt;
         if errorcode() then &lt;br /&gt;
            FinArchivo = true&lt;br /&gt;
            Break&lt;br /&gt;
         end&lt;br /&gt;
         ! HACER AQUÍ ALGÚN PROCESO..&lt;br /&gt;
      end&lt;br /&gt;
   of event:closewindow&lt;br /&gt;
      Setcursor()&lt;br /&gt;
      Close(PantaTrabaja)&lt;br /&gt;
      Break&lt;br /&gt;
   End!case&lt;br /&gt;
 &lt;br /&gt;
 case field()&lt;br /&gt;
   of ?BotonCancelar&lt;br /&gt;
      if event() = event:accepted then&lt;br /&gt;
         message(&#039;Proceso cancelado por el usuario.&#039;)&lt;br /&gt;
         post(event:closewindow)&lt;br /&gt;
      end&lt;br /&gt;
   End&lt;br /&gt;
 end!accept&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Drop Combo a mano, con opción a todos y recuerdo de último elegido ==&lt;br /&gt;
&lt;br /&gt;
En muchos de mis informes, cuando pido parámetros asumo lo siguiente, por ejemplo: Si el código de cliente es igual a 0 (cero) se imprimen todos los clientes, sino, se imprime uno solo. Este código permite armar lo mismo con un DropCombo, agregando la opción TODOS LOS XXXX (código Cero).&lt;br /&gt;
&lt;br /&gt;
1. Definir una cola con la siguiente estructura ( por ejemplo ):&lt;br /&gt;
&lt;br /&gt;
 Cola     QUEUE,PRE(col)&lt;br /&gt;
 descri     STRING(20)&lt;br /&gt;
 codigo     SHORT&lt;br /&gt;
          END&lt;br /&gt;
	&lt;br /&gt;
2. Definir una variable local llamada por ejemplo&lt;br /&gt;
&lt;br /&gt;
 Combo1     STRING(20)&lt;br /&gt;
	&lt;br /&gt;
3.	En la pantalla definir una drop combo a mano, en el campo FROM poner el nombre de la COLA, y en el USE poner Combo1 ( no poner ?Combo1 )&lt;br /&gt;
	&lt;br /&gt;
4.	Cargar la cola según corresponda después de abrir archivos, poniendo como último dato el código 0 y el texto &#039;TODOS LOS REGISTROS&#039; y hacer un ADD(Cola,1) para que quede al principio.&lt;br /&gt;
	&lt;br /&gt;
5.	Definir una variable global ( o local estática) que va a guardar el resultado elegido:&lt;br /&gt;
&lt;br /&gt;
 Glo:codigo    short     &lt;br /&gt;
	&lt;br /&gt;
6.	En el evento open window, ANTES de abrir la ventana va el siguiente código&lt;br /&gt;
&lt;br /&gt;
 loop x# = 1 to records(Cola)&lt;br /&gt;
   get(Cola,x#)&lt;br /&gt;
   if col:codigo = glo:codigo then break.&lt;br /&gt;
 end&lt;br /&gt;
 Combo1 = col:descri&lt;br /&gt;
 ?Combo1{prop:selected} = x#&lt;br /&gt;
	&lt;br /&gt;
7.	Al hacer esto, el combo se abre posicionado en el último elemento elegido o en TODOS LOS REGISTROS  si es la primera vez.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== En un report, poner los totales en otra página ==&lt;br /&gt;
&lt;br /&gt;
En algún informe, se puede solicitar como parámetro la opción de imprimir los totales correspondientes en una página nueva. Para ello, después de abrir el report:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 if glo:totpag then&lt;br /&gt;
    settarget(report)&lt;br /&gt;
    ?TituloTotalCategoria{prop:pagebefore} = 1&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
== En un report, cambiar el color de un campo según alguna condición ==&lt;br /&gt;
&lt;br /&gt;
Si durante la impresión de un informe se desea cambiar algún atributo (tal como el color) de un campo puede hacer lo siguiente (antes de imprimir el detalle):&lt;br /&gt;
&lt;br /&gt;
 if cl:impotota &amp;lt;&amp;gt; cl:totacalc&lt;br /&gt;
    settarget(report)&lt;br /&gt;
    ?ARC:importe{PROP:FONTCOLOR} = COLOR:RED&lt;br /&gt;
    settarget(ProgressWindow)&lt;br /&gt;
 else&lt;br /&gt;
    settarget(report)&lt;br /&gt;
    ?ARC:importe{PROP:FONTCOLOR} = COLOR:NONE&lt;br /&gt;
    settarget(ProgressWindow)&lt;br /&gt;
 end&lt;br /&gt;
 print(rpt:Detalle)  ! Imprimir la línea de detalle&lt;br /&gt;
&lt;br /&gt;
== Cerrar todas las ventanas abiertas ==&lt;br /&gt;
 LOOP Thrd# = 2 TO 64 !1 es el Frame&lt;br /&gt;
     POST(Event:CloseWindow,,Thrd#)&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Abrir cualquier archivo con ShellExecute ==&lt;br /&gt;
Hay varios templates gratis que implementan Shellexecute, por ejemplo:&lt;br /&gt;
http://www.sterlingdata.com/shellex.htm&lt;br /&gt;
&lt;br /&gt;
Para hacerlo con codigo:&lt;br /&gt;
&lt;br /&gt;
En Global-embed &#039;Inside the Global Map&#039;:&lt;br /&gt;
 Module(&#039;Win32.lib&#039;)&lt;br /&gt;
 ShellExecute(Long,*CString,*CString,*CString,*CString,Short),UShort,PASCAL,RAW,NAME(&#039;ShellExecuteA&#039;)&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
En Local Data&lt;br /&gt;
 LOC:Handle LONG&lt;br /&gt;
 LOC:Op     CSTRING (255)&lt;br /&gt;
 LOC:File   CSTRING (255)&lt;br /&gt;
 LOC:Path   CSTRING (255)&lt;br /&gt;
 LOC:Param  CSTRING (255)&lt;br /&gt;
 LOC:Show   LONG&lt;br /&gt;
 LOC:RetHandle LONG&lt;br /&gt;
&lt;br /&gt;
En el embed&lt;br /&gt;
 LOC:Handle = 0{PROP:Handle}&lt;br /&gt;
 LOC:Op     = &#039;Open&#039;&lt;br /&gt;
 LOC:File   = &#039;C:\TEST.TXT&#039;&lt;br /&gt;
 LOC:Path   = PATH()&lt;br /&gt;
 LOC:Param  = &#039; &#039;&lt;br /&gt;
 LOC:Show   = 1&lt;br /&gt;
 LOC:RetHandle =  ShellExecute(LOC:Handle,LOC:Op,LOC:File,LOC:Param,LOC:Path,LOC:Show)&lt;br /&gt;
 If LOC:Rethandle &amp;lt;&amp;gt; 0 Then&lt;br /&gt;
   Message(&#039;Error&#039;,&#039;Error&#039;,Icon:Exclamation)&lt;br /&gt;
 End&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Abrir una pagina WEB con ShellExecute ==&lt;br /&gt;
(Ver declaraciones del API y variables en el ejemplo anterior)&lt;br /&gt;
&lt;br /&gt;
En el embed&lt;br /&gt;
 LOC:Handle = 0{PROP:Handle}&lt;br /&gt;
 LOC:Op     = &#039;Open&#039;&lt;br /&gt;
 LOC:File   = &#039;http://www.templatesclarion.com.ar&#039;&lt;br /&gt;
 LOC:Path   = &#039; &#039;&lt;br /&gt;
 LOC:Param  = &#039; &#039;&lt;br /&gt;
 LOC:Show   = 1&lt;br /&gt;
 LOC:RetHandle = ShellExecute(LOC:Handle,LOC:Op,LOC:File,LOC:Param,LOC:Path,LOC:Show)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Convertir un LONG a un String Binario ==&lt;br /&gt;
 binario=&#039;&#039; !binario es CSTRING&lt;br /&gt;
 LOOP F# = 1 TO 32&lt;br /&gt;
    IF BAND(abinario, 1) !abinario es LONG&lt;br /&gt;
        binario =  &#039;1&#039; &amp;amp; binario&lt;br /&gt;
    ELSE&lt;br /&gt;
        binario = &#039;0&#039; &amp;amp; binario&lt;br /&gt;
    END&lt;br /&gt;
    abinario = abinario / 2&lt;br /&gt;
    if abinario = 0 then break.&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Convertir un string binario a LONG ==&lt;br /&gt;
Para volver del CSTRING al LONG seria simplemente&lt;br /&gt;
 X# = EVALUATE (binario &amp;amp; &#039;b&#039;)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Copiar la QUEUE (solo lo que se esta viendo) de un browse a Excel ==&lt;br /&gt;
 Copiar=&#039;&#039; !Cstring de 4.000.000&lt;br /&gt;
 LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
 !Primero una fila con los titulos&lt;br /&gt;
     Copiar= Copiar &amp;amp; ?Browse:1{PropList:Header,C#} &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
&lt;br /&gt;
 LOOP F# = 1 TO RECORDS(BRW1.Q)&lt;br /&gt;
    LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
        GET(BRW1.Q, F#)&lt;br /&gt;
        Copiar= Copiar &amp;amp; FORMAT(WHAT(BRW1.Q, C#), ?Browse:1{PropList:Picture,C#}) &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
    END&lt;br /&gt;
    Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 SETCLIPBOARD(Copiar)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Copiar el VIEW (cuidado: se lee todo) de un browse a Excel ==&lt;br /&gt;
 Copiar=&#039;&#039; !CSTRNG de 4.000.000&lt;br /&gt;
 LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
     Copiar= Copiar &amp;amp; ?Browse:1{PropList:Header,C#} &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
 &lt;br /&gt;
 SET (BRW1.View)&lt;br /&gt;
 LOOP&lt;br /&gt;
    NEXT(BRW1.View)&lt;br /&gt;
    IF ERRORCODE() THEN BREAK.&lt;br /&gt;
    BRW1.SetQueueRecord&lt;br /&gt;
    LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
        Copiar= Copiar &amp;amp; FORMAT(WHAT(BRW1.Q, C#), ?Browse:1{PropList:Picture,C#}) &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
    END&lt;br /&gt;
    Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 SETCLIPBOARD(Copiar)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
Para que no lea todo el contenido del archivo, sino que procese estrictamente lo mismo que muestra el browse, basta con agregar la antes del BRW1.SetQueueRecord el llamado a BRW1.ValidateRecord().&lt;br /&gt;
  ...&lt;br /&gt;
  LOOP&lt;br /&gt;
    NEXT(BRW1.View)&lt;br /&gt;
    IF ERRORCODE() THEN BREAK.&lt;br /&gt;
    IF BRW1.ValidateRecord() THEN CYCLE.&lt;br /&gt;
    BRW1.SetQueueRecrod&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
Daniel Ruzo&lt;br /&gt;
&lt;br /&gt;
== Mostrar iconos en un listbox desde una queue ==&lt;br /&gt;
Ejemplo: marcar en una cola de memoria si un empleado tiene entrada y/o salida&lt;br /&gt;
&lt;br /&gt;
Primero agregar los iconos al Project&lt;br /&gt;
&lt;br /&gt;
En el Codigo:&lt;br /&gt;
&lt;br /&gt;
 cola_empleados    queue, pre(que)&lt;br /&gt;
 nombre        string(30)&lt;br /&gt;
 entro          long&lt;br /&gt;
 entro_ico    long&lt;br /&gt;
 salio          long&lt;br /&gt;
 salio_ico    long&lt;br /&gt;
                         end&lt;br /&gt;
&lt;br /&gt;
En el list:&lt;br /&gt;
&lt;br /&gt;
primer campo: nombre&lt;br /&gt;
&lt;br /&gt;
segundo campo: entro, picture @p p, iconized, transparente&lt;br /&gt;
&lt;br /&gt;
tercer campo: salio, picture @p p, iconized, transparente&lt;br /&gt;
&lt;br /&gt;
En el init de la pantalla:&lt;br /&gt;
&lt;br /&gt;
 ?list{prop:iconlist,1} = &#039;~no.ico&#039;&lt;br /&gt;
 ?list{prop:iconlist,2} = &#039;~si.ico&#039;&lt;br /&gt;
&lt;br /&gt;
Donde cargo la cola y la muestro:&lt;br /&gt;
&lt;br /&gt;
 if condicion  (si NO hay entrada)&lt;br /&gt;
     que:entro_ico = 1    ! icono de no&lt;br /&gt;
 else&lt;br /&gt;
     que:entro_ico = 2    !icono de si&lt;br /&gt;
 end&lt;br /&gt;
 if condicion  (si NO hay salida)&lt;br /&gt;
     que:salio_ico = 1    ! icono de no&lt;br /&gt;
 else&lt;br /&gt;
     que:salio_ico = 2    !icono de si&lt;br /&gt;
 end&lt;br /&gt;
 ! otras asignaciones&lt;br /&gt;
 add(cola_empleados)&lt;br /&gt;
 !&lt;br /&gt;
 display(?list)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Calcular Días Hábiles ==&lt;br /&gt;
&lt;br /&gt;
Ante todo necesitas una tabla de feriados, con al menos un campo llamado,&lt;br /&gt;
por ej., diaferiado y una clave por dicho campo.&lt;br /&gt;
&lt;br /&gt;
Luego podés hacer lo siguiente&lt;br /&gt;
&lt;br /&gt;
 habiles# = 0&lt;br /&gt;
 loop dia# = FechaInicial to FechaFinal&lt;br /&gt;
  if (Dia# % 7) = 0 then cycle. ! porque es domingo&lt;br /&gt;
  if (Dia# % 7) = 6 then cycle. ! porque es sabado&lt;br /&gt;
  !&lt;br /&gt;
  ! busco si es feriado&lt;br /&gt;
  !&lt;br /&gt;
  clear(feriado:record)&lt;br /&gt;
  feriado:diaferiado = dia#&lt;br /&gt;
  if access:feriado.fetch(Feriado:PorDia) = level:benign then cycle. !porque es feriado&lt;br /&gt;
  !&lt;br /&gt;
  habiles# += 1&lt;br /&gt;
 end!loop&lt;br /&gt;
 corridos# = fechafinal - fechainicial&lt;br /&gt;
&lt;br /&gt;
Si los cálculos que vas a realizar son muchos y continuos, sería conveniente&lt;br /&gt;
que la tabla de feriados la cargues en una queue y realices las búsquedas&lt;br /&gt;
sobre ella.&lt;br /&gt;
&lt;br /&gt;
Adrian Gallegos - Mega Sistemas S.R.L.&lt;br /&gt;
&lt;br /&gt;
Otra opción: Días Hábiles del mes - Rutina que quita los días sábados y domingos del mes&lt;br /&gt;
&lt;br /&gt;
CONTAR_DIAS ROUTINE&lt;br /&gt;
    LOC:DIAS = 0&lt;br /&gt;
    tope# = 1&lt;br /&gt;
    LOOP UNTIL DAY(LOC:FECHA) = tope#&lt;br /&gt;
    CASE  LOC:FECHA % 7&lt;br /&gt;
      of  0   ! Domingo&lt;br /&gt;
       LOC:FECHA = LOC:FECHA - 1&lt;br /&gt;
       CYCLE&lt;br /&gt;
      of  6    ! sabado&lt;br /&gt;
       LOC:FECHA = LOC:FECHA - 1&lt;br /&gt;
       CYCLE&lt;br /&gt;
      ELSE&lt;br /&gt;
       LOC:DIAS += 1&lt;br /&gt;
       LOC:FECHA = LOC:FECHA - 1&lt;br /&gt;
     END !CASE&lt;br /&gt;
   END !LOOP&lt;br /&gt;
   IF  LOC:FECHA % 7 = 0 or  LOC:FECHA % 7 = 6 THEN  !si el primero es feriado&lt;br /&gt;
         ! nada&lt;br /&gt;
          else&lt;br /&gt;
         LOC:DIAS  += 1&lt;br /&gt;
   END !IF&lt;br /&gt;
&lt;br /&gt;
Julio César Britez&lt;br /&gt;
&lt;br /&gt;
== Último día del mes y cantidad de días (Lunes, Martes, etc) entre 2 Fechas ==&lt;br /&gt;
Incluye el truco de saber el último día del mes: en la parte &amp;quot; Date(4,1,2005)-1 &amp;quot; significa que le resto 1 al primer día del mes siguiente, lo cual es una forma de obtener el último día del mes actual...&lt;br /&gt;
&lt;br /&gt;
 Loop Fecha# = Date(3,1,2005) TO (Date(4,1,2005)-1)&lt;br /&gt;
    EXECUTE (Fecha# % 7) + 1&lt;br /&gt;
          Domingo# +=1&lt;br /&gt;
          Lunes# +=1&lt;br /&gt;
          Martes# +=1&lt;br /&gt;
          Miercoles# +=1&lt;br /&gt;
          Jueves# +=1&lt;br /&gt;
          Viernes# +=1&lt;br /&gt;
          Sabado# +=1&lt;br /&gt;
    END&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Fecha en Español (por ejemplo en el Frame)==&lt;br /&gt;
Si quieres que funcione independientemente de como este configurado windows,&lt;br /&gt;
lo mejor es poner este embed, al final de WindowManager.Init&lt;br /&gt;
&lt;br /&gt;
 EXECUTE (TODAY() % 7) + 1&lt;br /&gt;
 Dia&amp;quot;= &#039;Domingo&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Lunes&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Martes&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Miercoles&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Jueves&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Viernes&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Sabado&#039;&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
 EXECUTE (MONTH(TODAY()))&lt;br /&gt;
 Mes&amp;quot; = &#039;Enero&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Febrero&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Marzo&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Abril&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Mayo&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Junio&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Julio&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Agosto&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Septiembre&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Octubre&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Noviembre&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Diciembre&#039;&lt;br /&gt;
 END&lt;br /&gt;
 AppFrame{Prop:StatusText,1} = CLIP(Dia&amp;quot;) &amp;amp; &#039; &#039; &amp;amp; DAY(TODAY()) &amp;amp; &#039; de &#039; &amp;amp;&lt;br /&gt;
 CLIP(Mes&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
 Dia&amp;quot; = Choose(((TODAY() % 7) + 1),&#039;Domingo&#039;,&#039;Lunes&#039;,&#039;Martes&#039;,&#039;Miercoles&#039;,&#039;Jueves&#039;,&#039;Viernes&#039;,&#039;Sabado&#039;)&lt;br /&gt;
&lt;br /&gt;
Javier A. Junca Barreto [http://www.sicya.com (SICyA Software - Colombia)]&lt;br /&gt;
&lt;br /&gt;
== Obtener la fecha del server ==&lt;br /&gt;
Este código lee la fecha del servidor, usando el truco de crear un archivo en el servidor y leer la fecha y hora de los atributos:&lt;br /&gt;
&lt;br /&gt;
 !Data&lt;br /&gt;
 LOC:TMP STRING(254),STATIC&lt;br /&gt;
 TMP FILE,DRIVER(&#039;Ascii&#039;),CREATE,NAME(LOC:TMP)&lt;br /&gt;
 RECORD  RECORD&lt;br /&gt;
 LIN STRING(1)&lt;br /&gt;
    .&lt;br /&gt;
    .&lt;br /&gt;
 FILS   QUEUE(File:queue),PRE(FIL)&lt;br /&gt;
       END&lt;br /&gt;
&lt;br /&gt;
 CODE&lt;br /&gt;
  LOC:TMP = PATH()&amp;amp;&#039;\TMP&#039;&amp;amp;RANDOM(10000,99999)&amp;amp;&#039;.TMP&#039;&lt;br /&gt;
  CREATE(TMP)&lt;br /&gt;
  IF NOT ERRORCODE()&lt;br /&gt;
    DIRECTORY(FILS,LOC:TMP,0)&lt;br /&gt;
    REMOVE(TMP)&lt;br /&gt;
    GET(FILS,1)&lt;br /&gt;
    IF TODAY() &amp;lt;&amp;gt; FIL:DATE OR ABS(CLOCK()-FIL:TIME) &amp;gt; 100&lt;br /&gt;
 !FECHA DIFERENTE O 1 SEGUNDO DE DESFASE&lt;br /&gt;
      SETTODAY(FIL:DATE)&lt;br /&gt;
      SETCLOCK(FIL:TIME)&lt;br /&gt;
    END&lt;br /&gt;
  ELSE&lt;br /&gt;
    REMOVE(TMP)&lt;br /&gt;
  END&lt;br /&gt;
&lt;br /&gt;
Carlos Gutierrez&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Con SQL&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La sugerencia de Carlos es muy buena. Si estás usando SQL o drivers ODBC, la otra opción es preguntarle la fecha al motor de base de datos.&lt;br /&gt;
&lt;br /&gt;
La forma genérica de hacerlo es:&lt;br /&gt;
 temp{prop:sql}=&#039;SELECT {fn curdate() }&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Con NET TIME&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Posteado por Diego Sánchez al foro.&lt;br /&gt;
 Run(&#039;NET TIME \\Server_Name /SET /Y&#039;)&lt;br /&gt;
Reemplazar  &amp;quot;Server_Name&amp;quot;  por el nombre del servidor o equipo del cual se desea obtener la hora&lt;br /&gt;
Fue posteado originalmente por un NICOLAS VEILLEUX nveilleux@nbautomation.com, en el foro comp.lang.clarion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Llamar a un Stored Procedure ==&lt;br /&gt;
El código sería mas o menos asi:&lt;br /&gt;
 L:Query = &#039;CALL NombreDelStored (&#039;&#039;&#039; &amp;amp; FORMAT(ParamFecha,@D12) &amp;amp; &#039;&#039;&#039;, &#039;&#039;&#039; &amp;amp;&lt;br /&gt;
 FORMAT(OtraFecha,@D12) &amp;amp; &#039;&#039;&#039;, &#039; &amp;amp; OtroParam1 &amp;amp;&#039;, &#039; &amp;amp; OtroParam2 &amp;amp;&#039;, &#039; &amp;amp;&lt;br /&gt;
 OtroParam3 &amp;amp;&#039;  )&#039;&lt;br /&gt;
&lt;br /&gt;
 ResSQL{prop:sql} = L:Query&lt;br /&gt;
 Loop Until Access:ResSql.Next()&lt;br /&gt;
    MiVariable = R:Campo1&lt;br /&gt;
    etc     = R:Campo2&lt;br /&gt;
 ....&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
En la tabla auxiliar ResSQL obtienes el resultado del último SELECT que&lt;br /&gt;
tenga el Stored Procedure.&lt;br /&gt;
&lt;br /&gt;
Para mas detalles ver el documento sobre SQL Embebido [http://templatesclarion.com.ar/downloads/ (Templates Clarion)]&lt;br /&gt;
&lt;br /&gt;
También recomiendo leer el help &amp;quot;MSSQL Accelerator Calling a Stored&lt;br /&gt;
Procedure&amp;quot;. Ahi está explicado además el uso de valores de retorno y parámetros de salida.&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Encriptación básica ==&lt;br /&gt;
Encriptación / desencriptación básica de un campo usando el metodo XOR.&lt;br /&gt;
 !la primera vez encripta&lt;br /&gt;
 !al volver a aplicar el algoritmo con la misma&lt;br /&gt;
 !Clave de encriptado: desencripta&lt;br /&gt;
 X# = 1&lt;br /&gt;
 loop Y# = 1 to Len(Campo)&lt;br /&gt;
   Campo [Y#] = chr(bxor(val(Campo[Y#]), val(ClaveEncriptado[X#])))&lt;br /&gt;
   X# += 1&lt;br /&gt;
   if X# &amp;gt; len (ClaveEncriptado) then X# = 1.&lt;br /&gt;
 end&lt;br /&gt;
 display&lt;br /&gt;
&lt;br /&gt;
 !Campo y ClaveEncriptado son campos CString&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Generar un archivo de Texto a máxima velocidad==&lt;br /&gt;
Estas son las APIs para generar un archivo de texto sin necesidad de declararlo en el Diccionario.&lt;br /&gt;
&lt;br /&gt;
Además es muy rápido, ideal para exportaciones.&lt;br /&gt;
&lt;br /&gt;
En Global - Inside Global map:&lt;br /&gt;
&lt;br /&gt;
 MODULE(&#039;Windows API&#039;)&lt;br /&gt;
  _lcreat(*CSTRING,SIGNED),SIGNED,PASCAL,RAW&lt;br /&gt;
  _hwrite(SIGNED,*CSTRING,LONG),LONG,PASCAL,RAW&lt;br /&gt;
  _lclose(SIGNED),SIGNED,PASCAL&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Por ejemplo para guardar el contenido de un control Text&lt;br /&gt;
&lt;br /&gt;
 IF NOT FILEDIALOG(&#039;Guardar como&#039;,FileName,&#039;Text|*.TXT|Source|*.CLW&#039;,FILE:Save + FILE:LongName)&lt;br /&gt;
    CYCLE&lt;br /&gt;
 END&lt;br /&gt;
 F# = _lcreat(FileName,0)&lt;br /&gt;
 X# = _hwrite(F#,Texto,LEN(Texto))&lt;br /&gt;
 X# = _lclose(F#)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Efecto BLINK en un campo ==&lt;br /&gt;
Tienes que crear un timer en la ventana, para eso ponle cada cuanto se va a&lt;br /&gt;
ejecutar en la propiedad timer de la ventana. Son centesimas de seg, asi que&lt;br /&gt;
si le pones 50 por ejemplo tu campo va a titilar 2 veces por segundo.&lt;br /&gt;
&lt;br /&gt;
Luego cierra la ventana, vuelve a entrar y vas a encontrar un evento timer&lt;br /&gt;
de la ventana en los embeds.&lt;br /&gt;
&lt;br /&gt;
Window Events --&amp;gt; Timer&lt;br /&gt;
 if ?campo{prop:background} = COLOR:WHITE&lt;br /&gt;
    ?campo{prop:background} = COLOR:SILVER&lt;br /&gt;
 else&lt;br /&gt;
    ?campo{prop:background} = COLOR:WHITE&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Función para calcular dígito verificador en CUIT ==&lt;br /&gt;
- La siguiente función devuelve el numero de CUIT con el dígito verificador&lt;br /&gt;
correcto.&lt;br /&gt;
- El parámetro que recibe es el numero de CUIT a revisar incluyendo el&lt;br /&gt;
dígito verificador.&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
  CuitCliente=&#039;20-15433984-6&#039;&lt;br /&gt;
  IF Cuit(CuitCliente)=CuitCliente THEN&lt;br /&gt;
    MESSAGE(&#039;Digito verificador correcto&#039;)&lt;br /&gt;
  ELSE&lt;br /&gt;
    MESSAGE(&#039;Digito verificador incorrecto&#039;)&lt;br /&gt;
  END&lt;br /&gt;
  ...&lt;br /&gt;
 &lt;br /&gt;
 Cuit         PROCEDURE(cuit1)&lt;br /&gt;
 cuit2        STRING(255)&lt;br /&gt;
 digver       LONG&lt;br /&gt;
 lon          LONG&lt;br /&gt;
 fac          LONG&lt;br /&gt;
 car          STRING(1)&lt;br /&gt;
   &lt;br /&gt;
  CODE&lt;br /&gt;
  cuit2=cuit1&lt;br /&gt;
  digver=0&lt;br /&gt;
  fac=2&lt;br /&gt;
  lon=LEN(CLIP(cuit2))&lt;br /&gt;
  LOOP i#=lon-1 TO 1 BY -1&lt;br /&gt;
    car=SUB(cuit2,i#,1)&lt;br /&gt;
    IF car&amp;lt;&#039;0&#039; OR car&amp;gt;&#039;9&#039; THEN&lt;br /&gt;
      CYCLE&lt;br /&gt;
    .&lt;br /&gt;
    digver=digver+(car*fac)&lt;br /&gt;
    fac+=1&lt;br /&gt;
    IF fac&amp;gt;7 THEN&lt;br /&gt;
      fac=2&lt;br /&gt;
    .&lt;br /&gt;
  .&lt;br /&gt;
  digver=11-(digver%11)&lt;br /&gt;
  IF digver&amp;gt;9 THEN&lt;br /&gt;
    digver=0&lt;br /&gt;
  .&lt;br /&gt;
  cuit2=SUB(cuit2,1,lon-1) &amp;amp; FORMAT(digver,@n01)&lt;br /&gt;
  RETURN(cuit2)&lt;br /&gt;
&lt;br /&gt;
Este codigo esta en la documentacion del template de Impresoras Fiscales&lt;br /&gt;
(BIGSYS TEMPLATES) del amigo Juan Carlos Rodríguez&lt;br /&gt;
&lt;br /&gt;
== Validar Email ==&lt;br /&gt;
Puedes hacerlo con MATCH, el cual devuelve 1 o 0 si el mail no es valido.&lt;br /&gt;
Ejemplo:&lt;br /&gt;
&lt;br /&gt;
 X# =  MATCH(UPPER(CLIP(locemail)),|&lt;br /&gt;
 &#039;^[-A-Z0-9._]+@{{[-A-Z0-9._]+.}+[A-Z][A-Z][A-Z]?[A-Z]?$&#039;, Match:Regular)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Restar Horas==&lt;br /&gt;
Para sacar la diferencia entre horas es simplemente:&lt;br /&gt;
 resultado = hora2  - hora + 1&lt;br /&gt;
&lt;br /&gt;
El +1 es porque sino que faltaria un segundo cuando muestres el resultado (en formato @T6, por ej)&lt;br /&gt;
&lt;br /&gt;
Si Hora2 es del dia siguiente, la cuenta seria:&lt;br /&gt;
 resultado = (hora2 +(100*60*60*24)) - hora + 1&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Digito Verificador para 5 digitos ==&lt;br /&gt;
&lt;br /&gt;
Si tenes un numero de 5 digitos y deses verificar que el mismo es ingresado correctamente podes usar este codigo que genera un digito verificador&lt;br /&gt;
&lt;br /&gt;
Crea una funcion que tenga un parametro (numero a verificar) y retorne el digito verificador correspondiente ejem: DigitoV5(LONG xNumero),BYTE&lt;br /&gt;
&lt;br /&gt;
  loc:Numero = xNumero&lt;br /&gt;
 &lt;br /&gt;
  loc:Valor = (loc:Numero[1]*5) + |&lt;br /&gt;
              (loc:Numero[2]*4) + |&lt;br /&gt;
              (loc:Numero[3]*3) + |&lt;br /&gt;
              (loc:Numero[4]*2) + |&lt;br /&gt;
              (loc:Numero[5]*7)&lt;br /&gt;
 &lt;br /&gt;
  IF (loc:Valor%5) + 1 = 0 OR (loc:Valor%5) + 1 = 1&lt;br /&gt;
    loc:Digito = 0&lt;br /&gt;
  ELSE&lt;br /&gt;
    loc:Digito = 6 - ((loc:Valor%5) + 1)&lt;br /&gt;
  END&lt;br /&gt;
 &lt;br /&gt;
  RETURN loc:Digito&lt;br /&gt;
&lt;br /&gt;
Ruben Garcia [http://www.dipsarg.com (DiPS)]&lt;br /&gt;
&lt;br /&gt;
== Digito Verificador para Cualquier Longitud ==&lt;br /&gt;
&lt;br /&gt;
Si no sabes que longitud puede tener el numero a verificar podes probar verificarla con este codigo&lt;br /&gt;
&lt;br /&gt;
Crea una funcion cuyo parametro es el numero a verificar y retorne el digito verificador. Ej. DigitoV(STRING xNumero),BYTE&lt;br /&gt;
&lt;br /&gt;
  !Inicializa&lt;br /&gt;
  loc:Numero   = xNumero&lt;br /&gt;
  loc:Valor    = 0&lt;br /&gt;
  loc:Multiplo = 1&lt;br /&gt;
 &lt;br /&gt;
  !Barrido y calculo&lt;br /&gt;
  LOOP loc:Posicion = LEN(CLIP(loc:Numero)) TO 1 BY -1&lt;br /&gt;
    loc:Multiplo += 1&lt;br /&gt;
    IF loc:Multiplo &amp;gt; 7&lt;br /&gt;
      loc:Multiplo = 2&lt;br /&gt;
    END&lt;br /&gt;
    loc:Valor += loc:Numero[loc:Posicion] * loc:Multiplo&lt;br /&gt;
  END&lt;br /&gt;
 &lt;br /&gt;
  loc:Digito = loc:Valor % 11&lt;br /&gt;
 &lt;br /&gt;
  IF loc:Digito = 10&lt;br /&gt;
    loc:Digito = 0&lt;br /&gt;
  END&lt;br /&gt;
 &lt;br /&gt;
  RETURN loc:Digito&lt;br /&gt;
&lt;br /&gt;
Ruben Garcia [http://www.dipsarg.com (DiPS)]&lt;br /&gt;
&lt;br /&gt;
== Autoincremento Manual == &lt;br /&gt;
&lt;br /&gt;
Esto se utiliza cuando tenemos una tabla en la cual queremos manejar la clave de auto incremento &lt;br /&gt;
&lt;br /&gt;
 CLEAR(MASTER)&lt;br /&gt;
 SET(MAS:ClavePorID, MAS:ClavePorID)&lt;br /&gt;
 ACCES:MASTER.Previous()&lt;br /&gt;
 IF MAS:Id = 0 THEN &lt;br /&gt;
     MAS:Id = 1&lt;br /&gt;
 ELSE &lt;br /&gt;
     MAS:Id = MAS:Id +1 &lt;br /&gt;
 END &lt;br /&gt;
&lt;br /&gt;
!!!MAS:Id TENDRÍA EL VALOR DEL PROXIMO NUMERO.- &lt;br /&gt;
&lt;br /&gt;
Gracias [mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
Nota: El mecanismo descrito es &amp;quot;confiable&amp;quot; sólo para aplicaciones que usan TPS y en modo single-user. Para Multiusuario y SQL hay que tener cuidado con las lecturas simultáneas del último valor. &lt;br /&gt;
También puede simplificarse el &amp;quot;if ... then ... else ...&amp;quot; como &amp;quot;MAS:Id = MAS:Id +1&amp;quot; (si es cero, será 1, no hace falta chequearlo)&lt;br /&gt;
&lt;br /&gt;
== Desabilitar menu desde cualquier procedimiento ==&lt;br /&gt;
Una opcion seria usando NOTIFY.&lt;br /&gt;
En el Frame&lt;br /&gt;
 Window Events&lt;br /&gt;
    Notify&lt;br /&gt;
        DISABLE = VariableGlobal&lt;br /&gt;
&lt;br /&gt;
En cualquier procedimiento que se necesite deshabilitar un menu&lt;br /&gt;
&lt;br /&gt;
 VariableGlobal = Nro de Use del Menu&lt;br /&gt;
 NOTIFY (999, 1)&lt;br /&gt;
&lt;br /&gt;
Puede ser 999 o cualquier cosa ya que no estoy usando ese parametro.&lt;br /&gt;
(bueno, en realidad podria usar ese parametro en lugar de la global...)&lt;br /&gt;
1 es el Tread del Frame&lt;br /&gt;
&lt;br /&gt;
Como desde los otros procedimientos no existen los use del los items de menu&lt;br /&gt;
(o sea ?menuitem) lo que hay que hacer es ponerles un numero a cada uno.&lt;br /&gt;
Esto se logra poniendolos de esta manera ?use, numero en la definicion del&lt;br /&gt;
menu.&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
==Formatear fechas en SQL ==&lt;br /&gt;
Les paso una función que utilizo para trabajar con las fechas en el SQL parecido al format de clarion. Es muy útil.&lt;br /&gt;
  &lt;br /&gt;
  CREATE FUNCTION dbo.FormatDateTime&lt;br /&gt;
  (&lt;br /&gt;
      @dt DATETIME,&lt;br /&gt;
      @format VARCHAR(16)&lt;br /&gt;
  )&lt;br /&gt;
  RETURNS VARCHAR(64)&lt;br /&gt;
  AS&lt;br /&gt;
  BEGIN&lt;br /&gt;
      DECLARE @dtVC VARCHAR(64)&lt;br /&gt;
      SELECT @dtVC = CASE @format&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;LONGDATE&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          DATENAME(dw, @dt)&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + DATENAME(m, @dt)&lt;br /&gt;
          + SPACE(1) + CAST(DAY(@dt) AS VARCHAR(2))&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + CAST(YEAR(@dt) AS CHAR(4))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;LONGDATEANDTIME&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          DATENAME(dw, @dt)&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + DATENAME(m, @dt)&lt;br /&gt;
          + SPACE(1) + CAST(DAY(@dt) AS VARCHAR(2))&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + CAST(YEAR(@dt) AS CHAR(4))&lt;br /&gt;
          + SPACE(1) + RIGHT(CONVERT(CHAR(20),&lt;br /&gt;
          @dt - CONVERT(DATETIME, CONVERT(CHAR(8),&lt;br /&gt;
          @dt, 112)), 22), 11)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;SHORTDATE&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LEFT(CONVERT(CHAR(19), @dt, 0), 11)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;SHORTDATEANDTIME&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(REPLACE(CONVERT(CHAR(19), @dt, 0),&lt;br /&gt;
              &#039;AM&#039;, &#039; AM&#039;), &#039;PM&#039;, &#039; PM&#039;)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;UNIXTIMESTAMP&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CAST(DATEDIFF(SECOND, &#039;19700101&#039;, @dt)&lt;br /&gt;
          AS VARCHAR(64))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YYYYMMDD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 112)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YYYY-MM-DD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(10), @dt, 23)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YYMMDD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(VARCHAR(8), @dt, 12)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YY-MM-DD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          STUFF(STUFF(CONVERT(VARCHAR(8), @dt, 12),&lt;br /&gt;
          5, 0, &#039;-&#039;), 3, 0, &#039;-&#039;)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MMDDYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(CONVERT(CHAR(8), @dt, 10), &#039;-&#039;, SPACE(0))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MM-DD-YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 10)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MM/DD/YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 1)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MM/DD/YYYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(10), @dt, 101)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DDMMYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(CONVERT(CHAR(8), @dt, 3), &#039;/&#039;, SPACE(0))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DD-MM-YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(CONVERT(CHAR(8), @dt, 3), &#039;/&#039;, &#039;-&#039;)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DD/MM/YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 3)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DD/MM/YYYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(10), @dt, 103)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM:SS 24&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 8)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM 24&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LEFT(CONVERT(VARCHAR(8), @dt, 8), 5)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM:SS 12&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LTRIM(RIGHT(CONVERT(VARCHAR(20), @dt, 22), 11))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM 12&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LTRIM(SUBSTRING(CONVERT(&lt;br /&gt;
          VARCHAR(20), @dt, 22), 10, 5)&lt;br /&gt;
          + RIGHT(CONVERT(VARCHAR(20), @dt, 22), 3))&lt;br /&gt;
  &lt;br /&gt;
      ELSE&lt;br /&gt;
  &lt;br /&gt;
          &#039;Invalid format specified&#039;&lt;br /&gt;
  &lt;br /&gt;
      END&lt;br /&gt;
      RETURN @dtVC&lt;br /&gt;
  END&lt;br /&gt;
  GO&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  Ejemplos:&lt;br /&gt;
  &lt;br /&gt;
  DECLARE @now DATETIME&lt;br /&gt;
  SET @now = GETDATE()&lt;br /&gt;
  &lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;LONGDATE&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;LONGDATEANDTIME&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;SHORTDATE&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;SHORTDATEANDTIME&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;UNIXTIMESTAMP&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YYYYMMDD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YYYY-MM-DD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YYMMDD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YY-MM-DD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MMDDYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MM-DD-YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MM/DD/YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MM/DD/YYYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DDMMYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DD-MM-YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DD/MM/YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DD/MM/YYYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM:SS 24&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM 24&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM:SS 12&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM 12&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;goofy&#039;) &lt;br /&gt;
  &lt;br /&gt;
Posteado al foro por Omar Squiabro&lt;br /&gt;
&lt;br /&gt;
== Anular Tecla Escape ==&lt;br /&gt;
&lt;br /&gt;
Hay veces que se necesita que el usuario salga de una FORM o de una ventana solo cuando pulse un determinado boton o se halla completado alguna condicion. Y en estos casos puede suceder que si el usuario pulsa la tecla ESC cause algun problema&lt;br /&gt;
&lt;br /&gt;
Primero hay que activar la alerta de la tecla esc en el INIT de la ventana&lt;br /&gt;
&lt;br /&gt;
  ALERT(EscKey)&lt;br /&gt;
&lt;br /&gt;
Despues en el evento alertkey&lt;br /&gt;
&lt;br /&gt;
  IF KEYCODE() = EscKey&lt;br /&gt;
    SELECT(?UnDeterminadoCampo)  !1= al primero&lt;br /&gt;
    RETURN Level:Notify          &lt;br /&gt;
  END&lt;br /&gt;
&lt;br /&gt;
Si el codigo embebido es puesto despues del codigo generado por clarion reemplazar el RETURN por CYCLE&lt;br /&gt;
&lt;br /&gt;
Ruben Garcia [http://www.programaya.com (DiPS)]&lt;br /&gt;
&lt;br /&gt;
== Convertir Número Hexadecimal a Binario ==&lt;br /&gt;
A veces, y sobre todo al trabajar con estados de puertos de comunicaciones, necesitamos convertir numeros hexadecimales a binario para establecer errores o tratar envios y recepciones.&lt;br /&gt;
Esta función hace justamente esto de forma sencilla.&lt;br /&gt;
&lt;br /&gt;
 HexaABinario         PROCEDURE(PAR:Nhexa)   ! (String),String&lt;br /&gt;
 HBinario   String(4),dim(16) !Equivalencias&lt;br /&gt;
 RetuBina   String(100)       !Variable Retorno&lt;br /&gt;
&lt;br /&gt;
  CODE&lt;br /&gt;
   HBinario[01] = &#039;0000&#039; !     0h&lt;br /&gt;
   HBinario[02] = &#039;0001&#039; !     1h&lt;br /&gt;
   HBinario[03] = &#039;0010&#039; !     2h&lt;br /&gt;
   HBinario[04] = &#039;0011&#039; !     3h&lt;br /&gt;
   HBinario[05] = &#039;0100&#039; !     4h&lt;br /&gt;
   HBinario[06] = &#039;0101&#039; !     5h &lt;br /&gt;
   HBinario[07] = &#039;0110&#039; !     6h&lt;br /&gt;
   HBinario[08] = &#039;0111&#039; !     7h&lt;br /&gt;
   HBinario[09] = &#039;1000&#039; !     8h &lt;br /&gt;
   HBinario[10] = &#039;1001&#039; !     9h&lt;br /&gt;
   HBinario[11] = &#039;1010&#039; !     Ah  65&lt;br /&gt;
   HBinario[12] = &#039;1011&#039; !     Bh  66&lt;br /&gt;
   HBinario[13] = &#039;1100&#039; !     Ch  67&lt;br /&gt;
   HBinario[14] = &#039;1101&#039; !     Dh  68&lt;br /&gt;
   HBinario[15] = &#039;1110&#039; !     Eh  69&lt;br /&gt;
   HBinario[16] = &#039;1111&#039; !     Fh  70&lt;br /&gt;
   CLEAR(RetuBina)&lt;br /&gt;
   PAR:NHexa = UPPER(PAR:NHexa) !Aseguro letras en mayusculas&lt;br /&gt;
   LOOP I# = 1 to LEN(CLIP(PAR:NHexa))&lt;br /&gt;
     IF NUMERIC(PAR:NHexa[I#]) THEN  !Si es numero es &amp;lt;= 9&lt;br /&gt;
        !Asigno equivalente&lt;br /&gt;
        RetuBina = CLIP(RetuBina) &amp;amp; HBinario[PAR:NHexa[I#]+1]&lt;br /&gt;
     ELSE&lt;br /&gt;
       !Asigno equivalente tomando,por ejemplo, 65 - 54 = 11 para &amp;quot;A&amp;quot;&lt;br /&gt;
        RetuBina = CLIP(RetuBina) &amp;amp; HBinario[VAL(PAR:NHexa[I#])-54]&lt;br /&gt;
     END&lt;br /&gt;
   END&lt;br /&gt;
   !Retorno la cadena binaria sin ceros a la izquierda&lt;br /&gt;
   RETURN(SUB(RetuBina, INSTRING(&#039;1&#039;,RetuBina), LEN(CLIP(RetuBina)))) &lt;br /&gt;
&lt;br /&gt;
Bueno, espero les sea de utilidad!&lt;br /&gt;
&lt;br /&gt;
Mario A. Wojcik&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Transacciones, TCF, Logout, Commit, Rollback ==&lt;br /&gt;
&lt;br /&gt;
Solucion al tema del TCF, crearlo, verlo, etc.&lt;br /&gt;
&lt;br /&gt;
Paso 1&lt;br /&gt;
En el punto embebido del FRAME (windowsManager init (priority 6100) o sea antes que se abra ningun archivo, &lt;br /&gt;
Insertar el siguiente codigo:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GLO:Datos:TCF =  &#039;TCF=&#039; &amp;amp; CLIP(GLO:WorkDir) &amp;amp; &#039;\&#039; &amp;amp; &#039;AR.TCF&#039;&lt;br /&gt;
Send(CLIENTES,GLO:Datos:TCF)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nota 1:&lt;br /&gt;
No es necesario definir en ningun lado el archivo .TCF ni su estructura porque de todo se encarga el driver.&lt;br /&gt;
Nota 2: &lt;br /&gt;
no enviar (SEND) los datos para el .TCF  desde el diccionario, según los autores, esto no funciona.&lt;br /&gt;
Nota 3: Solo es necesario un archivo .TCF para toda la aplicación. &lt;br /&gt;
&lt;br /&gt;
Listo, el archivo de control de transacciones ya esta creado y sin dudas funcionara.&lt;br /&gt;
Para que todo quede completo deberiamos hacer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LOGOUT(1,CabeceraFactura,DetalleFactura)	!Comienza la transaccion&lt;br /&gt;
DO ErrHandler 				!Chequear errores&lt;br /&gt;
ADD(CabeceraFactura) 			!Agregar registro al padre&lt;br /&gt;
DO ErrHandler 				!&lt;br /&gt;
LOOP X# = 1 TO RECORDS(DetailQue) 	!Procesar los registros almacenados&lt;br /&gt;
GET(DetailQue,X#) 				!traer uno de la Cola&lt;br /&gt;
DO ErrHandler 				!&lt;br /&gt;
Det:Record = DetailQue 			!Asignar al record buffer&lt;br /&gt;
ADD(DetalleFactura) 			!Hacer el Add al archivo&lt;br /&gt;
DO ErrHandler 				!&lt;br /&gt;
END&lt;br /&gt;
COMMIT 					!Terminar la transaccion, todo OK&lt;br /&gt;
ASSERT(~ERRORCODE())&lt;br /&gt;
&lt;br /&gt;
ErrHandler ROUTINE 				!Runina de manejo de errores&lt;br /&gt;
IF NOT ERRORCODE() THEN EXIT. 		!Sale si no hay errores&lt;br /&gt;
Err&amp;quot; = ERROR() 				!Guarda el mensaje de error&lt;br /&gt;
ROLLBACK 					!Anula la transaccion&lt;br /&gt;
ASSERT(~ERRORCODE())&lt;br /&gt;
BEEP 						!Avisa al usuario&lt;br /&gt;
MESSAGE(&#039;Transaction Error - &#039; &amp;amp; Err&amp;quot;)&lt;br /&gt;
RETURN 					&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Lo anterior es copia de la ayuda del manual de Clarion 6.3&lt;br /&gt;
&lt;br /&gt;
Para verificar que exista realmente el archivo .TCF podemos usar en cualquier punto de la aplicación lo siguiente:&lt;br /&gt;
&lt;br /&gt;
Poner un string variable en pantalla y en un boton:&lt;br /&gt;
&lt;br /&gt;
Loc:Tcf = SEND(CLIENTES, &#039;TCF&#039;)&lt;br /&gt;
DISPLAY()&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Eso deberia ser todo. Por lo menos para mi funciona.&lt;br /&gt;
&lt;br /&gt;
Carlos Barroso&lt;br /&gt;
DBA Oracle&lt;br /&gt;
Programador Clarion &amp;lt;/pr&amp;gt;&lt;/div&gt;</summary>
		<author><name>Carlos Barroso</name></author>
	</entry>
	<entry>
		<id>https://clarionwiki.com.ar/index.php?title=Codigo_Util&amp;diff=86</id>
		<title>Codigo Util</title>
		<link rel="alternate" type="text/html" href="https://clarionwiki.com.ar/index.php?title=Codigo_Util&amp;diff=86"/>
		<updated>2015-12-15T15:23:30Z</updated>

		<summary type="html">&lt;p&gt;Carlos Barroso: /* Transacciones, TCF, Logout, Commit, Rollback */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;En esta sección se incluyen rutinas de código que nos resultaron útiles en más de una ocasión.&lt;br /&gt;
&lt;br /&gt;
== PDF con cualquier versión de Clarion ==&lt;br /&gt;
Después de mucho rebuscar algo gratis que haga esto, hoy pude componer algo:&lt;br /&gt;
1º) Aporte de Fernando Cerini para &amp;quot;capturar&amp;quot; los .wmf de los reportes de Clarion:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En el embed PrintPreview.Open, despues de Parent call&lt;br /&gt;
 &lt;br /&gt;
 get(SELF.ImageQueue,POINTER(SELF.ImageQueue))&lt;br /&gt;
 LOOP a# = 1 to RECORDS(SELF.ImageQueue)&lt;br /&gt;
          get(SELF.ImageQueue,a#)&lt;br /&gt;
          COPY(SELF.ImageQueue, &#039;c:\temp\Pagina&#039; &amp;amp; a# &amp;amp;&#039;.WMF&#039;)&lt;br /&gt;
          IF ERRORCODE() THEN MESSAGE(ERROR()).&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
En mi caso, con Clarion 5.0 ABC el embed point fue Previewer.Open, después del Parent call&lt;br /&gt;
&lt;br /&gt;
2º) Instalar OpenOffice y seguir las instrucciones de&lt;br /&gt;
http://www.xml.com/pub/a/2006/01/11/from-microsoft-to-openoffice.html&lt;br /&gt;
para crear la macro.&lt;br /&gt;
La verdad, no pude cambiar el nombre de Module1 a Conversiones o algo así, pero no importó mucho.&lt;br /&gt;
&lt;br /&gt;
3º) Cree un batch con la línea&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;C:\Archivos de Programa\OpenOffice.org 2.3\program\soffice&amp;quot; -invisible macro:///Standard.Module1.SaveAsPDF(c:\temp\pagina8.wmf)&lt;br /&gt;
&lt;br /&gt;
Es lo que dice el instructivo mencionado en el punto 2º) acomodado a mi instalación de OpenOffice, lo corrí y funcionó a la primera prueba.&lt;br /&gt;
&lt;br /&gt;
4º) Sólo queda hacer la llamada mediante RUN en el código de Clarion y santo remedio.&lt;br /&gt;
&lt;br /&gt;
Espero sea de ayuda para alguien.&lt;br /&gt;
Saludos&lt;br /&gt;
Ezequiel&lt;br /&gt;
&lt;br /&gt;
== Como filtrar un Tree ==&lt;br /&gt;
Una tecnica de Saul Perez Quezada para filtrar Arboles (generados con el template Relacion Tree), con filtros en runtime que son complejos, y que no podemos hacer con un simple SetFilter().&lt;br /&gt;
&lt;br /&gt;
Alguien ha posteado una pregunta sobre como Filtrar arboles, yo en lo personal he dejado de usar arboles, por que son bastante lentos y deficientes, pero en algunas aplicaciones es bastante util, de manera que comparto la experiencia y les subo una pequeña explicacion de como filtrar Arboles (generados con el template Relacion Tree), con filtros en runtime que son complejos, y que no podemos hacer con un simple SetFilter(), ya que no existe esto en la propiedad.&lt;br /&gt;
 &lt;br /&gt;
Saludos y espero les sirva.&lt;br /&gt;
 &lt;br /&gt;
Como filtrar un Tree con solamente tablas:&lt;br /&gt;
 &lt;br /&gt;
Dentro del Template para armar los arboles, tiene dentro de Primary y Secondary files la opcion de filtrar, agregando un filtro, pero muchas veces dicho filtro no es tan simple de armar, o necesitamos estarlo cambiando en runtime.&lt;br /&gt;
 &lt;br /&gt;
Aqui les presento un caso y como su solucion, esperando que les sirva...&lt;br /&gt;
 &lt;br /&gt;
Tengo tres tablas:&lt;br /&gt;
 &lt;br /&gt;
Abuelos&lt;br /&gt;
Padres&lt;br /&gt;
Hijos&lt;br /&gt;
 &lt;br /&gt;
Y en la tabla hijos, tengo un campo que define en que nivel de escuela en que va cada hijo: Pre-escolar, Primaria, Secundaria, Bachillerato, Universidad, PostGrado, Maestria y Doctorado.&lt;br /&gt;
 &lt;br /&gt;
En la ventana requiero que el usuario pueda filtrar por el nivel escolar, pero con las siguientes condiciones:&lt;br /&gt;
 &lt;br /&gt;
1.- El usuario puede seleccionar si quiere ver cualquier combinacion de estudios, es decir, que pueda ver los de primaria y universidad solamente o todos, o ninguno...&lt;br /&gt;
2.- Si selecciona un filtro donde resulte que un abuelo o padre no tiene hijos que cumplan la condicion, entonces no debe mostrarnos a esos abuelos y padres...&lt;br /&gt;
 &lt;br /&gt;
Considerando que el Tree template, muestra siempre los niveles de acuerdo al filtro que se arma, pues podria convertirse algo engorroso, estar cambiando el filtro, ya que no es una propiedad del relacion tree, tal como sucede con el browse class, sino que el tree filtra a base de If&#039;s interpuestos al llenar el queue...&lt;br /&gt;
 &lt;br /&gt;
Usando el template y solo poniendo los filtros de NIVELESCOLAR = LOC:MINIVEL, solo mostraria los hijos de un nivel, asi que este metodo no nos servira...&lt;br /&gt;
 &lt;br /&gt;
La idea seria entonces que como filtro usaremos una condicion matematica, si la condicion es Verdadera, entonces mostrara el hijo, de lo contrario no lo mostrara.&lt;br /&gt;
 &lt;br /&gt;
Asi que con el comando EVALUATE, evaluaremos un filtro, dentro del secondary files, opcion filter, si el filtro es bueno (igual a 1), entonces nos mostrara la info de lo contrario no...&lt;br /&gt;
 &lt;br /&gt;
Por ejemplo:&lt;br /&gt;
 &lt;br /&gt;
En el secondary files en buscamos la tabla de Hijos y le escribimos en el filtro:&lt;br /&gt;
 &lt;br /&gt;
0&amp;lt;EVALUATE(Filtro) ! Si el filtro es valido, entonces nos mostrara el resultado, de lo contrario no...&lt;br /&gt;
 &lt;br /&gt;
Declaramos un check box por cada cada condicion, con su respectiva variable: Preescolar, Primaria, Secundaria, etc... que sea tipo byte, con valor true = 1 y valor false = 0&lt;br /&gt;
 &lt;br /&gt;
! En el acepted del check box Preescolar... y cada condicion...&lt;br /&gt;
Do Filtrar&lt;br /&gt;
Display&lt;br /&gt;
 &lt;br /&gt;
! En procedure routines&lt;br /&gt;
Filtrar Routine&lt;br /&gt;
  Clear(Filtro) ! Variable para filtrar&lt;br /&gt;
 &lt;br /&gt;
  If Preescolar Then Filtro = &#039;HIJ:NIVELESCOLAR=&#039;&#039;Preescolar&#039;&#039;&#039; ..&lt;br /&gt;
  &lt;br /&gt;
  If Filtro And Primaria&lt;br /&gt;
    Filtro = Clip(Filtro) &amp;amp; &#039; And HIJ:NIVELESCOLAR=&#039;&#039;Primaria&#039;&#039;&#039;&lt;br /&gt;
  ElsIf Filtro And Primaria&lt;br /&gt;
    Filtro = &#039;HIJ:NIVELESCOLAR=&#039;&#039;Primaria&#039;&#039;&#039;&lt;br /&gt;
  End&lt;br /&gt;
  ! Esto se repite con todas las condiciones...&lt;br /&gt;
  ...&lt;br /&gt;
  ...&lt;br /&gt;
  &lt;br /&gt;
  ! Despues de armar el filtro, refrescar el browse...&lt;br /&gt;
 &lt;br /&gt;
  DO REL4::RefreshTree ! Esta rutina la genera el template, se debe buscar en los modulos su nombre correcto.&lt;br /&gt;
  POST(EVENT:NewSelection,?RelTree)&lt;br /&gt;
 &lt;br /&gt;
 Exit  &lt;br /&gt;
 &lt;br /&gt;
Hasta ahora, lo que se ha logrado es que cuando el usuario seleccione un tipo nivel escolar, nos mostrara o no los hijos, pero resulta, que ahora lo que necesito es que solo me muestre los abuelos que tienen nietos, y los padres que tienen hijos, es entonces por que no tiene caso que el arbol se llene con Mil abuelos, si solo 30 de ellos tienen nietos...&lt;br /&gt;
 &lt;br /&gt;
Para hacer debemos entender como se llena el queue del tree, basicamente, el tree, tantas vueltas como combinaciones de nuestra estructura tengamos, es decir, suponiendo que tenemos 3 abuelos, 3 padres, con 3 hijos cada uno, lo que hace el template es:&lt;br /&gt;
 &lt;br /&gt;
Por cada Abuelo Barre (con Loop) la tabla Padres por completo 1 vez y selecciono los que me corresponden y por cada padre barro la tabla hijos (con Loop) por completo y selecciono los que me corresponden...&lt;br /&gt;
 &lt;br /&gt;
Entonces esta estructura daria 3 x 3 x 3 = 27 vueltas. (Ahora imaginen lo que tarda en entre tablas de 10000 registros...)&lt;br /&gt;
 &lt;br /&gt;
Bueno, siguiendo esta idea, entonces lo que necesitamos, es interceptar cada vuelta en el punto donde no queremos que se ingrese al queue y hacer un break.&lt;br /&gt;
 &lt;br /&gt;
Conociendo esto buscamos en nuestros embeds, embedido llamado:&lt;br /&gt;
 &lt;br /&gt;
RelacionTree After Next Primary File y RelationTree After Next Secondary File...&lt;br /&gt;
 &lt;br /&gt;
Aqui buscamos el archivo que requerimos excluir en algunos casos, que es Padres e Hijos, le escribimos en el caso de Padres:&lt;br /&gt;
 &lt;br /&gt;
PAD:AbueloId = ABU:AbueloId&lt;br /&gt;
If Access:Padres.Fetch(FK AbueloId)  ! Si no existen padres que sean dependientes de este abuelo&lt;br /&gt;
  Break          ! hacer el break al Loop del armado del tree...&lt;br /&gt;
End&lt;br /&gt;
 &lt;br /&gt;
Y el caso de los hijos repetimos la condicion:&lt;br /&gt;
 &lt;br /&gt;
HIJ:PadreId = PAD:PadreId&lt;br /&gt;
If Access:Hijos.Fetch(FK PadreId)  ! Si no existen hijos que sean dependientes de este abuelo&lt;br /&gt;
  Break          ! hacer el break al Loop del armado del tree...&lt;br /&gt;
End&lt;br /&gt;
 &lt;br /&gt;
El efecto sera que no nos mostrara aquellos padres y abuelos que no tengan hijos.&lt;br /&gt;
 &lt;br /&gt;
Si requerimos algo mas complejo podemos usar una vista, en vez de una Fetch a una tabla, pero obviamente se alentara esto cada vez mas y mas al llenar el Arbol.&lt;br /&gt;
 &lt;br /&gt;
Nota&lt;br /&gt;
Si usamos un motor de SQL, una alternativa mucho mas eficiente es declarar un Vista en nuestro motor y diccionario que sirva de base para llenar el Arbol, con esto aumentaria considerablemente la velocidad.&lt;br /&gt;
&lt;br /&gt;
== Reemplazar caracteres en un string ==&lt;br /&gt;
Una rutina generica para reemplazar todas las instancias de un string dentro de otro&lt;br /&gt;
&lt;br /&gt;
 Replace       PROCEDURE(string find,string replace,*cstring into)&lt;br /&gt;
 Locate LONG,AUTO&lt;br /&gt;
  CODE&lt;br /&gt;
    IF UPPER(find)&amp;lt;&amp;gt;UPPER(replace)&lt;br /&gt;
       LOOP&lt;br /&gt;
         Locate = INSTRING(UPPER(find),UPPER(into),1,1)&lt;br /&gt;
         IF ~Locate THEN RETURN .&lt;br /&gt;
         into = SUB(into,1,Locate-1) &amp;amp; replace &amp;amp;SUB(into,Locate+LEN(find),LEN(into))&lt;br /&gt;
       END&lt;br /&gt;
    END&lt;br /&gt;
&lt;br /&gt;
Para llamarlo:&lt;br /&gt;
&lt;br /&gt;
Replace(&#039;Busacr&#039;,&#039;Reemplazar&#039;,Loc:miString)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Contar los threads abiertos ==&lt;br /&gt;
Este código fue posteado por Jeff Slarve&lt;br /&gt;
 ChildThreadCount Procedure!,Long&lt;br /&gt;
 Ndx   Long&lt;br /&gt;
 Count Long&lt;br /&gt;
 T     &amp;amp;Window&lt;br /&gt;
 W     &amp;amp;Window&lt;br /&gt;
  Code&lt;br /&gt;
&lt;br /&gt;
  Count = 0&lt;br /&gt;
  SetTarget&lt;br /&gt;
  T &amp;amp;= System{PROP:Target}&lt;br /&gt;
  Loop Ndx = 1 to MaxThreads&lt;br /&gt;
     SetTarget(,Ndx)&lt;br /&gt;
     W &amp;amp;= System{PROP:Target}&lt;br /&gt;
     If NOT W &amp;amp;= T&lt;br /&gt;
       Count += 1&lt;br /&gt;
     end&lt;br /&gt;
  end&lt;br /&gt;
  SetTarget&lt;br /&gt;
  Return Count&lt;br /&gt;
&lt;br /&gt;
== Permitir solo una instancia del EXE en ejecucion ==&lt;br /&gt;
En Inside the global map:&lt;br /&gt;
&lt;br /&gt;
 INCLUDE(&#039;CWUTIL.INC&#039;),ONCE&lt;br /&gt;
&lt;br /&gt;
En Global Program Setup:&lt;br /&gt;
&lt;br /&gt;
 IF NOT BeginUnique(&#039;MiAPP.exe&#039;)&lt;br /&gt;
    BEEP(BEEP:SystemExclamation)&lt;br /&gt;
    YIELD()&lt;br /&gt;
    CASE MESSAGE(&#039;El programa ya esta ejecutando..&#039;,&#039;Ho, ho...&#039;,ICON:Asterisk,BUTTON:OK,BUTTON:OK,0)&lt;br /&gt;
    OF BUTTON:OK&lt;br /&gt;
       HALT()&lt;br /&gt;
    END&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Cambiar los atributos de un archivo ==&lt;br /&gt;
(Del FAQ de SoftVelocity)&lt;br /&gt;
1. En Global Embeds&lt;br /&gt;
&lt;br /&gt;
A. Before Global Includes:&lt;br /&gt;
&lt;br /&gt;
 LPCSTR EQUATE(CSTRING)&lt;br /&gt;
 DWORD EQUATE(ULONG)&lt;br /&gt;
&lt;br /&gt;
B. Global Data: Equates de los artributos&lt;br /&gt;
&lt;br /&gt;
 FILE_ATTRIBUTE_READONLY EQUATE(00000001h)&lt;br /&gt;
 FILE_ATTRIBUTE_HIDDEN EQUATE(00000002h)&lt;br /&gt;
 FILE_ATTRIBUTE_SYSTEM EQUATE(00000004h)&lt;br /&gt;
 FILE_ATTRIBUTE_ARCHIVE EQUATE(00000020h)&lt;br /&gt;
 FILE_ATTRIBUTE_NORMAL EQUATE(00000080h)&lt;br /&gt;
 FILE_ATTRIBUTE_TEMPORARY EQUATE(00000100h)&lt;br /&gt;
&lt;br /&gt;
C. Inside Global Map:&lt;br /&gt;
&lt;br /&gt;
 Module(&#039;Win32.lib&#039;)&lt;br /&gt;
 SetFileAttributes(*CSTRING, ULONG), BOOL, RAW, PASCAL, NAME(&#039;SetFileAttributesA&#039;)&lt;br /&gt;
 GetFileAttributes(*CSTRING), ULONG, RAW, PASCAL, NAME(&#039;GetFileAttributesA&#039;)&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
2. En el codigo:&lt;br /&gt;
&lt;br /&gt;
 filename CSTRING(50)&lt;br /&gt;
&lt;br /&gt;
 filename = &#039;test.txt&#039;&lt;br /&gt;
 y# = GetFileAttributes(filename) ! Leer atributos&lt;br /&gt;
 y# = SetFileAttributes(filename, FILE_ATTRIBUTE_READONLY)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Crear directorios ==&lt;br /&gt;
Hay 2 opciones:&lt;br /&gt;
&lt;br /&gt;
1- Con Clarion (no sé si estarán estas funciones en versiones anteriores de Clarion)&lt;br /&gt;
&lt;br /&gt;
En &amp;quot;Inside the Global map&amp;quot;&lt;br /&gt;
 Include(&#039;clib.clw&#039;)&lt;br /&gt;
&lt;br /&gt;
En Local Data&lt;br /&gt;
 MiDir Cstring(256)&lt;br /&gt;
 Ret     Long&lt;br /&gt;
&lt;br /&gt;
En tu embed&lt;br /&gt;
 MiDir = &#039;Dirtest&#039;&lt;br /&gt;
 Ret = MkDir(MiDir)&lt;br /&gt;
&lt;br /&gt;
2- Con API&lt;br /&gt;
En Global Embeds - inside global map:&lt;br /&gt;
&lt;br /&gt;
 MODULE(&#039;&#039;)&lt;br /&gt;
  DirAccess(*CSTRING,SHORT=0),SHORT,RAW,NAME(&#039;_access&#039;),PROC&lt;br /&gt;
  MkDir(*CSTRING),SHORT,RAW,NAME(&#039;_mkdir&#039;),PROC&lt;br /&gt;
  DirRename(*CSTRING, *CSTRING), SHORT, RAW, NAME(&#039;_rename&#039;),PROC&lt;br /&gt;
  RmDir(*CSTRING),SHORT,RAW,NAME(&#039;_rmdir&#039;),PROC&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
Y en tu embebido podés poner lo siguiente:&lt;br /&gt;
&lt;br /&gt;
 Var=&#039;O:\test&#039;      ! Ojo tiene que ser Cstring&lt;br /&gt;
 IF DirAccess(Var)&amp;lt;&amp;gt;0        !Si no existe&lt;br /&gt;
          MkDir(Var)                    !Crearlo&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
También se puede usar DirRename y RmDir para renombrar o borrar&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Modificar la posicion de un informe ==&lt;br /&gt;
&lt;br /&gt;
Este código permite modificar la posición inicial vertical y horizontal de un informe. Es útil cuando trabajamos con un formulario preimpreso o una hoja membretada y necesitamos desplazar la impresión sin necesidad de reconfigurar el programa.&lt;br /&gt;
&lt;br /&gt;
El punto embebido donde se ubica es After Open the Report.&lt;br /&gt;
&lt;br /&gt;
 !=====================================================&lt;br /&gt;
 ! MODIFICAR LA POSICION DEL INFORME&lt;br /&gt;
 ! El pie de página modificarlo sólo si se usa: &lt;br /&gt;
 ! Por ejemplo se pone ahí el Nro de página o algo así.&lt;br /&gt;
 ! No es necesario cambiar los atributos de las bandas &lt;br /&gt;
 ! de detalle adicionales que se usan en el informe.&lt;br /&gt;
 !-----------------------------------------------------&lt;br /&gt;
 DesplazamientoX =  getini(&#039;Parametros&#039;,|&lt;br /&gt;
                    &#039;DesplazamientoX&#039;,&#039;&#039;,&#039;.\prog.ini&#039;)&lt;br /&gt;
 DesplazamientoY = getini(&#039;Parametros&#039;,|&lt;br /&gt;
                    &#039;DesplazamientoY&#039;,&#039;&#039;,&#039;.\prog.ini&#039;)&lt;br /&gt;
 &lt;br /&gt;
 if DesplazamientoX or DesplazamientoY then&lt;br /&gt;
    SETTARGET(Report)&lt;br /&gt;
    x# = report{prop:Xpos}&lt;br /&gt;
    y# = report{prop:Ypos}&lt;br /&gt;
    x# += DesplazamientoX&lt;br /&gt;
    y# += DesplazamientoY&lt;br /&gt;
    target{prop:Xpos} = x#&lt;br /&gt;
    target{prop:Ypos} = y#&lt;br /&gt;
    settarget&lt;br /&gt;
 &lt;br /&gt;
    SETTARGET(Report,?Encabezado)&lt;br /&gt;
    x# = ?Encabezado{prop:Xpos}&lt;br /&gt;
    y# = ?Encabezado{prop:Ypos}&lt;br /&gt;
    x# += DesplazamientoX&lt;br /&gt;
    y# += DesplazamientoY&lt;br /&gt;
    ?Encabezado{prop:Xpos} = x#&lt;br /&gt;
    ?Encabezado{prop:Ypos} = y#&lt;br /&gt;
    settarget&lt;br /&gt;
 &lt;br /&gt;
    SETTARGET(Report,?PiePagina)&lt;br /&gt;
    x# = ?PiePagina{prop:Xpos}&lt;br /&gt;
    y# = ?PiePagina{prop:Ypos}&lt;br /&gt;
    x# += DesplazamientoX&lt;br /&gt;
    y# += DesplazamientoY&lt;br /&gt;
    ?PiePagina{prop:Xpos} = x#&lt;br /&gt;
    ?PiePagina{prop:Ypos} = y#&lt;br /&gt;
    SETTARGET&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== Generar un informe a partir de una cola en memoria ==&lt;br /&gt;
&lt;br /&gt;
(Sin pasar por el template)&lt;br /&gt;
&lt;br /&gt;
Se puede definir en el módulo en la parte de DATA el reporte necesario y mandarlo a imprimir.&lt;br /&gt;
&lt;br /&gt;
En DATA SECTION&lt;br /&gt;
&lt;br /&gt;
 WMFQue        QUEUE&lt;br /&gt;
 PageImage       STRING(64)&lt;br /&gt;
               END&lt;br /&gt;
 ReportRunDate LONG&lt;br /&gt;
 ReportRunTime LONG&lt;br /&gt;
 !!&amp;gt; Report (portrait) &lt;br /&gt;
 Report  REPORT,AT(1000,2000,6000,7000),THOUS,PRE(RPT),FONT(&#039;Arial&#039;,10)&lt;br /&gt;
         HEADER,AT(1000,1000,6000,1000)&lt;br /&gt;
         END&lt;br /&gt;
 Detail   DETAIL&lt;br /&gt;
         END&lt;br /&gt;
         FOOTER,AT(1000,9000,6000,1000)&lt;br /&gt;
         END&lt;br /&gt;
         FORM,AT(1000,1000,6000,9000)&lt;br /&gt;
         END&lt;br /&gt;
       END&lt;br /&gt;
&lt;br /&gt;
       &lt;br /&gt;
Luego, en el botón que imprime,  poner el siguiente código:&lt;br /&gt;
&lt;br /&gt;
 ReportRunDate = today()&lt;br /&gt;
 ReportRunDate = today()&lt;br /&gt;
 ReportRunTime = clock()&lt;br /&gt;
 OPEN(Report)&lt;br /&gt;
 LOOP x# = 1 to records(ColaError)&lt;br /&gt;
    get(ColaError,x#)&lt;br /&gt;
    PRINT(rpt:DetalleUno)&lt;br /&gt;
 END&lt;br /&gt;
 ENDPAGE(Report)&lt;br /&gt;
 ReportPreview(WMFQue)&lt;br /&gt;
 IF GlobalResponse = RequestCompleted&lt;br /&gt;
    Report{PROP:FlushPreview} = True&lt;br /&gt;
 END&lt;br /&gt;
 CLOSE(Report)&lt;br /&gt;
 FREE(WMFQue)&lt;br /&gt;
&lt;br /&gt;
Este proceso usa el Print Preview de Clarion para visualizar el informe, por consiguiente, la apariencia es totalmente normal para el usuario&lt;br /&gt;
&lt;br /&gt;
== Proceso BATCH que funcione sincronizado con el TIMER de la ventana. ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En un proceso BATCH hecho a mano se puede operar en forma similar al report o al proccess, para eso hay que definir una pantalla que tenga el atributo TIMER (un valor de 1 es suficiente). El código fuente quedaría de la siguiente manera:&lt;br /&gt;
&lt;br /&gt;
 open(PantaTrabaja)&lt;br /&gt;
 Accept&lt;br /&gt;
 case event()&lt;br /&gt;
   of event:openwindow&lt;br /&gt;
      Display()&lt;br /&gt;
      ! Abrir pantalla, Abrir archivos, hacer el set &lt;br /&gt;
      ! Inicial&lt;br /&gt;
   of event:timer&lt;br /&gt;
      ?Mensaje{prop:text} = &#039;Procesando...&#039;&lt;br /&gt;
      display(?Mensaje)&lt;br /&gt;
      loop 2 times&lt;br /&gt;
         next(archivo)&lt;br /&gt;
         if errorcode() then &lt;br /&gt;
            FinArchivo = true&lt;br /&gt;
            Break&lt;br /&gt;
         end&lt;br /&gt;
         ! HACER AQUÍ ALGÚN PROCESO..&lt;br /&gt;
      end&lt;br /&gt;
   of event:closewindow&lt;br /&gt;
      Setcursor()&lt;br /&gt;
      Close(PantaTrabaja)&lt;br /&gt;
      Break&lt;br /&gt;
   End!case&lt;br /&gt;
 &lt;br /&gt;
 case field()&lt;br /&gt;
   of ?BotonCancelar&lt;br /&gt;
      if event() = event:accepted then&lt;br /&gt;
         message(&#039;Proceso cancelado por el usuario.&#039;)&lt;br /&gt;
         post(event:closewindow)&lt;br /&gt;
      end&lt;br /&gt;
   End&lt;br /&gt;
 end!accept&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Drop Combo a mano, con opción a todos y recuerdo de último elegido ==&lt;br /&gt;
&lt;br /&gt;
En muchos de mis informes, cuando pido parámetros asumo lo siguiente, por ejemplo: Si el código de cliente es igual a 0 (cero) se imprimen todos los clientes, sino, se imprime uno solo. Este código permite armar lo mismo con un DropCombo, agregando la opción TODOS LOS XXXX (código Cero).&lt;br /&gt;
&lt;br /&gt;
1. Definir una cola con la siguiente estructura ( por ejemplo ):&lt;br /&gt;
&lt;br /&gt;
 Cola     QUEUE,PRE(col)&lt;br /&gt;
 descri     STRING(20)&lt;br /&gt;
 codigo     SHORT&lt;br /&gt;
          END&lt;br /&gt;
	&lt;br /&gt;
2. Definir una variable local llamada por ejemplo&lt;br /&gt;
&lt;br /&gt;
 Combo1     STRING(20)&lt;br /&gt;
	&lt;br /&gt;
3.	En la pantalla definir una drop combo a mano, en el campo FROM poner el nombre de la COLA, y en el USE poner Combo1 ( no poner ?Combo1 )&lt;br /&gt;
	&lt;br /&gt;
4.	Cargar la cola según corresponda después de abrir archivos, poniendo como último dato el código 0 y el texto &#039;TODOS LOS REGISTROS&#039; y hacer un ADD(Cola,1) para que quede al principio.&lt;br /&gt;
	&lt;br /&gt;
5.	Definir una variable global ( o local estática) que va a guardar el resultado elegido:&lt;br /&gt;
&lt;br /&gt;
 Glo:codigo    short     &lt;br /&gt;
	&lt;br /&gt;
6.	En el evento open window, ANTES de abrir la ventana va el siguiente código&lt;br /&gt;
&lt;br /&gt;
 loop x# = 1 to records(Cola)&lt;br /&gt;
   get(Cola,x#)&lt;br /&gt;
   if col:codigo = glo:codigo then break.&lt;br /&gt;
 end&lt;br /&gt;
 Combo1 = col:descri&lt;br /&gt;
 ?Combo1{prop:selected} = x#&lt;br /&gt;
	&lt;br /&gt;
7.	Al hacer esto, el combo se abre posicionado en el último elemento elegido o en TODOS LOS REGISTROS  si es la primera vez.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== En un report, poner los totales en otra página ==&lt;br /&gt;
&lt;br /&gt;
En algún informe, se puede solicitar como parámetro la opción de imprimir los totales correspondientes en una página nueva. Para ello, después de abrir el report:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 if glo:totpag then&lt;br /&gt;
    settarget(report)&lt;br /&gt;
    ?TituloTotalCategoria{prop:pagebefore} = 1&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
== En un report, cambiar el color de un campo según alguna condición ==&lt;br /&gt;
&lt;br /&gt;
Si durante la impresión de un informe se desea cambiar algún atributo (tal como el color) de un campo puede hacer lo siguiente (antes de imprimir el detalle):&lt;br /&gt;
&lt;br /&gt;
 if cl:impotota &amp;lt;&amp;gt; cl:totacalc&lt;br /&gt;
    settarget(report)&lt;br /&gt;
    ?ARC:importe{PROP:FONTCOLOR} = COLOR:RED&lt;br /&gt;
    settarget(ProgressWindow)&lt;br /&gt;
 else&lt;br /&gt;
    settarget(report)&lt;br /&gt;
    ?ARC:importe{PROP:FONTCOLOR} = COLOR:NONE&lt;br /&gt;
    settarget(ProgressWindow)&lt;br /&gt;
 end&lt;br /&gt;
 print(rpt:Detalle)  ! Imprimir la línea de detalle&lt;br /&gt;
&lt;br /&gt;
== Cerrar todas las ventanas abiertas ==&lt;br /&gt;
 LOOP Thrd# = 2 TO 64 !1 es el Frame&lt;br /&gt;
     POST(Event:CloseWindow,,Thrd#)&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Abrir cualquier archivo con ShellExecute ==&lt;br /&gt;
Hay varios templates gratis que implementan Shellexecute, por ejemplo:&lt;br /&gt;
http://www.sterlingdata.com/shellex.htm&lt;br /&gt;
&lt;br /&gt;
Para hacerlo con codigo:&lt;br /&gt;
&lt;br /&gt;
En Global-embed &#039;Inside the Global Map&#039;:&lt;br /&gt;
 Module(&#039;Win32.lib&#039;)&lt;br /&gt;
 ShellExecute(Long,*CString,*CString,*CString,*CString,Short),UShort,PASCAL,RAW,NAME(&#039;ShellExecuteA&#039;)&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
En Local Data&lt;br /&gt;
 LOC:Handle LONG&lt;br /&gt;
 LOC:Op     CSTRING (255)&lt;br /&gt;
 LOC:File   CSTRING (255)&lt;br /&gt;
 LOC:Path   CSTRING (255)&lt;br /&gt;
 LOC:Param  CSTRING (255)&lt;br /&gt;
 LOC:Show   LONG&lt;br /&gt;
 LOC:RetHandle LONG&lt;br /&gt;
&lt;br /&gt;
En el embed&lt;br /&gt;
 LOC:Handle = 0{PROP:Handle}&lt;br /&gt;
 LOC:Op     = &#039;Open&#039;&lt;br /&gt;
 LOC:File   = &#039;C:\TEST.TXT&#039;&lt;br /&gt;
 LOC:Path   = PATH()&lt;br /&gt;
 LOC:Param  = &#039; &#039;&lt;br /&gt;
 LOC:Show   = 1&lt;br /&gt;
 LOC:RetHandle =  ShellExecute(LOC:Handle,LOC:Op,LOC:File,LOC:Param,LOC:Path,LOC:Show)&lt;br /&gt;
 If LOC:Rethandle &amp;lt;&amp;gt; 0 Then&lt;br /&gt;
   Message(&#039;Error&#039;,&#039;Error&#039;,Icon:Exclamation)&lt;br /&gt;
 End&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Abrir una pagina WEB con ShellExecute ==&lt;br /&gt;
(Ver declaraciones del API y variables en el ejemplo anterior)&lt;br /&gt;
&lt;br /&gt;
En el embed&lt;br /&gt;
 LOC:Handle = 0{PROP:Handle}&lt;br /&gt;
 LOC:Op     = &#039;Open&#039;&lt;br /&gt;
 LOC:File   = &#039;http://www.templatesclarion.com.ar&#039;&lt;br /&gt;
 LOC:Path   = &#039; &#039;&lt;br /&gt;
 LOC:Param  = &#039; &#039;&lt;br /&gt;
 LOC:Show   = 1&lt;br /&gt;
 LOC:RetHandle = ShellExecute(LOC:Handle,LOC:Op,LOC:File,LOC:Param,LOC:Path,LOC:Show)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Convertir un LONG a un String Binario ==&lt;br /&gt;
 binario=&#039;&#039; !binario es CSTRING&lt;br /&gt;
 LOOP F# = 1 TO 32&lt;br /&gt;
    IF BAND(abinario, 1) !abinario es LONG&lt;br /&gt;
        binario =  &#039;1&#039; &amp;amp; binario&lt;br /&gt;
    ELSE&lt;br /&gt;
        binario = &#039;0&#039; &amp;amp; binario&lt;br /&gt;
    END&lt;br /&gt;
    abinario = abinario / 2&lt;br /&gt;
    if abinario = 0 then break.&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Convertir un string binario a LONG ==&lt;br /&gt;
Para volver del CSTRING al LONG seria simplemente&lt;br /&gt;
 X# = EVALUATE (binario &amp;amp; &#039;b&#039;)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Copiar la QUEUE (solo lo que se esta viendo) de un browse a Excel ==&lt;br /&gt;
 Copiar=&#039;&#039; !Cstring de 4.000.000&lt;br /&gt;
 LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
 !Primero una fila con los titulos&lt;br /&gt;
     Copiar= Copiar &amp;amp; ?Browse:1{PropList:Header,C#} &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
&lt;br /&gt;
 LOOP F# = 1 TO RECORDS(BRW1.Q)&lt;br /&gt;
    LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
        GET(BRW1.Q, F#)&lt;br /&gt;
        Copiar= Copiar &amp;amp; FORMAT(WHAT(BRW1.Q, C#), ?Browse:1{PropList:Picture,C#}) &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
    END&lt;br /&gt;
    Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 SETCLIPBOARD(Copiar)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Copiar el VIEW (cuidado: se lee todo) de un browse a Excel ==&lt;br /&gt;
 Copiar=&#039;&#039; !CSTRNG de 4.000.000&lt;br /&gt;
 LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
     Copiar= Copiar &amp;amp; ?Browse:1{PropList:Header,C#} &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
 &lt;br /&gt;
 SET (BRW1.View)&lt;br /&gt;
 LOOP&lt;br /&gt;
    NEXT(BRW1.View)&lt;br /&gt;
    IF ERRORCODE() THEN BREAK.&lt;br /&gt;
    BRW1.SetQueueRecord&lt;br /&gt;
    LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
        Copiar= Copiar &amp;amp; FORMAT(WHAT(BRW1.Q, C#), ?Browse:1{PropList:Picture,C#}) &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
    END&lt;br /&gt;
    Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 SETCLIPBOARD(Copiar)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
Para que no lea todo el contenido del archivo, sino que procese estrictamente lo mismo que muestra el browse, basta con agregar la antes del BRW1.SetQueueRecord el llamado a BRW1.ValidateRecord().&lt;br /&gt;
  ...&lt;br /&gt;
  LOOP&lt;br /&gt;
    NEXT(BRW1.View)&lt;br /&gt;
    IF ERRORCODE() THEN BREAK.&lt;br /&gt;
    IF BRW1.ValidateRecord() THEN CYCLE.&lt;br /&gt;
    BRW1.SetQueueRecrod&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
Daniel Ruzo&lt;br /&gt;
&lt;br /&gt;
== Mostrar iconos en un listbox desde una queue ==&lt;br /&gt;
Ejemplo: marcar en una cola de memoria si un empleado tiene entrada y/o salida&lt;br /&gt;
&lt;br /&gt;
Primero agregar los iconos al Project&lt;br /&gt;
&lt;br /&gt;
En el Codigo:&lt;br /&gt;
&lt;br /&gt;
 cola_empleados    queue, pre(que)&lt;br /&gt;
 nombre        string(30)&lt;br /&gt;
 entro          long&lt;br /&gt;
 entro_ico    long&lt;br /&gt;
 salio          long&lt;br /&gt;
 salio_ico    long&lt;br /&gt;
                         end&lt;br /&gt;
&lt;br /&gt;
En el list:&lt;br /&gt;
&lt;br /&gt;
primer campo: nombre&lt;br /&gt;
&lt;br /&gt;
segundo campo: entro, picture @p p, iconized, transparente&lt;br /&gt;
&lt;br /&gt;
tercer campo: salio, picture @p p, iconized, transparente&lt;br /&gt;
&lt;br /&gt;
En el init de la pantalla:&lt;br /&gt;
&lt;br /&gt;
 ?list{prop:iconlist,1} = &#039;~no.ico&#039;&lt;br /&gt;
 ?list{prop:iconlist,2} = &#039;~si.ico&#039;&lt;br /&gt;
&lt;br /&gt;
Donde cargo la cola y la muestro:&lt;br /&gt;
&lt;br /&gt;
 if condicion  (si NO hay entrada)&lt;br /&gt;
     que:entro_ico = 1    ! icono de no&lt;br /&gt;
 else&lt;br /&gt;
     que:entro_ico = 2    !icono de si&lt;br /&gt;
 end&lt;br /&gt;
 if condicion  (si NO hay salida)&lt;br /&gt;
     que:salio_ico = 1    ! icono de no&lt;br /&gt;
 else&lt;br /&gt;
     que:salio_ico = 2    !icono de si&lt;br /&gt;
 end&lt;br /&gt;
 ! otras asignaciones&lt;br /&gt;
 add(cola_empleados)&lt;br /&gt;
 !&lt;br /&gt;
 display(?list)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Calcular Días Hábiles ==&lt;br /&gt;
&lt;br /&gt;
Ante todo necesitas una tabla de feriados, con al menos un campo llamado,&lt;br /&gt;
por ej., diaferiado y una clave por dicho campo.&lt;br /&gt;
&lt;br /&gt;
Luego podés hacer lo siguiente&lt;br /&gt;
&lt;br /&gt;
 habiles# = 0&lt;br /&gt;
 loop dia# = FechaInicial to FechaFinal&lt;br /&gt;
  if (Dia# % 7) = 0 then cycle. ! porque es domingo&lt;br /&gt;
  if (Dia# % 7) = 6 then cycle. ! porque es sabado&lt;br /&gt;
  !&lt;br /&gt;
  ! busco si es feriado&lt;br /&gt;
  !&lt;br /&gt;
  clear(feriado:record)&lt;br /&gt;
  feriado:diaferiado = dia#&lt;br /&gt;
  if access:feriado.fetch(Feriado:PorDia) = level:benign then cycle. !porque es feriado&lt;br /&gt;
  !&lt;br /&gt;
  habiles# += 1&lt;br /&gt;
 end!loop&lt;br /&gt;
 corridos# = fechafinal - fechainicial&lt;br /&gt;
&lt;br /&gt;
Si los cálculos que vas a realizar son muchos y continuos, sería conveniente&lt;br /&gt;
que la tabla de feriados la cargues en una queue y realices las búsquedas&lt;br /&gt;
sobre ella.&lt;br /&gt;
&lt;br /&gt;
Adrian Gallegos - Mega Sistemas S.R.L.&lt;br /&gt;
&lt;br /&gt;
Otra opción: Días Hábiles del mes - Rutina que quita los días sábados y domingos del mes&lt;br /&gt;
&lt;br /&gt;
CONTAR_DIAS ROUTINE&lt;br /&gt;
    LOC:DIAS = 0&lt;br /&gt;
    tope# = 1&lt;br /&gt;
    LOOP UNTIL DAY(LOC:FECHA) = tope#&lt;br /&gt;
    CASE  LOC:FECHA % 7&lt;br /&gt;
      of  0   ! Domingo&lt;br /&gt;
       LOC:FECHA = LOC:FECHA - 1&lt;br /&gt;
       CYCLE&lt;br /&gt;
      of  6    ! sabado&lt;br /&gt;
       LOC:FECHA = LOC:FECHA - 1&lt;br /&gt;
       CYCLE&lt;br /&gt;
      ELSE&lt;br /&gt;
       LOC:DIAS += 1&lt;br /&gt;
       LOC:FECHA = LOC:FECHA - 1&lt;br /&gt;
     END !CASE&lt;br /&gt;
   END !LOOP&lt;br /&gt;
   IF  LOC:FECHA % 7 = 0 or  LOC:FECHA % 7 = 6 THEN  !si el primero es feriado&lt;br /&gt;
         ! nada&lt;br /&gt;
          else&lt;br /&gt;
         LOC:DIAS  += 1&lt;br /&gt;
   END !IF&lt;br /&gt;
&lt;br /&gt;
Julio César Britez&lt;br /&gt;
&lt;br /&gt;
== Último día del mes y cantidad de días (Lunes, Martes, etc) entre 2 Fechas ==&lt;br /&gt;
Incluye el truco de saber el último día del mes: en la parte &amp;quot; Date(4,1,2005)-1 &amp;quot; significa que le resto 1 al primer día del mes siguiente, lo cual es una forma de obtener el último día del mes actual...&lt;br /&gt;
&lt;br /&gt;
 Loop Fecha# = Date(3,1,2005) TO (Date(4,1,2005)-1)&lt;br /&gt;
    EXECUTE (Fecha# % 7) + 1&lt;br /&gt;
          Domingo# +=1&lt;br /&gt;
          Lunes# +=1&lt;br /&gt;
          Martes# +=1&lt;br /&gt;
          Miercoles# +=1&lt;br /&gt;
          Jueves# +=1&lt;br /&gt;
          Viernes# +=1&lt;br /&gt;
          Sabado# +=1&lt;br /&gt;
    END&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Fecha en Español (por ejemplo en el Frame)==&lt;br /&gt;
Si quieres que funcione independientemente de como este configurado windows,&lt;br /&gt;
lo mejor es poner este embed, al final de WindowManager.Init&lt;br /&gt;
&lt;br /&gt;
 EXECUTE (TODAY() % 7) + 1&lt;br /&gt;
 Dia&amp;quot;= &#039;Domingo&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Lunes&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Martes&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Miercoles&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Jueves&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Viernes&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Sabado&#039;&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
 EXECUTE (MONTH(TODAY()))&lt;br /&gt;
 Mes&amp;quot; = &#039;Enero&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Febrero&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Marzo&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Abril&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Mayo&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Junio&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Julio&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Agosto&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Septiembre&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Octubre&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Noviembre&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Diciembre&#039;&lt;br /&gt;
 END&lt;br /&gt;
 AppFrame{Prop:StatusText,1} = CLIP(Dia&amp;quot;) &amp;amp; &#039; &#039; &amp;amp; DAY(TODAY()) &amp;amp; &#039; de &#039; &amp;amp;&lt;br /&gt;
 CLIP(Mes&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
 Dia&amp;quot; = Choose(((TODAY() % 7) + 1),&#039;Domingo&#039;,&#039;Lunes&#039;,&#039;Martes&#039;,&#039;Miercoles&#039;,&#039;Jueves&#039;,&#039;Viernes&#039;,&#039;Sabado&#039;)&lt;br /&gt;
&lt;br /&gt;
Javier A. Junca Barreto [http://www.sicya.com (SICyA Software - Colombia)]&lt;br /&gt;
&lt;br /&gt;
== Obtener la fecha del server ==&lt;br /&gt;
Este código lee la fecha del servidor, usando el truco de crear un archivo en el servidor y leer la fecha y hora de los atributos:&lt;br /&gt;
&lt;br /&gt;
 !Data&lt;br /&gt;
 LOC:TMP STRING(254),STATIC&lt;br /&gt;
 TMP FILE,DRIVER(&#039;Ascii&#039;),CREATE,NAME(LOC:TMP)&lt;br /&gt;
 RECORD  RECORD&lt;br /&gt;
 LIN STRING(1)&lt;br /&gt;
    .&lt;br /&gt;
    .&lt;br /&gt;
 FILS   QUEUE(File:queue),PRE(FIL)&lt;br /&gt;
       END&lt;br /&gt;
&lt;br /&gt;
 CODE&lt;br /&gt;
  LOC:TMP = PATH()&amp;amp;&#039;\TMP&#039;&amp;amp;RANDOM(10000,99999)&amp;amp;&#039;.TMP&#039;&lt;br /&gt;
  CREATE(TMP)&lt;br /&gt;
  IF NOT ERRORCODE()&lt;br /&gt;
    DIRECTORY(FILS,LOC:TMP,0)&lt;br /&gt;
    REMOVE(TMP)&lt;br /&gt;
    GET(FILS,1)&lt;br /&gt;
    IF TODAY() &amp;lt;&amp;gt; FIL:DATE OR ABS(CLOCK()-FIL:TIME) &amp;gt; 100&lt;br /&gt;
 !FECHA DIFERENTE O 1 SEGUNDO DE DESFASE&lt;br /&gt;
      SETTODAY(FIL:DATE)&lt;br /&gt;
      SETCLOCK(FIL:TIME)&lt;br /&gt;
    END&lt;br /&gt;
  ELSE&lt;br /&gt;
    REMOVE(TMP)&lt;br /&gt;
  END&lt;br /&gt;
&lt;br /&gt;
Carlos Gutierrez&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Con SQL&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La sugerencia de Carlos es muy buena. Si estás usando SQL o drivers ODBC, la otra opción es preguntarle la fecha al motor de base de datos.&lt;br /&gt;
&lt;br /&gt;
La forma genérica de hacerlo es:&lt;br /&gt;
 temp{prop:sql}=&#039;SELECT {fn curdate() }&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Con NET TIME&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Posteado por Diego Sánchez al foro.&lt;br /&gt;
 Run(&#039;NET TIME \\Server_Name /SET /Y&#039;)&lt;br /&gt;
Reemplazar  &amp;quot;Server_Name&amp;quot;  por el nombre del servidor o equipo del cual se desea obtener la hora&lt;br /&gt;
Fue posteado originalmente por un NICOLAS VEILLEUX nveilleux@nbautomation.com, en el foro comp.lang.clarion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Llamar a un Stored Procedure ==&lt;br /&gt;
El código sería mas o menos asi:&lt;br /&gt;
 L:Query = &#039;CALL NombreDelStored (&#039;&#039;&#039; &amp;amp; FORMAT(ParamFecha,@D12) &amp;amp; &#039;&#039;&#039;, &#039;&#039;&#039; &amp;amp;&lt;br /&gt;
 FORMAT(OtraFecha,@D12) &amp;amp; &#039;&#039;&#039;, &#039; &amp;amp; OtroParam1 &amp;amp;&#039;, &#039; &amp;amp; OtroParam2 &amp;amp;&#039;, &#039; &amp;amp;&lt;br /&gt;
 OtroParam3 &amp;amp;&#039;  )&#039;&lt;br /&gt;
&lt;br /&gt;
 ResSQL{prop:sql} = L:Query&lt;br /&gt;
 Loop Until Access:ResSql.Next()&lt;br /&gt;
    MiVariable = R:Campo1&lt;br /&gt;
    etc     = R:Campo2&lt;br /&gt;
 ....&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
En la tabla auxiliar ResSQL obtienes el resultado del último SELECT que&lt;br /&gt;
tenga el Stored Procedure.&lt;br /&gt;
&lt;br /&gt;
Para mas detalles ver el documento sobre SQL Embebido [http://templatesclarion.com.ar/downloads/ (Templates Clarion)]&lt;br /&gt;
&lt;br /&gt;
También recomiendo leer el help &amp;quot;MSSQL Accelerator Calling a Stored&lt;br /&gt;
Procedure&amp;quot;. Ahi está explicado además el uso de valores de retorno y parámetros de salida.&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Encriptación básica ==&lt;br /&gt;
Encriptación / desencriptación básica de un campo usando el metodo XOR.&lt;br /&gt;
 !la primera vez encripta&lt;br /&gt;
 !al volver a aplicar el algoritmo con la misma&lt;br /&gt;
 !Clave de encriptado: desencripta&lt;br /&gt;
 X# = 1&lt;br /&gt;
 loop Y# = 1 to Len(Campo)&lt;br /&gt;
   Campo [Y#] = chr(bxor(val(Campo[Y#]), val(ClaveEncriptado[X#])))&lt;br /&gt;
   X# += 1&lt;br /&gt;
   if X# &amp;gt; len (ClaveEncriptado) then X# = 1.&lt;br /&gt;
 end&lt;br /&gt;
 display&lt;br /&gt;
&lt;br /&gt;
 !Campo y ClaveEncriptado son campos CString&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Generar un archivo de Texto a máxima velocidad==&lt;br /&gt;
Estas son las APIs para generar un archivo de texto sin necesidad de declararlo en el Diccionario.&lt;br /&gt;
&lt;br /&gt;
Además es muy rápido, ideal para exportaciones.&lt;br /&gt;
&lt;br /&gt;
En Global - Inside Global map:&lt;br /&gt;
&lt;br /&gt;
 MODULE(&#039;Windows API&#039;)&lt;br /&gt;
  _lcreat(*CSTRING,SIGNED),SIGNED,PASCAL,RAW&lt;br /&gt;
  _hwrite(SIGNED,*CSTRING,LONG),LONG,PASCAL,RAW&lt;br /&gt;
  _lclose(SIGNED),SIGNED,PASCAL&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Por ejemplo para guardar el contenido de un control Text&lt;br /&gt;
&lt;br /&gt;
 IF NOT FILEDIALOG(&#039;Guardar como&#039;,FileName,&#039;Text|*.TXT|Source|*.CLW&#039;,FILE:Save + FILE:LongName)&lt;br /&gt;
    CYCLE&lt;br /&gt;
 END&lt;br /&gt;
 F# = _lcreat(FileName,0)&lt;br /&gt;
 X# = _hwrite(F#,Texto,LEN(Texto))&lt;br /&gt;
 X# = _lclose(F#)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Efecto BLINK en un campo ==&lt;br /&gt;
Tienes que crear un timer en la ventana, para eso ponle cada cuanto se va a&lt;br /&gt;
ejecutar en la propiedad timer de la ventana. Son centesimas de seg, asi que&lt;br /&gt;
si le pones 50 por ejemplo tu campo va a titilar 2 veces por segundo.&lt;br /&gt;
&lt;br /&gt;
Luego cierra la ventana, vuelve a entrar y vas a encontrar un evento timer&lt;br /&gt;
de la ventana en los embeds.&lt;br /&gt;
&lt;br /&gt;
Window Events --&amp;gt; Timer&lt;br /&gt;
 if ?campo{prop:background} = COLOR:WHITE&lt;br /&gt;
    ?campo{prop:background} = COLOR:SILVER&lt;br /&gt;
 else&lt;br /&gt;
    ?campo{prop:background} = COLOR:WHITE&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Función para calcular dígito verificador en CUIT ==&lt;br /&gt;
- La siguiente función devuelve el numero de CUIT con el dígito verificador&lt;br /&gt;
correcto.&lt;br /&gt;
- El parámetro que recibe es el numero de CUIT a revisar incluyendo el&lt;br /&gt;
dígito verificador.&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
  CuitCliente=&#039;20-15433984-6&#039;&lt;br /&gt;
  IF Cuit(CuitCliente)=CuitCliente THEN&lt;br /&gt;
    MESSAGE(&#039;Digito verificador correcto&#039;)&lt;br /&gt;
  ELSE&lt;br /&gt;
    MESSAGE(&#039;Digito verificador incorrecto&#039;)&lt;br /&gt;
  END&lt;br /&gt;
  ...&lt;br /&gt;
 &lt;br /&gt;
 Cuit         PROCEDURE(cuit1)&lt;br /&gt;
 cuit2        STRING(255)&lt;br /&gt;
 digver       LONG&lt;br /&gt;
 lon          LONG&lt;br /&gt;
 fac          LONG&lt;br /&gt;
 car          STRING(1)&lt;br /&gt;
   &lt;br /&gt;
  CODE&lt;br /&gt;
  cuit2=cuit1&lt;br /&gt;
  digver=0&lt;br /&gt;
  fac=2&lt;br /&gt;
  lon=LEN(CLIP(cuit2))&lt;br /&gt;
  LOOP i#=lon-1 TO 1 BY -1&lt;br /&gt;
    car=SUB(cuit2,i#,1)&lt;br /&gt;
    IF car&amp;lt;&#039;0&#039; OR car&amp;gt;&#039;9&#039; THEN&lt;br /&gt;
      CYCLE&lt;br /&gt;
    .&lt;br /&gt;
    digver=digver+(car*fac)&lt;br /&gt;
    fac+=1&lt;br /&gt;
    IF fac&amp;gt;7 THEN&lt;br /&gt;
      fac=2&lt;br /&gt;
    .&lt;br /&gt;
  .&lt;br /&gt;
  digver=11-(digver%11)&lt;br /&gt;
  IF digver&amp;gt;9 THEN&lt;br /&gt;
    digver=0&lt;br /&gt;
  .&lt;br /&gt;
  cuit2=SUB(cuit2,1,lon-1) &amp;amp; FORMAT(digver,@n01)&lt;br /&gt;
  RETURN(cuit2)&lt;br /&gt;
&lt;br /&gt;
Este codigo esta en la documentacion del template de Impresoras Fiscales&lt;br /&gt;
(BIGSYS TEMPLATES) del amigo Juan Carlos Rodríguez&lt;br /&gt;
&lt;br /&gt;
== Validar Email ==&lt;br /&gt;
Puedes hacerlo con MATCH, el cual devuelve 1 o 0 si el mail no es valido.&lt;br /&gt;
Ejemplo:&lt;br /&gt;
&lt;br /&gt;
 X# =  MATCH(UPPER(CLIP(locemail)),|&lt;br /&gt;
 &#039;^[-A-Z0-9._]+@{{[-A-Z0-9._]+.}+[A-Z][A-Z][A-Z]?[A-Z]?$&#039;, Match:Regular)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Restar Horas==&lt;br /&gt;
Para sacar la diferencia entre horas es simplemente:&lt;br /&gt;
 resultado = hora2  - hora + 1&lt;br /&gt;
&lt;br /&gt;
El +1 es porque sino que faltaria un segundo cuando muestres el resultado (en formato @T6, por ej)&lt;br /&gt;
&lt;br /&gt;
Si Hora2 es del dia siguiente, la cuenta seria:&lt;br /&gt;
 resultado = (hora2 +(100*60*60*24)) - hora + 1&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Digito Verificador para 5 digitos ==&lt;br /&gt;
&lt;br /&gt;
Si tenes un numero de 5 digitos y deses verificar que el mismo es ingresado correctamente podes usar este codigo que genera un digito verificador&lt;br /&gt;
&lt;br /&gt;
Crea una funcion que tenga un parametro (numero a verificar) y retorne el digito verificador correspondiente ejem: DigitoV5(LONG xNumero),BYTE&lt;br /&gt;
&lt;br /&gt;
  loc:Numero = xNumero&lt;br /&gt;
 &lt;br /&gt;
  loc:Valor = (loc:Numero[1]*5) + |&lt;br /&gt;
              (loc:Numero[2]*4) + |&lt;br /&gt;
              (loc:Numero[3]*3) + |&lt;br /&gt;
              (loc:Numero[4]*2) + |&lt;br /&gt;
              (loc:Numero[5]*7)&lt;br /&gt;
 &lt;br /&gt;
  IF (loc:Valor%5) + 1 = 0 OR (loc:Valor%5) + 1 = 1&lt;br /&gt;
    loc:Digito = 0&lt;br /&gt;
  ELSE&lt;br /&gt;
    loc:Digito = 6 - ((loc:Valor%5) + 1)&lt;br /&gt;
  END&lt;br /&gt;
 &lt;br /&gt;
  RETURN loc:Digito&lt;br /&gt;
&lt;br /&gt;
Ruben Garcia [http://www.dipsarg.com (DiPS)]&lt;br /&gt;
&lt;br /&gt;
== Digito Verificador para Cualquier Longitud ==&lt;br /&gt;
&lt;br /&gt;
Si no sabes que longitud puede tener el numero a verificar podes probar verificarla con este codigo&lt;br /&gt;
&lt;br /&gt;
Crea una funcion cuyo parametro es el numero a verificar y retorne el digito verificador. Ej. DigitoV(STRING xNumero),BYTE&lt;br /&gt;
&lt;br /&gt;
  !Inicializa&lt;br /&gt;
  loc:Numero   = xNumero&lt;br /&gt;
  loc:Valor    = 0&lt;br /&gt;
  loc:Multiplo = 1&lt;br /&gt;
 &lt;br /&gt;
  !Barrido y calculo&lt;br /&gt;
  LOOP loc:Posicion = LEN(CLIP(loc:Numero)) TO 1 BY -1&lt;br /&gt;
    loc:Multiplo += 1&lt;br /&gt;
    IF loc:Multiplo &amp;gt; 7&lt;br /&gt;
      loc:Multiplo = 2&lt;br /&gt;
    END&lt;br /&gt;
    loc:Valor += loc:Numero[loc:Posicion] * loc:Multiplo&lt;br /&gt;
  END&lt;br /&gt;
 &lt;br /&gt;
  loc:Digito = loc:Valor % 11&lt;br /&gt;
 &lt;br /&gt;
  IF loc:Digito = 10&lt;br /&gt;
    loc:Digito = 0&lt;br /&gt;
  END&lt;br /&gt;
 &lt;br /&gt;
  RETURN loc:Digito&lt;br /&gt;
&lt;br /&gt;
Ruben Garcia [http://www.dipsarg.com (DiPS)]&lt;br /&gt;
&lt;br /&gt;
== Autoincremento Manual == &lt;br /&gt;
&lt;br /&gt;
Esto se utiliza cuando tenemos una tabla en la cual queremos manejar la clave de auto incremento &lt;br /&gt;
&lt;br /&gt;
 CLEAR(MASTER)&lt;br /&gt;
 SET(MAS:ClavePorID, MAS:ClavePorID)&lt;br /&gt;
 ACCES:MASTER.Previous()&lt;br /&gt;
 IF MAS:Id = 0 THEN &lt;br /&gt;
     MAS:Id = 1&lt;br /&gt;
 ELSE &lt;br /&gt;
     MAS:Id = MAS:Id +1 &lt;br /&gt;
 END &lt;br /&gt;
&lt;br /&gt;
!!!MAS:Id TENDRÍA EL VALOR DEL PROXIMO NUMERO.- &lt;br /&gt;
&lt;br /&gt;
Gracias [mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
Nota: El mecanismo descrito es &amp;quot;confiable&amp;quot; sólo para aplicaciones que usan TPS y en modo single-user. Para Multiusuario y SQL hay que tener cuidado con las lecturas simultáneas del último valor. &lt;br /&gt;
También puede simplificarse el &amp;quot;if ... then ... else ...&amp;quot; como &amp;quot;MAS:Id = MAS:Id +1&amp;quot; (si es cero, será 1, no hace falta chequearlo)&lt;br /&gt;
&lt;br /&gt;
== Desabilitar menu desde cualquier procedimiento ==&lt;br /&gt;
Una opcion seria usando NOTIFY.&lt;br /&gt;
En el Frame&lt;br /&gt;
 Window Events&lt;br /&gt;
    Notify&lt;br /&gt;
        DISABLE = VariableGlobal&lt;br /&gt;
&lt;br /&gt;
En cualquier procedimiento que se necesite deshabilitar un menu&lt;br /&gt;
&lt;br /&gt;
 VariableGlobal = Nro de Use del Menu&lt;br /&gt;
 NOTIFY (999, 1)&lt;br /&gt;
&lt;br /&gt;
Puede ser 999 o cualquier cosa ya que no estoy usando ese parametro.&lt;br /&gt;
(bueno, en realidad podria usar ese parametro en lugar de la global...)&lt;br /&gt;
1 es el Tread del Frame&lt;br /&gt;
&lt;br /&gt;
Como desde los otros procedimientos no existen los use del los items de menu&lt;br /&gt;
(o sea ?menuitem) lo que hay que hacer es ponerles un numero a cada uno.&lt;br /&gt;
Esto se logra poniendolos de esta manera ?use, numero en la definicion del&lt;br /&gt;
menu.&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
==Formatear fechas en SQL ==&lt;br /&gt;
Les paso una función que utilizo para trabajar con las fechas en el SQL parecido al format de clarion. Es muy útil.&lt;br /&gt;
  &lt;br /&gt;
  CREATE FUNCTION dbo.FormatDateTime&lt;br /&gt;
  (&lt;br /&gt;
      @dt DATETIME,&lt;br /&gt;
      @format VARCHAR(16)&lt;br /&gt;
  )&lt;br /&gt;
  RETURNS VARCHAR(64)&lt;br /&gt;
  AS&lt;br /&gt;
  BEGIN&lt;br /&gt;
      DECLARE @dtVC VARCHAR(64)&lt;br /&gt;
      SELECT @dtVC = CASE @format&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;LONGDATE&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          DATENAME(dw, @dt)&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + DATENAME(m, @dt)&lt;br /&gt;
          + SPACE(1) + CAST(DAY(@dt) AS VARCHAR(2))&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + CAST(YEAR(@dt) AS CHAR(4))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;LONGDATEANDTIME&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          DATENAME(dw, @dt)&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + DATENAME(m, @dt)&lt;br /&gt;
          + SPACE(1) + CAST(DAY(@dt) AS VARCHAR(2))&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + CAST(YEAR(@dt) AS CHAR(4))&lt;br /&gt;
          + SPACE(1) + RIGHT(CONVERT(CHAR(20),&lt;br /&gt;
          @dt - CONVERT(DATETIME, CONVERT(CHAR(8),&lt;br /&gt;
          @dt, 112)), 22), 11)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;SHORTDATE&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LEFT(CONVERT(CHAR(19), @dt, 0), 11)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;SHORTDATEANDTIME&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(REPLACE(CONVERT(CHAR(19), @dt, 0),&lt;br /&gt;
              &#039;AM&#039;, &#039; AM&#039;), &#039;PM&#039;, &#039; PM&#039;)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;UNIXTIMESTAMP&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CAST(DATEDIFF(SECOND, &#039;19700101&#039;, @dt)&lt;br /&gt;
          AS VARCHAR(64))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YYYYMMDD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 112)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YYYY-MM-DD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(10), @dt, 23)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YYMMDD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(VARCHAR(8), @dt, 12)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YY-MM-DD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          STUFF(STUFF(CONVERT(VARCHAR(8), @dt, 12),&lt;br /&gt;
          5, 0, &#039;-&#039;), 3, 0, &#039;-&#039;)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MMDDYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(CONVERT(CHAR(8), @dt, 10), &#039;-&#039;, SPACE(0))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MM-DD-YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 10)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MM/DD/YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 1)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MM/DD/YYYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(10), @dt, 101)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DDMMYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(CONVERT(CHAR(8), @dt, 3), &#039;/&#039;, SPACE(0))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DD-MM-YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(CONVERT(CHAR(8), @dt, 3), &#039;/&#039;, &#039;-&#039;)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DD/MM/YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 3)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DD/MM/YYYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(10), @dt, 103)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM:SS 24&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 8)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM 24&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LEFT(CONVERT(VARCHAR(8), @dt, 8), 5)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM:SS 12&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LTRIM(RIGHT(CONVERT(VARCHAR(20), @dt, 22), 11))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM 12&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LTRIM(SUBSTRING(CONVERT(&lt;br /&gt;
          VARCHAR(20), @dt, 22), 10, 5)&lt;br /&gt;
          + RIGHT(CONVERT(VARCHAR(20), @dt, 22), 3))&lt;br /&gt;
  &lt;br /&gt;
      ELSE&lt;br /&gt;
  &lt;br /&gt;
          &#039;Invalid format specified&#039;&lt;br /&gt;
  &lt;br /&gt;
      END&lt;br /&gt;
      RETURN @dtVC&lt;br /&gt;
  END&lt;br /&gt;
  GO&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  Ejemplos:&lt;br /&gt;
  &lt;br /&gt;
  DECLARE @now DATETIME&lt;br /&gt;
  SET @now = GETDATE()&lt;br /&gt;
  &lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;LONGDATE&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;LONGDATEANDTIME&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;SHORTDATE&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;SHORTDATEANDTIME&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;UNIXTIMESTAMP&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YYYYMMDD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YYYY-MM-DD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YYMMDD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YY-MM-DD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MMDDYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MM-DD-YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MM/DD/YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MM/DD/YYYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DDMMYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DD-MM-YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DD/MM/YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DD/MM/YYYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM:SS 24&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM 24&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM:SS 12&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM 12&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;goofy&#039;) &lt;br /&gt;
  &lt;br /&gt;
Posteado al foro por Omar Squiabro&lt;br /&gt;
&lt;br /&gt;
== Anular Tecla Escape ==&lt;br /&gt;
&lt;br /&gt;
Hay veces que se necesita que el usuario salga de una FORM o de una ventana solo cuando pulse un determinado boton o se halla completado alguna condicion. Y en estos casos puede suceder que si el usuario pulsa la tecla ESC cause algun problema&lt;br /&gt;
&lt;br /&gt;
Primero hay que activar la alerta de la tecla esc en el INIT de la ventana&lt;br /&gt;
&lt;br /&gt;
  ALERT(EscKey)&lt;br /&gt;
&lt;br /&gt;
Despues en el evento alertkey&lt;br /&gt;
&lt;br /&gt;
  IF KEYCODE() = EscKey&lt;br /&gt;
    SELECT(?UnDeterminadoCampo)  !1= al primero&lt;br /&gt;
    RETURN Level:Notify          &lt;br /&gt;
  END&lt;br /&gt;
&lt;br /&gt;
Si el codigo embebido es puesto despues del codigo generado por clarion reemplazar el RETURN por CYCLE&lt;br /&gt;
&lt;br /&gt;
Ruben Garcia [http://www.programaya.com (DiPS)]&lt;br /&gt;
&lt;br /&gt;
== Convertir Número Hexadecimal a Binario ==&lt;br /&gt;
A veces, y sobre todo al trabajar con estados de puertos de comunicaciones, necesitamos convertir numeros hexadecimales a binario para establecer errores o tratar envios y recepciones.&lt;br /&gt;
Esta función hace justamente esto de forma sencilla.&lt;br /&gt;
&lt;br /&gt;
 HexaABinario         PROCEDURE(PAR:Nhexa)   ! (String),String&lt;br /&gt;
 HBinario   String(4),dim(16) !Equivalencias&lt;br /&gt;
 RetuBina   String(100)       !Variable Retorno&lt;br /&gt;
&lt;br /&gt;
  CODE&lt;br /&gt;
   HBinario[01] = &#039;0000&#039; !     0h&lt;br /&gt;
   HBinario[02] = &#039;0001&#039; !     1h&lt;br /&gt;
   HBinario[03] = &#039;0010&#039; !     2h&lt;br /&gt;
   HBinario[04] = &#039;0011&#039; !     3h&lt;br /&gt;
   HBinario[05] = &#039;0100&#039; !     4h&lt;br /&gt;
   HBinario[06] = &#039;0101&#039; !     5h &lt;br /&gt;
   HBinario[07] = &#039;0110&#039; !     6h&lt;br /&gt;
   HBinario[08] = &#039;0111&#039; !     7h&lt;br /&gt;
   HBinario[09] = &#039;1000&#039; !     8h &lt;br /&gt;
   HBinario[10] = &#039;1001&#039; !     9h&lt;br /&gt;
   HBinario[11] = &#039;1010&#039; !     Ah  65&lt;br /&gt;
   HBinario[12] = &#039;1011&#039; !     Bh  66&lt;br /&gt;
   HBinario[13] = &#039;1100&#039; !     Ch  67&lt;br /&gt;
   HBinario[14] = &#039;1101&#039; !     Dh  68&lt;br /&gt;
   HBinario[15] = &#039;1110&#039; !     Eh  69&lt;br /&gt;
   HBinario[16] = &#039;1111&#039; !     Fh  70&lt;br /&gt;
   CLEAR(RetuBina)&lt;br /&gt;
   PAR:NHexa = UPPER(PAR:NHexa) !Aseguro letras en mayusculas&lt;br /&gt;
   LOOP I# = 1 to LEN(CLIP(PAR:NHexa))&lt;br /&gt;
     IF NUMERIC(PAR:NHexa[I#]) THEN  !Si es numero es &amp;lt;= 9&lt;br /&gt;
        !Asigno equivalente&lt;br /&gt;
        RetuBina = CLIP(RetuBina) &amp;amp; HBinario[PAR:NHexa[I#]+1]&lt;br /&gt;
     ELSE&lt;br /&gt;
       !Asigno equivalente tomando,por ejemplo, 65 - 54 = 11 para &amp;quot;A&amp;quot;&lt;br /&gt;
        RetuBina = CLIP(RetuBina) &amp;amp; HBinario[VAL(PAR:NHexa[I#])-54]&lt;br /&gt;
     END&lt;br /&gt;
   END&lt;br /&gt;
   !Retorno la cadena binaria sin ceros a la izquierda&lt;br /&gt;
   RETURN(SUB(RetuBina, INSTRING(&#039;1&#039;,RetuBina), LEN(CLIP(RetuBina)))) &lt;br /&gt;
&lt;br /&gt;
Bueno, espero les sea de utilidad!&lt;br /&gt;
&lt;br /&gt;
Mario A. Wojcik&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Transacciones, TCF, Logout, Commit, Rollback ==&lt;br /&gt;
&lt;br /&gt;
Solucion al tema del TCF, crearlo, verlo, etc.&lt;br /&gt;
&lt;br /&gt;
Paso 1&lt;br /&gt;
En el punto embebido del FRAME (windowsManager init (priority 6100) o sea antes que se abra ningun archivo, &lt;br /&gt;
Insertar el siguiente codigo:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GLO:Datos:TCF =  &#039;TCF=&#039; &amp;amp; CLIP(GLO:WorkDir) &amp;amp; &#039;\&#039; &amp;amp; &#039;AR.TCF&#039;&lt;br /&gt;
Send(CLIENTES,GLO:Datos:TCF)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nota 1:&lt;br /&gt;
No es necesario definir en ningun lado el archivo .TCF ni su estructura porque de todo se encarga el driver.&lt;br /&gt;
Nota 2: &lt;br /&gt;
no enviar (SEND) los datos para el .TCF  desde el diccionario, según los autores, esto no funciona.&lt;br /&gt;
Nota 3: Solo es necesario un archivo .TCF para toda la aplicación. &lt;br /&gt;
&lt;br /&gt;
Listo, el archivo de control de transacciones ya esta creado y sin dudas funcionara.&lt;br /&gt;
Para que todo quede completo deberiamos hacer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LOGOUT(1,CabeceraFactura,DetalleFactura)	!Comienza la transaccion&lt;br /&gt;
DO ErrHandler 				!Chequear errores&lt;br /&gt;
ADD(CabeceraFactura) 			!Agregar registro al padre&lt;br /&gt;
DO ErrHandler 				!&lt;br /&gt;
LOOP X# = 1 TO RECORDS(DetailQue) 	!Procesar los registros almacenados&lt;br /&gt;
GET(DetailQue,X#) 				!traer uno de la Cola&lt;br /&gt;
DO ErrHandler 				!&lt;br /&gt;
Det:Record = DetailQue 			!Asignar al record buffer&lt;br /&gt;
ADD(DetalleFactura) 			!Hacer el Add al archivo&lt;br /&gt;
DO ErrHandler 				!&lt;br /&gt;
END&lt;br /&gt;
COMMIT 					!Terminar la transaccion, todo OK&lt;br /&gt;
ASSERT(~ERRORCODE())&lt;br /&gt;
&lt;br /&gt;
ErrHandler ROUTINE 				!Runina de manejo de errores&lt;br /&gt;
IF NOT ERRORCODE() THEN EXIT. 		!Sale si no hay errores&lt;br /&gt;
Err&amp;quot; = ERROR() 				!Guarda el mensaje de error&lt;br /&gt;
ROLLBACK 					!Anula la transaccion&lt;br /&gt;
ASSERT(~ERRORCODE())&lt;br /&gt;
BEEP 						!Avisa al usuario&lt;br /&gt;
MESSAGE(&#039;Transaction Error - &#039; &amp;amp; Err&amp;quot;)&lt;br /&gt;
RETURN 					&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Lo anterior es copia de la ayuda del manual de Clarion 6.3&lt;br /&gt;
&lt;br /&gt;
Para verificar que exista realmente el archivo .TCF podemos usar en cualquer punto de la aplicación lo siguiente:&lt;br /&gt;
&lt;br /&gt;
Poner un string variable en pantalla y en un boton:&lt;br /&gt;
&lt;br /&gt;
Loc:Tcf = SEND(CLIENTES, &#039;TCF&#039;)&lt;br /&gt;
DISPLAY()&lt;br /&gt;
&lt;br /&gt;
Eso deberia ser todo. Por lo menos para mi funciona.&lt;br /&gt;
Carlos Barroso&lt;br /&gt;
DBA Oracle&lt;br /&gt;
Programador Clarion&lt;/div&gt;</summary>
		<author><name>Carlos Barroso</name></author>
	</entry>
	<entry>
		<id>https://clarionwiki.com.ar/index.php?title=Codigo_Util&amp;diff=85</id>
		<title>Codigo Util</title>
		<link rel="alternate" type="text/html" href="https://clarionwiki.com.ar/index.php?title=Codigo_Util&amp;diff=85"/>
		<updated>2015-12-15T15:22:46Z</updated>

		<summary type="html">&lt;p&gt;Carlos Barroso: Solucion al tema del TCF, crearlo, verlo, etc.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;En esta sección se incluyen rutinas de código que nos resultaron útiles en más de una ocasión.&lt;br /&gt;
&lt;br /&gt;
== PDF con cualquier versión de Clarion ==&lt;br /&gt;
Después de mucho rebuscar algo gratis que haga esto, hoy pude componer algo:&lt;br /&gt;
1º) Aporte de Fernando Cerini para &amp;quot;capturar&amp;quot; los .wmf de los reportes de Clarion:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En el embed PrintPreview.Open, despues de Parent call&lt;br /&gt;
 &lt;br /&gt;
 get(SELF.ImageQueue,POINTER(SELF.ImageQueue))&lt;br /&gt;
 LOOP a# = 1 to RECORDS(SELF.ImageQueue)&lt;br /&gt;
          get(SELF.ImageQueue,a#)&lt;br /&gt;
          COPY(SELF.ImageQueue, &#039;c:\temp\Pagina&#039; &amp;amp; a# &amp;amp;&#039;.WMF&#039;)&lt;br /&gt;
          IF ERRORCODE() THEN MESSAGE(ERROR()).&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
En mi caso, con Clarion 5.0 ABC el embed point fue Previewer.Open, después del Parent call&lt;br /&gt;
&lt;br /&gt;
2º) Instalar OpenOffice y seguir las instrucciones de&lt;br /&gt;
http://www.xml.com/pub/a/2006/01/11/from-microsoft-to-openoffice.html&lt;br /&gt;
para crear la macro.&lt;br /&gt;
La verdad, no pude cambiar el nombre de Module1 a Conversiones o algo así, pero no importó mucho.&lt;br /&gt;
&lt;br /&gt;
3º) Cree un batch con la línea&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;C:\Archivos de Programa\OpenOffice.org 2.3\program\soffice&amp;quot; -invisible macro:///Standard.Module1.SaveAsPDF(c:\temp\pagina8.wmf)&lt;br /&gt;
&lt;br /&gt;
Es lo que dice el instructivo mencionado en el punto 2º) acomodado a mi instalación de OpenOffice, lo corrí y funcionó a la primera prueba.&lt;br /&gt;
&lt;br /&gt;
4º) Sólo queda hacer la llamada mediante RUN en el código de Clarion y santo remedio.&lt;br /&gt;
&lt;br /&gt;
Espero sea de ayuda para alguien.&lt;br /&gt;
Saludos&lt;br /&gt;
Ezequiel&lt;br /&gt;
&lt;br /&gt;
== Como filtrar un Tree ==&lt;br /&gt;
Una tecnica de Saul Perez Quezada para filtrar Arboles (generados con el template Relacion Tree), con filtros en runtime que son complejos, y que no podemos hacer con un simple SetFilter().&lt;br /&gt;
&lt;br /&gt;
Alguien ha posteado una pregunta sobre como Filtrar arboles, yo en lo personal he dejado de usar arboles, por que son bastante lentos y deficientes, pero en algunas aplicaciones es bastante util, de manera que comparto la experiencia y les subo una pequeña explicacion de como filtrar Arboles (generados con el template Relacion Tree), con filtros en runtime que son complejos, y que no podemos hacer con un simple SetFilter(), ya que no existe esto en la propiedad.&lt;br /&gt;
 &lt;br /&gt;
Saludos y espero les sirva.&lt;br /&gt;
 &lt;br /&gt;
Como filtrar un Tree con solamente tablas:&lt;br /&gt;
 &lt;br /&gt;
Dentro del Template para armar los arboles, tiene dentro de Primary y Secondary files la opcion de filtrar, agregando un filtro, pero muchas veces dicho filtro no es tan simple de armar, o necesitamos estarlo cambiando en runtime.&lt;br /&gt;
 &lt;br /&gt;
Aqui les presento un caso y como su solucion, esperando que les sirva...&lt;br /&gt;
 &lt;br /&gt;
Tengo tres tablas:&lt;br /&gt;
 &lt;br /&gt;
Abuelos&lt;br /&gt;
Padres&lt;br /&gt;
Hijos&lt;br /&gt;
 &lt;br /&gt;
Y en la tabla hijos, tengo un campo que define en que nivel de escuela en que va cada hijo: Pre-escolar, Primaria, Secundaria, Bachillerato, Universidad, PostGrado, Maestria y Doctorado.&lt;br /&gt;
 &lt;br /&gt;
En la ventana requiero que el usuario pueda filtrar por el nivel escolar, pero con las siguientes condiciones:&lt;br /&gt;
 &lt;br /&gt;
1.- El usuario puede seleccionar si quiere ver cualquier combinacion de estudios, es decir, que pueda ver los de primaria y universidad solamente o todos, o ninguno...&lt;br /&gt;
2.- Si selecciona un filtro donde resulte que un abuelo o padre no tiene hijos que cumplan la condicion, entonces no debe mostrarnos a esos abuelos y padres...&lt;br /&gt;
 &lt;br /&gt;
Considerando que el Tree template, muestra siempre los niveles de acuerdo al filtro que se arma, pues podria convertirse algo engorroso, estar cambiando el filtro, ya que no es una propiedad del relacion tree, tal como sucede con el browse class, sino que el tree filtra a base de If&#039;s interpuestos al llenar el queue...&lt;br /&gt;
 &lt;br /&gt;
Usando el template y solo poniendo los filtros de NIVELESCOLAR = LOC:MINIVEL, solo mostraria los hijos de un nivel, asi que este metodo no nos servira...&lt;br /&gt;
 &lt;br /&gt;
La idea seria entonces que como filtro usaremos una condicion matematica, si la condicion es Verdadera, entonces mostrara el hijo, de lo contrario no lo mostrara.&lt;br /&gt;
 &lt;br /&gt;
Asi que con el comando EVALUATE, evaluaremos un filtro, dentro del secondary files, opcion filter, si el filtro es bueno (igual a 1), entonces nos mostrara la info de lo contrario no...&lt;br /&gt;
 &lt;br /&gt;
Por ejemplo:&lt;br /&gt;
 &lt;br /&gt;
En el secondary files en buscamos la tabla de Hijos y le escribimos en el filtro:&lt;br /&gt;
 &lt;br /&gt;
0&amp;lt;EVALUATE(Filtro) ! Si el filtro es valido, entonces nos mostrara el resultado, de lo contrario no...&lt;br /&gt;
 &lt;br /&gt;
Declaramos un check box por cada cada condicion, con su respectiva variable: Preescolar, Primaria, Secundaria, etc... que sea tipo byte, con valor true = 1 y valor false = 0&lt;br /&gt;
 &lt;br /&gt;
! En el acepted del check box Preescolar... y cada condicion...&lt;br /&gt;
Do Filtrar&lt;br /&gt;
Display&lt;br /&gt;
 &lt;br /&gt;
! En procedure routines&lt;br /&gt;
Filtrar Routine&lt;br /&gt;
  Clear(Filtro) ! Variable para filtrar&lt;br /&gt;
 &lt;br /&gt;
  If Preescolar Then Filtro = &#039;HIJ:NIVELESCOLAR=&#039;&#039;Preescolar&#039;&#039;&#039; ..&lt;br /&gt;
  &lt;br /&gt;
  If Filtro And Primaria&lt;br /&gt;
    Filtro = Clip(Filtro) &amp;amp; &#039; And HIJ:NIVELESCOLAR=&#039;&#039;Primaria&#039;&#039;&#039;&lt;br /&gt;
  ElsIf Filtro And Primaria&lt;br /&gt;
    Filtro = &#039;HIJ:NIVELESCOLAR=&#039;&#039;Primaria&#039;&#039;&#039;&lt;br /&gt;
  End&lt;br /&gt;
  ! Esto se repite con todas las condiciones...&lt;br /&gt;
  ...&lt;br /&gt;
  ...&lt;br /&gt;
  &lt;br /&gt;
  ! Despues de armar el filtro, refrescar el browse...&lt;br /&gt;
 &lt;br /&gt;
  DO REL4::RefreshTree ! Esta rutina la genera el template, se debe buscar en los modulos su nombre correcto.&lt;br /&gt;
  POST(EVENT:NewSelection,?RelTree)&lt;br /&gt;
 &lt;br /&gt;
 Exit  &lt;br /&gt;
 &lt;br /&gt;
Hasta ahora, lo que se ha logrado es que cuando el usuario seleccione un tipo nivel escolar, nos mostrara o no los hijos, pero resulta, que ahora lo que necesito es que solo me muestre los abuelos que tienen nietos, y los padres que tienen hijos, es entonces por que no tiene caso que el arbol se llene con Mil abuelos, si solo 30 de ellos tienen nietos...&lt;br /&gt;
 &lt;br /&gt;
Para hacer debemos entender como se llena el queue del tree, basicamente, el tree, tantas vueltas como combinaciones de nuestra estructura tengamos, es decir, suponiendo que tenemos 3 abuelos, 3 padres, con 3 hijos cada uno, lo que hace el template es:&lt;br /&gt;
 &lt;br /&gt;
Por cada Abuelo Barre (con Loop) la tabla Padres por completo 1 vez y selecciono los que me corresponden y por cada padre barro la tabla hijos (con Loop) por completo y selecciono los que me corresponden...&lt;br /&gt;
 &lt;br /&gt;
Entonces esta estructura daria 3 x 3 x 3 = 27 vueltas. (Ahora imaginen lo que tarda en entre tablas de 10000 registros...)&lt;br /&gt;
 &lt;br /&gt;
Bueno, siguiendo esta idea, entonces lo que necesitamos, es interceptar cada vuelta en el punto donde no queremos que se ingrese al queue y hacer un break.&lt;br /&gt;
 &lt;br /&gt;
Conociendo esto buscamos en nuestros embeds, embedido llamado:&lt;br /&gt;
 &lt;br /&gt;
RelacionTree After Next Primary File y RelationTree After Next Secondary File...&lt;br /&gt;
 &lt;br /&gt;
Aqui buscamos el archivo que requerimos excluir en algunos casos, que es Padres e Hijos, le escribimos en el caso de Padres:&lt;br /&gt;
 &lt;br /&gt;
PAD:AbueloId = ABU:AbueloId&lt;br /&gt;
If Access:Padres.Fetch(FK AbueloId)  ! Si no existen padres que sean dependientes de este abuelo&lt;br /&gt;
  Break          ! hacer el break al Loop del armado del tree...&lt;br /&gt;
End&lt;br /&gt;
 &lt;br /&gt;
Y el caso de los hijos repetimos la condicion:&lt;br /&gt;
 &lt;br /&gt;
HIJ:PadreId = PAD:PadreId&lt;br /&gt;
If Access:Hijos.Fetch(FK PadreId)  ! Si no existen hijos que sean dependientes de este abuelo&lt;br /&gt;
  Break          ! hacer el break al Loop del armado del tree...&lt;br /&gt;
End&lt;br /&gt;
 &lt;br /&gt;
El efecto sera que no nos mostrara aquellos padres y abuelos que no tengan hijos.&lt;br /&gt;
 &lt;br /&gt;
Si requerimos algo mas complejo podemos usar una vista, en vez de una Fetch a una tabla, pero obviamente se alentara esto cada vez mas y mas al llenar el Arbol.&lt;br /&gt;
 &lt;br /&gt;
Nota&lt;br /&gt;
Si usamos un motor de SQL, una alternativa mucho mas eficiente es declarar un Vista en nuestro motor y diccionario que sirva de base para llenar el Arbol, con esto aumentaria considerablemente la velocidad.&lt;br /&gt;
&lt;br /&gt;
== Reemplazar caracteres en un string ==&lt;br /&gt;
Una rutina generica para reemplazar todas las instancias de un string dentro de otro&lt;br /&gt;
&lt;br /&gt;
 Replace       PROCEDURE(string find,string replace,*cstring into)&lt;br /&gt;
 Locate LONG,AUTO&lt;br /&gt;
  CODE&lt;br /&gt;
    IF UPPER(find)&amp;lt;&amp;gt;UPPER(replace)&lt;br /&gt;
       LOOP&lt;br /&gt;
         Locate = INSTRING(UPPER(find),UPPER(into),1,1)&lt;br /&gt;
         IF ~Locate THEN RETURN .&lt;br /&gt;
         into = SUB(into,1,Locate-1) &amp;amp; replace &amp;amp;SUB(into,Locate+LEN(find),LEN(into))&lt;br /&gt;
       END&lt;br /&gt;
    END&lt;br /&gt;
&lt;br /&gt;
Para llamarlo:&lt;br /&gt;
&lt;br /&gt;
Replace(&#039;Busacr&#039;,&#039;Reemplazar&#039;,Loc:miString)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Contar los threads abiertos ==&lt;br /&gt;
Este código fue posteado por Jeff Slarve&lt;br /&gt;
 ChildThreadCount Procedure!,Long&lt;br /&gt;
 Ndx   Long&lt;br /&gt;
 Count Long&lt;br /&gt;
 T     &amp;amp;Window&lt;br /&gt;
 W     &amp;amp;Window&lt;br /&gt;
  Code&lt;br /&gt;
&lt;br /&gt;
  Count = 0&lt;br /&gt;
  SetTarget&lt;br /&gt;
  T &amp;amp;= System{PROP:Target}&lt;br /&gt;
  Loop Ndx = 1 to MaxThreads&lt;br /&gt;
     SetTarget(,Ndx)&lt;br /&gt;
     W &amp;amp;= System{PROP:Target}&lt;br /&gt;
     If NOT W &amp;amp;= T&lt;br /&gt;
       Count += 1&lt;br /&gt;
     end&lt;br /&gt;
  end&lt;br /&gt;
  SetTarget&lt;br /&gt;
  Return Count&lt;br /&gt;
&lt;br /&gt;
== Permitir solo una instancia del EXE en ejecucion ==&lt;br /&gt;
En Inside the global map:&lt;br /&gt;
&lt;br /&gt;
 INCLUDE(&#039;CWUTIL.INC&#039;),ONCE&lt;br /&gt;
&lt;br /&gt;
En Global Program Setup:&lt;br /&gt;
&lt;br /&gt;
 IF NOT BeginUnique(&#039;MiAPP.exe&#039;)&lt;br /&gt;
    BEEP(BEEP:SystemExclamation)&lt;br /&gt;
    YIELD()&lt;br /&gt;
    CASE MESSAGE(&#039;El programa ya esta ejecutando..&#039;,&#039;Ho, ho...&#039;,ICON:Asterisk,BUTTON:OK,BUTTON:OK,0)&lt;br /&gt;
    OF BUTTON:OK&lt;br /&gt;
       HALT()&lt;br /&gt;
    END&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Cambiar los atributos de un archivo ==&lt;br /&gt;
(Del FAQ de SoftVelocity)&lt;br /&gt;
1. En Global Embeds&lt;br /&gt;
&lt;br /&gt;
A. Before Global Includes:&lt;br /&gt;
&lt;br /&gt;
 LPCSTR EQUATE(CSTRING)&lt;br /&gt;
 DWORD EQUATE(ULONG)&lt;br /&gt;
&lt;br /&gt;
B. Global Data: Equates de los artributos&lt;br /&gt;
&lt;br /&gt;
 FILE_ATTRIBUTE_READONLY EQUATE(00000001h)&lt;br /&gt;
 FILE_ATTRIBUTE_HIDDEN EQUATE(00000002h)&lt;br /&gt;
 FILE_ATTRIBUTE_SYSTEM EQUATE(00000004h)&lt;br /&gt;
 FILE_ATTRIBUTE_ARCHIVE EQUATE(00000020h)&lt;br /&gt;
 FILE_ATTRIBUTE_NORMAL EQUATE(00000080h)&lt;br /&gt;
 FILE_ATTRIBUTE_TEMPORARY EQUATE(00000100h)&lt;br /&gt;
&lt;br /&gt;
C. Inside Global Map:&lt;br /&gt;
&lt;br /&gt;
 Module(&#039;Win32.lib&#039;)&lt;br /&gt;
 SetFileAttributes(*CSTRING, ULONG), BOOL, RAW, PASCAL, NAME(&#039;SetFileAttributesA&#039;)&lt;br /&gt;
 GetFileAttributes(*CSTRING), ULONG, RAW, PASCAL, NAME(&#039;GetFileAttributesA&#039;)&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
2. En el codigo:&lt;br /&gt;
&lt;br /&gt;
 filename CSTRING(50)&lt;br /&gt;
&lt;br /&gt;
 filename = &#039;test.txt&#039;&lt;br /&gt;
 y# = GetFileAttributes(filename) ! Leer atributos&lt;br /&gt;
 y# = SetFileAttributes(filename, FILE_ATTRIBUTE_READONLY)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Crear directorios ==&lt;br /&gt;
Hay 2 opciones:&lt;br /&gt;
&lt;br /&gt;
1- Con Clarion (no sé si estarán estas funciones en versiones anteriores de Clarion)&lt;br /&gt;
&lt;br /&gt;
En &amp;quot;Inside the Global map&amp;quot;&lt;br /&gt;
 Include(&#039;clib.clw&#039;)&lt;br /&gt;
&lt;br /&gt;
En Local Data&lt;br /&gt;
 MiDir Cstring(256)&lt;br /&gt;
 Ret     Long&lt;br /&gt;
&lt;br /&gt;
En tu embed&lt;br /&gt;
 MiDir = &#039;Dirtest&#039;&lt;br /&gt;
 Ret = MkDir(MiDir)&lt;br /&gt;
&lt;br /&gt;
2- Con API&lt;br /&gt;
En Global Embeds - inside global map:&lt;br /&gt;
&lt;br /&gt;
 MODULE(&#039;&#039;)&lt;br /&gt;
  DirAccess(*CSTRING,SHORT=0),SHORT,RAW,NAME(&#039;_access&#039;),PROC&lt;br /&gt;
  MkDir(*CSTRING),SHORT,RAW,NAME(&#039;_mkdir&#039;),PROC&lt;br /&gt;
  DirRename(*CSTRING, *CSTRING), SHORT, RAW, NAME(&#039;_rename&#039;),PROC&lt;br /&gt;
  RmDir(*CSTRING),SHORT,RAW,NAME(&#039;_rmdir&#039;),PROC&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
Y en tu embebido podés poner lo siguiente:&lt;br /&gt;
&lt;br /&gt;
 Var=&#039;O:\test&#039;      ! Ojo tiene que ser Cstring&lt;br /&gt;
 IF DirAccess(Var)&amp;lt;&amp;gt;0        !Si no existe&lt;br /&gt;
          MkDir(Var)                    !Crearlo&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
También se puede usar DirRename y RmDir para renombrar o borrar&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Modificar la posicion de un informe ==&lt;br /&gt;
&lt;br /&gt;
Este código permite modificar la posición inicial vertical y horizontal de un informe. Es útil cuando trabajamos con un formulario preimpreso o una hoja membretada y necesitamos desplazar la impresión sin necesidad de reconfigurar el programa.&lt;br /&gt;
&lt;br /&gt;
El punto embebido donde se ubica es After Open the Report.&lt;br /&gt;
&lt;br /&gt;
 !=====================================================&lt;br /&gt;
 ! MODIFICAR LA POSICION DEL INFORME&lt;br /&gt;
 ! El pie de página modificarlo sólo si se usa: &lt;br /&gt;
 ! Por ejemplo se pone ahí el Nro de página o algo así.&lt;br /&gt;
 ! No es necesario cambiar los atributos de las bandas &lt;br /&gt;
 ! de detalle adicionales que se usan en el informe.&lt;br /&gt;
 !-----------------------------------------------------&lt;br /&gt;
 DesplazamientoX =  getini(&#039;Parametros&#039;,|&lt;br /&gt;
                    &#039;DesplazamientoX&#039;,&#039;&#039;,&#039;.\prog.ini&#039;)&lt;br /&gt;
 DesplazamientoY = getini(&#039;Parametros&#039;,|&lt;br /&gt;
                    &#039;DesplazamientoY&#039;,&#039;&#039;,&#039;.\prog.ini&#039;)&lt;br /&gt;
 &lt;br /&gt;
 if DesplazamientoX or DesplazamientoY then&lt;br /&gt;
    SETTARGET(Report)&lt;br /&gt;
    x# = report{prop:Xpos}&lt;br /&gt;
    y# = report{prop:Ypos}&lt;br /&gt;
    x# += DesplazamientoX&lt;br /&gt;
    y# += DesplazamientoY&lt;br /&gt;
    target{prop:Xpos} = x#&lt;br /&gt;
    target{prop:Ypos} = y#&lt;br /&gt;
    settarget&lt;br /&gt;
 &lt;br /&gt;
    SETTARGET(Report,?Encabezado)&lt;br /&gt;
    x# = ?Encabezado{prop:Xpos}&lt;br /&gt;
    y# = ?Encabezado{prop:Ypos}&lt;br /&gt;
    x# += DesplazamientoX&lt;br /&gt;
    y# += DesplazamientoY&lt;br /&gt;
    ?Encabezado{prop:Xpos} = x#&lt;br /&gt;
    ?Encabezado{prop:Ypos} = y#&lt;br /&gt;
    settarget&lt;br /&gt;
 &lt;br /&gt;
    SETTARGET(Report,?PiePagina)&lt;br /&gt;
    x# = ?PiePagina{prop:Xpos}&lt;br /&gt;
    y# = ?PiePagina{prop:Ypos}&lt;br /&gt;
    x# += DesplazamientoX&lt;br /&gt;
    y# += DesplazamientoY&lt;br /&gt;
    ?PiePagina{prop:Xpos} = x#&lt;br /&gt;
    ?PiePagina{prop:Ypos} = y#&lt;br /&gt;
    SETTARGET&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== Generar un informe a partir de una cola en memoria ==&lt;br /&gt;
&lt;br /&gt;
(Sin pasar por el template)&lt;br /&gt;
&lt;br /&gt;
Se puede definir en el módulo en la parte de DATA el reporte necesario y mandarlo a imprimir.&lt;br /&gt;
&lt;br /&gt;
En DATA SECTION&lt;br /&gt;
&lt;br /&gt;
 WMFQue        QUEUE&lt;br /&gt;
 PageImage       STRING(64)&lt;br /&gt;
               END&lt;br /&gt;
 ReportRunDate LONG&lt;br /&gt;
 ReportRunTime LONG&lt;br /&gt;
 !!&amp;gt; Report (portrait) &lt;br /&gt;
 Report  REPORT,AT(1000,2000,6000,7000),THOUS,PRE(RPT),FONT(&#039;Arial&#039;,10)&lt;br /&gt;
         HEADER,AT(1000,1000,6000,1000)&lt;br /&gt;
         END&lt;br /&gt;
 Detail   DETAIL&lt;br /&gt;
         END&lt;br /&gt;
         FOOTER,AT(1000,9000,6000,1000)&lt;br /&gt;
         END&lt;br /&gt;
         FORM,AT(1000,1000,6000,9000)&lt;br /&gt;
         END&lt;br /&gt;
       END&lt;br /&gt;
&lt;br /&gt;
       &lt;br /&gt;
Luego, en el botón que imprime,  poner el siguiente código:&lt;br /&gt;
&lt;br /&gt;
 ReportRunDate = today()&lt;br /&gt;
 ReportRunDate = today()&lt;br /&gt;
 ReportRunTime = clock()&lt;br /&gt;
 OPEN(Report)&lt;br /&gt;
 LOOP x# = 1 to records(ColaError)&lt;br /&gt;
    get(ColaError,x#)&lt;br /&gt;
    PRINT(rpt:DetalleUno)&lt;br /&gt;
 END&lt;br /&gt;
 ENDPAGE(Report)&lt;br /&gt;
 ReportPreview(WMFQue)&lt;br /&gt;
 IF GlobalResponse = RequestCompleted&lt;br /&gt;
    Report{PROP:FlushPreview} = True&lt;br /&gt;
 END&lt;br /&gt;
 CLOSE(Report)&lt;br /&gt;
 FREE(WMFQue)&lt;br /&gt;
&lt;br /&gt;
Este proceso usa el Print Preview de Clarion para visualizar el informe, por consiguiente, la apariencia es totalmente normal para el usuario&lt;br /&gt;
&lt;br /&gt;
== Proceso BATCH que funcione sincronizado con el TIMER de la ventana. ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En un proceso BATCH hecho a mano se puede operar en forma similar al report o al proccess, para eso hay que definir una pantalla que tenga el atributo TIMER (un valor de 1 es suficiente). El código fuente quedaría de la siguiente manera:&lt;br /&gt;
&lt;br /&gt;
 open(PantaTrabaja)&lt;br /&gt;
 Accept&lt;br /&gt;
 case event()&lt;br /&gt;
   of event:openwindow&lt;br /&gt;
      Display()&lt;br /&gt;
      ! Abrir pantalla, Abrir archivos, hacer el set &lt;br /&gt;
      ! Inicial&lt;br /&gt;
   of event:timer&lt;br /&gt;
      ?Mensaje{prop:text} = &#039;Procesando...&#039;&lt;br /&gt;
      display(?Mensaje)&lt;br /&gt;
      loop 2 times&lt;br /&gt;
         next(archivo)&lt;br /&gt;
         if errorcode() then &lt;br /&gt;
            FinArchivo = true&lt;br /&gt;
            Break&lt;br /&gt;
         end&lt;br /&gt;
         ! HACER AQUÍ ALGÚN PROCESO..&lt;br /&gt;
      end&lt;br /&gt;
   of event:closewindow&lt;br /&gt;
      Setcursor()&lt;br /&gt;
      Close(PantaTrabaja)&lt;br /&gt;
      Break&lt;br /&gt;
   End!case&lt;br /&gt;
 &lt;br /&gt;
 case field()&lt;br /&gt;
   of ?BotonCancelar&lt;br /&gt;
      if event() = event:accepted then&lt;br /&gt;
         message(&#039;Proceso cancelado por el usuario.&#039;)&lt;br /&gt;
         post(event:closewindow)&lt;br /&gt;
      end&lt;br /&gt;
   End&lt;br /&gt;
 end!accept&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Drop Combo a mano, con opción a todos y recuerdo de último elegido ==&lt;br /&gt;
&lt;br /&gt;
En muchos de mis informes, cuando pido parámetros asumo lo siguiente, por ejemplo: Si el código de cliente es igual a 0 (cero) se imprimen todos los clientes, sino, se imprime uno solo. Este código permite armar lo mismo con un DropCombo, agregando la opción TODOS LOS XXXX (código Cero).&lt;br /&gt;
&lt;br /&gt;
1. Definir una cola con la siguiente estructura ( por ejemplo ):&lt;br /&gt;
&lt;br /&gt;
 Cola     QUEUE,PRE(col)&lt;br /&gt;
 descri     STRING(20)&lt;br /&gt;
 codigo     SHORT&lt;br /&gt;
          END&lt;br /&gt;
	&lt;br /&gt;
2. Definir una variable local llamada por ejemplo&lt;br /&gt;
&lt;br /&gt;
 Combo1     STRING(20)&lt;br /&gt;
	&lt;br /&gt;
3.	En la pantalla definir una drop combo a mano, en el campo FROM poner el nombre de la COLA, y en el USE poner Combo1 ( no poner ?Combo1 )&lt;br /&gt;
	&lt;br /&gt;
4.	Cargar la cola según corresponda después de abrir archivos, poniendo como último dato el código 0 y el texto &#039;TODOS LOS REGISTROS&#039; y hacer un ADD(Cola,1) para que quede al principio.&lt;br /&gt;
	&lt;br /&gt;
5.	Definir una variable global ( o local estática) que va a guardar el resultado elegido:&lt;br /&gt;
&lt;br /&gt;
 Glo:codigo    short     &lt;br /&gt;
	&lt;br /&gt;
6.	En el evento open window, ANTES de abrir la ventana va el siguiente código&lt;br /&gt;
&lt;br /&gt;
 loop x# = 1 to records(Cola)&lt;br /&gt;
   get(Cola,x#)&lt;br /&gt;
   if col:codigo = glo:codigo then break.&lt;br /&gt;
 end&lt;br /&gt;
 Combo1 = col:descri&lt;br /&gt;
 ?Combo1{prop:selected} = x#&lt;br /&gt;
	&lt;br /&gt;
7.	Al hacer esto, el combo se abre posicionado en el último elemento elegido o en TODOS LOS REGISTROS  si es la primera vez.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== En un report, poner los totales en otra página ==&lt;br /&gt;
&lt;br /&gt;
En algún informe, se puede solicitar como parámetro la opción de imprimir los totales correspondientes en una página nueva. Para ello, después de abrir el report:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 if glo:totpag then&lt;br /&gt;
    settarget(report)&lt;br /&gt;
    ?TituloTotalCategoria{prop:pagebefore} = 1&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
== En un report, cambiar el color de un campo según alguna condición ==&lt;br /&gt;
&lt;br /&gt;
Si durante la impresión de un informe se desea cambiar algún atributo (tal como el color) de un campo puede hacer lo siguiente (antes de imprimir el detalle):&lt;br /&gt;
&lt;br /&gt;
 if cl:impotota &amp;lt;&amp;gt; cl:totacalc&lt;br /&gt;
    settarget(report)&lt;br /&gt;
    ?ARC:importe{PROP:FONTCOLOR} = COLOR:RED&lt;br /&gt;
    settarget(ProgressWindow)&lt;br /&gt;
 else&lt;br /&gt;
    settarget(report)&lt;br /&gt;
    ?ARC:importe{PROP:FONTCOLOR} = COLOR:NONE&lt;br /&gt;
    settarget(ProgressWindow)&lt;br /&gt;
 end&lt;br /&gt;
 print(rpt:Detalle)  ! Imprimir la línea de detalle&lt;br /&gt;
&lt;br /&gt;
== Cerrar todas las ventanas abiertas ==&lt;br /&gt;
 LOOP Thrd# = 2 TO 64 !1 es el Frame&lt;br /&gt;
     POST(Event:CloseWindow,,Thrd#)&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Abrir cualquier archivo con ShellExecute ==&lt;br /&gt;
Hay varios templates gratis que implementan Shellexecute, por ejemplo:&lt;br /&gt;
http://www.sterlingdata.com/shellex.htm&lt;br /&gt;
&lt;br /&gt;
Para hacerlo con codigo:&lt;br /&gt;
&lt;br /&gt;
En Global-embed &#039;Inside the Global Map&#039;:&lt;br /&gt;
 Module(&#039;Win32.lib&#039;)&lt;br /&gt;
 ShellExecute(Long,*CString,*CString,*CString,*CString,Short),UShort,PASCAL,RAW,NAME(&#039;ShellExecuteA&#039;)&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
En Local Data&lt;br /&gt;
 LOC:Handle LONG&lt;br /&gt;
 LOC:Op     CSTRING (255)&lt;br /&gt;
 LOC:File   CSTRING (255)&lt;br /&gt;
 LOC:Path   CSTRING (255)&lt;br /&gt;
 LOC:Param  CSTRING (255)&lt;br /&gt;
 LOC:Show   LONG&lt;br /&gt;
 LOC:RetHandle LONG&lt;br /&gt;
&lt;br /&gt;
En el embed&lt;br /&gt;
 LOC:Handle = 0{PROP:Handle}&lt;br /&gt;
 LOC:Op     = &#039;Open&#039;&lt;br /&gt;
 LOC:File   = &#039;C:\TEST.TXT&#039;&lt;br /&gt;
 LOC:Path   = PATH()&lt;br /&gt;
 LOC:Param  = &#039; &#039;&lt;br /&gt;
 LOC:Show   = 1&lt;br /&gt;
 LOC:RetHandle =  ShellExecute(LOC:Handle,LOC:Op,LOC:File,LOC:Param,LOC:Path,LOC:Show)&lt;br /&gt;
 If LOC:Rethandle &amp;lt;&amp;gt; 0 Then&lt;br /&gt;
   Message(&#039;Error&#039;,&#039;Error&#039;,Icon:Exclamation)&lt;br /&gt;
 End&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Abrir una pagina WEB con ShellExecute ==&lt;br /&gt;
(Ver declaraciones del API y variables en el ejemplo anterior)&lt;br /&gt;
&lt;br /&gt;
En el embed&lt;br /&gt;
 LOC:Handle = 0{PROP:Handle}&lt;br /&gt;
 LOC:Op     = &#039;Open&#039;&lt;br /&gt;
 LOC:File   = &#039;http://www.templatesclarion.com.ar&#039;&lt;br /&gt;
 LOC:Path   = &#039; &#039;&lt;br /&gt;
 LOC:Param  = &#039; &#039;&lt;br /&gt;
 LOC:Show   = 1&lt;br /&gt;
 LOC:RetHandle = ShellExecute(LOC:Handle,LOC:Op,LOC:File,LOC:Param,LOC:Path,LOC:Show)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Convertir un LONG a un String Binario ==&lt;br /&gt;
 binario=&#039;&#039; !binario es CSTRING&lt;br /&gt;
 LOOP F# = 1 TO 32&lt;br /&gt;
    IF BAND(abinario, 1) !abinario es LONG&lt;br /&gt;
        binario =  &#039;1&#039; &amp;amp; binario&lt;br /&gt;
    ELSE&lt;br /&gt;
        binario = &#039;0&#039; &amp;amp; binario&lt;br /&gt;
    END&lt;br /&gt;
    abinario = abinario / 2&lt;br /&gt;
    if abinario = 0 then break.&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Convertir un string binario a LONG ==&lt;br /&gt;
Para volver del CSTRING al LONG seria simplemente&lt;br /&gt;
 X# = EVALUATE (binario &amp;amp; &#039;b&#039;)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Copiar la QUEUE (solo lo que se esta viendo) de un browse a Excel ==&lt;br /&gt;
 Copiar=&#039;&#039; !Cstring de 4.000.000&lt;br /&gt;
 LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
 !Primero una fila con los titulos&lt;br /&gt;
     Copiar= Copiar &amp;amp; ?Browse:1{PropList:Header,C#} &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
&lt;br /&gt;
 LOOP F# = 1 TO RECORDS(BRW1.Q)&lt;br /&gt;
    LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
        GET(BRW1.Q, F#)&lt;br /&gt;
        Copiar= Copiar &amp;amp; FORMAT(WHAT(BRW1.Q, C#), ?Browse:1{PropList:Picture,C#}) &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
    END&lt;br /&gt;
    Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 SETCLIPBOARD(Copiar)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Copiar el VIEW (cuidado: se lee todo) de un browse a Excel ==&lt;br /&gt;
 Copiar=&#039;&#039; !CSTRNG de 4.000.000&lt;br /&gt;
 LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
     Copiar= Copiar &amp;amp; ?Browse:1{PropList:Header,C#} &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
 &lt;br /&gt;
 SET (BRW1.View)&lt;br /&gt;
 LOOP&lt;br /&gt;
    NEXT(BRW1.View)&lt;br /&gt;
    IF ERRORCODE() THEN BREAK.&lt;br /&gt;
    BRW1.SetQueueRecord&lt;br /&gt;
    LOOP C#= 1 TO BRW1.View{Prop:Fields}&lt;br /&gt;
        Copiar= Copiar &amp;amp; FORMAT(WHAT(BRW1.Q, C#), ?Browse:1{PropList:Picture,C#}) &amp;amp; &#039;&amp;lt;9&amp;gt;&#039;&lt;br /&gt;
    END&lt;br /&gt;
    Copiar= Copiar &amp;amp;&#039;&amp;lt;13,10&amp;gt;&#039;&lt;br /&gt;
 END&lt;br /&gt;
 SETCLIPBOARD(Copiar)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
Para que no lea todo el contenido del archivo, sino que procese estrictamente lo mismo que muestra el browse, basta con agregar la antes del BRW1.SetQueueRecord el llamado a BRW1.ValidateRecord().&lt;br /&gt;
  ...&lt;br /&gt;
  LOOP&lt;br /&gt;
    NEXT(BRW1.View)&lt;br /&gt;
    IF ERRORCODE() THEN BREAK.&lt;br /&gt;
    IF BRW1.ValidateRecord() THEN CYCLE.&lt;br /&gt;
    BRW1.SetQueueRecrod&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
Daniel Ruzo&lt;br /&gt;
&lt;br /&gt;
== Mostrar iconos en un listbox desde una queue ==&lt;br /&gt;
Ejemplo: marcar en una cola de memoria si un empleado tiene entrada y/o salida&lt;br /&gt;
&lt;br /&gt;
Primero agregar los iconos al Project&lt;br /&gt;
&lt;br /&gt;
En el Codigo:&lt;br /&gt;
&lt;br /&gt;
 cola_empleados    queue, pre(que)&lt;br /&gt;
 nombre        string(30)&lt;br /&gt;
 entro          long&lt;br /&gt;
 entro_ico    long&lt;br /&gt;
 salio          long&lt;br /&gt;
 salio_ico    long&lt;br /&gt;
                         end&lt;br /&gt;
&lt;br /&gt;
En el list:&lt;br /&gt;
&lt;br /&gt;
primer campo: nombre&lt;br /&gt;
&lt;br /&gt;
segundo campo: entro, picture @p p, iconized, transparente&lt;br /&gt;
&lt;br /&gt;
tercer campo: salio, picture @p p, iconized, transparente&lt;br /&gt;
&lt;br /&gt;
En el init de la pantalla:&lt;br /&gt;
&lt;br /&gt;
 ?list{prop:iconlist,1} = &#039;~no.ico&#039;&lt;br /&gt;
 ?list{prop:iconlist,2} = &#039;~si.ico&#039;&lt;br /&gt;
&lt;br /&gt;
Donde cargo la cola y la muestro:&lt;br /&gt;
&lt;br /&gt;
 if condicion  (si NO hay entrada)&lt;br /&gt;
     que:entro_ico = 1    ! icono de no&lt;br /&gt;
 else&lt;br /&gt;
     que:entro_ico = 2    !icono de si&lt;br /&gt;
 end&lt;br /&gt;
 if condicion  (si NO hay salida)&lt;br /&gt;
     que:salio_ico = 1    ! icono de no&lt;br /&gt;
 else&lt;br /&gt;
     que:salio_ico = 2    !icono de si&lt;br /&gt;
 end&lt;br /&gt;
 ! otras asignaciones&lt;br /&gt;
 add(cola_empleados)&lt;br /&gt;
 !&lt;br /&gt;
 display(?list)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Calcular Días Hábiles ==&lt;br /&gt;
&lt;br /&gt;
Ante todo necesitas una tabla de feriados, con al menos un campo llamado,&lt;br /&gt;
por ej., diaferiado y una clave por dicho campo.&lt;br /&gt;
&lt;br /&gt;
Luego podés hacer lo siguiente&lt;br /&gt;
&lt;br /&gt;
 habiles# = 0&lt;br /&gt;
 loop dia# = FechaInicial to FechaFinal&lt;br /&gt;
  if (Dia# % 7) = 0 then cycle. ! porque es domingo&lt;br /&gt;
  if (Dia# % 7) = 6 then cycle. ! porque es sabado&lt;br /&gt;
  !&lt;br /&gt;
  ! busco si es feriado&lt;br /&gt;
  !&lt;br /&gt;
  clear(feriado:record)&lt;br /&gt;
  feriado:diaferiado = dia#&lt;br /&gt;
  if access:feriado.fetch(Feriado:PorDia) = level:benign then cycle. !porque es feriado&lt;br /&gt;
  !&lt;br /&gt;
  habiles# += 1&lt;br /&gt;
 end!loop&lt;br /&gt;
 corridos# = fechafinal - fechainicial&lt;br /&gt;
&lt;br /&gt;
Si los cálculos que vas a realizar son muchos y continuos, sería conveniente&lt;br /&gt;
que la tabla de feriados la cargues en una queue y realices las búsquedas&lt;br /&gt;
sobre ella.&lt;br /&gt;
&lt;br /&gt;
Adrian Gallegos - Mega Sistemas S.R.L.&lt;br /&gt;
&lt;br /&gt;
Otra opción: Días Hábiles del mes - Rutina que quita los días sábados y domingos del mes&lt;br /&gt;
&lt;br /&gt;
CONTAR_DIAS ROUTINE&lt;br /&gt;
    LOC:DIAS = 0&lt;br /&gt;
    tope# = 1&lt;br /&gt;
    LOOP UNTIL DAY(LOC:FECHA) = tope#&lt;br /&gt;
    CASE  LOC:FECHA % 7&lt;br /&gt;
      of  0   ! Domingo&lt;br /&gt;
       LOC:FECHA = LOC:FECHA - 1&lt;br /&gt;
       CYCLE&lt;br /&gt;
      of  6    ! sabado&lt;br /&gt;
       LOC:FECHA = LOC:FECHA - 1&lt;br /&gt;
       CYCLE&lt;br /&gt;
      ELSE&lt;br /&gt;
       LOC:DIAS += 1&lt;br /&gt;
       LOC:FECHA = LOC:FECHA - 1&lt;br /&gt;
     END !CASE&lt;br /&gt;
   END !LOOP&lt;br /&gt;
   IF  LOC:FECHA % 7 = 0 or  LOC:FECHA % 7 = 6 THEN  !si el primero es feriado&lt;br /&gt;
         ! nada&lt;br /&gt;
          else&lt;br /&gt;
         LOC:DIAS  += 1&lt;br /&gt;
   END !IF&lt;br /&gt;
&lt;br /&gt;
Julio César Britez&lt;br /&gt;
&lt;br /&gt;
== Último día del mes y cantidad de días (Lunes, Martes, etc) entre 2 Fechas ==&lt;br /&gt;
Incluye el truco de saber el último día del mes: en la parte &amp;quot; Date(4,1,2005)-1 &amp;quot; significa que le resto 1 al primer día del mes siguiente, lo cual es una forma de obtener el último día del mes actual...&lt;br /&gt;
&lt;br /&gt;
 Loop Fecha# = Date(3,1,2005) TO (Date(4,1,2005)-1)&lt;br /&gt;
    EXECUTE (Fecha# % 7) + 1&lt;br /&gt;
          Domingo# +=1&lt;br /&gt;
          Lunes# +=1&lt;br /&gt;
          Martes# +=1&lt;br /&gt;
          Miercoles# +=1&lt;br /&gt;
          Jueves# +=1&lt;br /&gt;
          Viernes# +=1&lt;br /&gt;
          Sabado# +=1&lt;br /&gt;
    END&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Fecha en Español (por ejemplo en el Frame)==&lt;br /&gt;
Si quieres que funcione independientemente de como este configurado windows,&lt;br /&gt;
lo mejor es poner este embed, al final de WindowManager.Init&lt;br /&gt;
&lt;br /&gt;
 EXECUTE (TODAY() % 7) + 1&lt;br /&gt;
 Dia&amp;quot;= &#039;Domingo&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Lunes&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Martes&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Miercoles&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Jueves&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Viernes&#039;&lt;br /&gt;
 Dia&amp;quot;= &#039;Sabado&#039;&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
 EXECUTE (MONTH(TODAY()))&lt;br /&gt;
 Mes&amp;quot; = &#039;Enero&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Febrero&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Marzo&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Abril&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Mayo&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Junio&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Julio&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Agosto&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Septiembre&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Octubre&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Noviembre&#039;&lt;br /&gt;
 Mes&amp;quot; = &#039;Diciembre&#039;&lt;br /&gt;
 END&lt;br /&gt;
 AppFrame{Prop:StatusText,1} = CLIP(Dia&amp;quot;) &amp;amp; &#039; &#039; &amp;amp; DAY(TODAY()) &amp;amp; &#039; de &#039; &amp;amp;&lt;br /&gt;
 CLIP(Mes&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
 Dia&amp;quot; = Choose(((TODAY() % 7) + 1),&#039;Domingo&#039;,&#039;Lunes&#039;,&#039;Martes&#039;,&#039;Miercoles&#039;,&#039;Jueves&#039;,&#039;Viernes&#039;,&#039;Sabado&#039;)&lt;br /&gt;
&lt;br /&gt;
Javier A. Junca Barreto [http://www.sicya.com (SICyA Software - Colombia)]&lt;br /&gt;
&lt;br /&gt;
== Obtener la fecha del server ==&lt;br /&gt;
Este código lee la fecha del servidor, usando el truco de crear un archivo en el servidor y leer la fecha y hora de los atributos:&lt;br /&gt;
&lt;br /&gt;
 !Data&lt;br /&gt;
 LOC:TMP STRING(254),STATIC&lt;br /&gt;
 TMP FILE,DRIVER(&#039;Ascii&#039;),CREATE,NAME(LOC:TMP)&lt;br /&gt;
 RECORD  RECORD&lt;br /&gt;
 LIN STRING(1)&lt;br /&gt;
    .&lt;br /&gt;
    .&lt;br /&gt;
 FILS   QUEUE(File:queue),PRE(FIL)&lt;br /&gt;
       END&lt;br /&gt;
&lt;br /&gt;
 CODE&lt;br /&gt;
  LOC:TMP = PATH()&amp;amp;&#039;\TMP&#039;&amp;amp;RANDOM(10000,99999)&amp;amp;&#039;.TMP&#039;&lt;br /&gt;
  CREATE(TMP)&lt;br /&gt;
  IF NOT ERRORCODE()&lt;br /&gt;
    DIRECTORY(FILS,LOC:TMP,0)&lt;br /&gt;
    REMOVE(TMP)&lt;br /&gt;
    GET(FILS,1)&lt;br /&gt;
    IF TODAY() &amp;lt;&amp;gt; FIL:DATE OR ABS(CLOCK()-FIL:TIME) &amp;gt; 100&lt;br /&gt;
 !FECHA DIFERENTE O 1 SEGUNDO DE DESFASE&lt;br /&gt;
      SETTODAY(FIL:DATE)&lt;br /&gt;
      SETCLOCK(FIL:TIME)&lt;br /&gt;
    END&lt;br /&gt;
  ELSE&lt;br /&gt;
    REMOVE(TMP)&lt;br /&gt;
  END&lt;br /&gt;
&lt;br /&gt;
Carlos Gutierrez&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Con SQL&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La sugerencia de Carlos es muy buena. Si estás usando SQL o drivers ODBC, la otra opción es preguntarle la fecha al motor de base de datos.&lt;br /&gt;
&lt;br /&gt;
La forma genérica de hacerlo es:&lt;br /&gt;
 temp{prop:sql}=&#039;SELECT {fn curdate() }&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Con NET TIME&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Posteado por Diego Sánchez al foro.&lt;br /&gt;
 Run(&#039;NET TIME \\Server_Name /SET /Y&#039;)&lt;br /&gt;
Reemplazar  &amp;quot;Server_Name&amp;quot;  por el nombre del servidor o equipo del cual se desea obtener la hora&lt;br /&gt;
Fue posteado originalmente por un NICOLAS VEILLEUX nveilleux@nbautomation.com, en el foro comp.lang.clarion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Llamar a un Stored Procedure ==&lt;br /&gt;
El código sería mas o menos asi:&lt;br /&gt;
 L:Query = &#039;CALL NombreDelStored (&#039;&#039;&#039; &amp;amp; FORMAT(ParamFecha,@D12) &amp;amp; &#039;&#039;&#039;, &#039;&#039;&#039; &amp;amp;&lt;br /&gt;
 FORMAT(OtraFecha,@D12) &amp;amp; &#039;&#039;&#039;, &#039; &amp;amp; OtroParam1 &amp;amp;&#039;, &#039; &amp;amp; OtroParam2 &amp;amp;&#039;, &#039; &amp;amp;&lt;br /&gt;
 OtroParam3 &amp;amp;&#039;  )&#039;&lt;br /&gt;
&lt;br /&gt;
 ResSQL{prop:sql} = L:Query&lt;br /&gt;
 Loop Until Access:ResSql.Next()&lt;br /&gt;
    MiVariable = R:Campo1&lt;br /&gt;
    etc     = R:Campo2&lt;br /&gt;
 ....&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
En la tabla auxiliar ResSQL obtienes el resultado del último SELECT que&lt;br /&gt;
tenga el Stored Procedure.&lt;br /&gt;
&lt;br /&gt;
Para mas detalles ver el documento sobre SQL Embebido [http://templatesclarion.com.ar/downloads/ (Templates Clarion)]&lt;br /&gt;
&lt;br /&gt;
También recomiendo leer el help &amp;quot;MSSQL Accelerator Calling a Stored&lt;br /&gt;
Procedure&amp;quot;. Ahi está explicado además el uso de valores de retorno y parámetros de salida.&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Encriptación básica ==&lt;br /&gt;
Encriptación / desencriptación básica de un campo usando el metodo XOR.&lt;br /&gt;
 !la primera vez encripta&lt;br /&gt;
 !al volver a aplicar el algoritmo con la misma&lt;br /&gt;
 !Clave de encriptado: desencripta&lt;br /&gt;
 X# = 1&lt;br /&gt;
 loop Y# = 1 to Len(Campo)&lt;br /&gt;
   Campo [Y#] = chr(bxor(val(Campo[Y#]), val(ClaveEncriptado[X#])))&lt;br /&gt;
   X# += 1&lt;br /&gt;
   if X# &amp;gt; len (ClaveEncriptado) then X# = 1.&lt;br /&gt;
 end&lt;br /&gt;
 display&lt;br /&gt;
&lt;br /&gt;
 !Campo y ClaveEncriptado son campos CString&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Generar un archivo de Texto a máxima velocidad==&lt;br /&gt;
Estas son las APIs para generar un archivo de texto sin necesidad de declararlo en el Diccionario.&lt;br /&gt;
&lt;br /&gt;
Además es muy rápido, ideal para exportaciones.&lt;br /&gt;
&lt;br /&gt;
En Global - Inside Global map:&lt;br /&gt;
&lt;br /&gt;
 MODULE(&#039;Windows API&#039;)&lt;br /&gt;
  _lcreat(*CSTRING,SIGNED),SIGNED,PASCAL,RAW&lt;br /&gt;
  _hwrite(SIGNED,*CSTRING,LONG),LONG,PASCAL,RAW&lt;br /&gt;
  _lclose(SIGNED),SIGNED,PASCAL&lt;br /&gt;
 END&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Por ejemplo para guardar el contenido de un control Text&lt;br /&gt;
&lt;br /&gt;
 IF NOT FILEDIALOG(&#039;Guardar como&#039;,FileName,&#039;Text|*.TXT|Source|*.CLW&#039;,FILE:Save + FILE:LongName)&lt;br /&gt;
    CYCLE&lt;br /&gt;
 END&lt;br /&gt;
 F# = _lcreat(FileName,0)&lt;br /&gt;
 X# = _hwrite(F#,Texto,LEN(Texto))&lt;br /&gt;
 X# = _lclose(F#)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Efecto BLINK en un campo ==&lt;br /&gt;
Tienes que crear un timer en la ventana, para eso ponle cada cuanto se va a&lt;br /&gt;
ejecutar en la propiedad timer de la ventana. Son centesimas de seg, asi que&lt;br /&gt;
si le pones 50 por ejemplo tu campo va a titilar 2 veces por segundo.&lt;br /&gt;
&lt;br /&gt;
Luego cierra la ventana, vuelve a entrar y vas a encontrar un evento timer&lt;br /&gt;
de la ventana en los embeds.&lt;br /&gt;
&lt;br /&gt;
Window Events --&amp;gt; Timer&lt;br /&gt;
 if ?campo{prop:background} = COLOR:WHITE&lt;br /&gt;
    ?campo{prop:background} = COLOR:SILVER&lt;br /&gt;
 else&lt;br /&gt;
    ?campo{prop:background} = COLOR:WHITE&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Función para calcular dígito verificador en CUIT ==&lt;br /&gt;
- La siguiente función devuelve el numero de CUIT con el dígito verificador&lt;br /&gt;
correcto.&lt;br /&gt;
- El parámetro que recibe es el numero de CUIT a revisar incluyendo el&lt;br /&gt;
dígito verificador.&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
  CuitCliente=&#039;20-15433984-6&#039;&lt;br /&gt;
  IF Cuit(CuitCliente)=CuitCliente THEN&lt;br /&gt;
    MESSAGE(&#039;Digito verificador correcto&#039;)&lt;br /&gt;
  ELSE&lt;br /&gt;
    MESSAGE(&#039;Digito verificador incorrecto&#039;)&lt;br /&gt;
  END&lt;br /&gt;
  ...&lt;br /&gt;
 &lt;br /&gt;
 Cuit         PROCEDURE(cuit1)&lt;br /&gt;
 cuit2        STRING(255)&lt;br /&gt;
 digver       LONG&lt;br /&gt;
 lon          LONG&lt;br /&gt;
 fac          LONG&lt;br /&gt;
 car          STRING(1)&lt;br /&gt;
   &lt;br /&gt;
  CODE&lt;br /&gt;
  cuit2=cuit1&lt;br /&gt;
  digver=0&lt;br /&gt;
  fac=2&lt;br /&gt;
  lon=LEN(CLIP(cuit2))&lt;br /&gt;
  LOOP i#=lon-1 TO 1 BY -1&lt;br /&gt;
    car=SUB(cuit2,i#,1)&lt;br /&gt;
    IF car&amp;lt;&#039;0&#039; OR car&amp;gt;&#039;9&#039; THEN&lt;br /&gt;
      CYCLE&lt;br /&gt;
    .&lt;br /&gt;
    digver=digver+(car*fac)&lt;br /&gt;
    fac+=1&lt;br /&gt;
    IF fac&amp;gt;7 THEN&lt;br /&gt;
      fac=2&lt;br /&gt;
    .&lt;br /&gt;
  .&lt;br /&gt;
  digver=11-(digver%11)&lt;br /&gt;
  IF digver&amp;gt;9 THEN&lt;br /&gt;
    digver=0&lt;br /&gt;
  .&lt;br /&gt;
  cuit2=SUB(cuit2,1,lon-1) &amp;amp; FORMAT(digver,@n01)&lt;br /&gt;
  RETURN(cuit2)&lt;br /&gt;
&lt;br /&gt;
Este codigo esta en la documentacion del template de Impresoras Fiscales&lt;br /&gt;
(BIGSYS TEMPLATES) del amigo Juan Carlos Rodríguez&lt;br /&gt;
&lt;br /&gt;
== Validar Email ==&lt;br /&gt;
Puedes hacerlo con MATCH, el cual devuelve 1 o 0 si el mail no es valido.&lt;br /&gt;
Ejemplo:&lt;br /&gt;
&lt;br /&gt;
 X# =  MATCH(UPPER(CLIP(locemail)),|&lt;br /&gt;
 &#039;^[-A-Z0-9._]+@{{[-A-Z0-9._]+.}+[A-Z][A-Z][A-Z]?[A-Z]?$&#039;, Match:Regular)&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Restar Horas==&lt;br /&gt;
Para sacar la diferencia entre horas es simplemente:&lt;br /&gt;
 resultado = hora2  - hora + 1&lt;br /&gt;
&lt;br /&gt;
El +1 es porque sino que faltaria un segundo cuando muestres el resultado (en formato @T6, por ej)&lt;br /&gt;
&lt;br /&gt;
Si Hora2 es del dia siguiente, la cuenta seria:&lt;br /&gt;
 resultado = (hora2 +(100*60*60*24)) - hora + 1&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
== Digito Verificador para 5 digitos ==&lt;br /&gt;
&lt;br /&gt;
Si tenes un numero de 5 digitos y deses verificar que el mismo es ingresado correctamente podes usar este codigo que genera un digito verificador&lt;br /&gt;
&lt;br /&gt;
Crea una funcion que tenga un parametro (numero a verificar) y retorne el digito verificador correspondiente ejem: DigitoV5(LONG xNumero),BYTE&lt;br /&gt;
&lt;br /&gt;
  loc:Numero = xNumero&lt;br /&gt;
 &lt;br /&gt;
  loc:Valor = (loc:Numero[1]*5) + |&lt;br /&gt;
              (loc:Numero[2]*4) + |&lt;br /&gt;
              (loc:Numero[3]*3) + |&lt;br /&gt;
              (loc:Numero[4]*2) + |&lt;br /&gt;
              (loc:Numero[5]*7)&lt;br /&gt;
 &lt;br /&gt;
  IF (loc:Valor%5) + 1 = 0 OR (loc:Valor%5) + 1 = 1&lt;br /&gt;
    loc:Digito = 0&lt;br /&gt;
  ELSE&lt;br /&gt;
    loc:Digito = 6 - ((loc:Valor%5) + 1)&lt;br /&gt;
  END&lt;br /&gt;
 &lt;br /&gt;
  RETURN loc:Digito&lt;br /&gt;
&lt;br /&gt;
Ruben Garcia [http://www.dipsarg.com (DiPS)]&lt;br /&gt;
&lt;br /&gt;
== Digito Verificador para Cualquier Longitud ==&lt;br /&gt;
&lt;br /&gt;
Si no sabes que longitud puede tener el numero a verificar podes probar verificarla con este codigo&lt;br /&gt;
&lt;br /&gt;
Crea una funcion cuyo parametro es el numero a verificar y retorne el digito verificador. Ej. DigitoV(STRING xNumero),BYTE&lt;br /&gt;
&lt;br /&gt;
  !Inicializa&lt;br /&gt;
  loc:Numero   = xNumero&lt;br /&gt;
  loc:Valor    = 0&lt;br /&gt;
  loc:Multiplo = 1&lt;br /&gt;
 &lt;br /&gt;
  !Barrido y calculo&lt;br /&gt;
  LOOP loc:Posicion = LEN(CLIP(loc:Numero)) TO 1 BY -1&lt;br /&gt;
    loc:Multiplo += 1&lt;br /&gt;
    IF loc:Multiplo &amp;gt; 7&lt;br /&gt;
      loc:Multiplo = 2&lt;br /&gt;
    END&lt;br /&gt;
    loc:Valor += loc:Numero[loc:Posicion] * loc:Multiplo&lt;br /&gt;
  END&lt;br /&gt;
 &lt;br /&gt;
  loc:Digito = loc:Valor % 11&lt;br /&gt;
 &lt;br /&gt;
  IF loc:Digito = 10&lt;br /&gt;
    loc:Digito = 0&lt;br /&gt;
  END&lt;br /&gt;
 &lt;br /&gt;
  RETURN loc:Digito&lt;br /&gt;
&lt;br /&gt;
Ruben Garcia [http://www.dipsarg.com (DiPS)]&lt;br /&gt;
&lt;br /&gt;
== Autoincremento Manual == &lt;br /&gt;
&lt;br /&gt;
Esto se utiliza cuando tenemos una tabla en la cual queremos manejar la clave de auto incremento &lt;br /&gt;
&lt;br /&gt;
 CLEAR(MASTER)&lt;br /&gt;
 SET(MAS:ClavePorID, MAS:ClavePorID)&lt;br /&gt;
 ACCES:MASTER.Previous()&lt;br /&gt;
 IF MAS:Id = 0 THEN &lt;br /&gt;
     MAS:Id = 1&lt;br /&gt;
 ELSE &lt;br /&gt;
     MAS:Id = MAS:Id +1 &lt;br /&gt;
 END &lt;br /&gt;
&lt;br /&gt;
!!!MAS:Id TENDRÍA EL VALOR DEL PROXIMO NUMERO.- &lt;br /&gt;
&lt;br /&gt;
Gracias [mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
Nota: El mecanismo descrito es &amp;quot;confiable&amp;quot; sólo para aplicaciones que usan TPS y en modo single-user. Para Multiusuario y SQL hay que tener cuidado con las lecturas simultáneas del último valor. &lt;br /&gt;
También puede simplificarse el &amp;quot;if ... then ... else ...&amp;quot; como &amp;quot;MAS:Id = MAS:Id +1&amp;quot; (si es cero, será 1, no hace falta chequearlo)&lt;br /&gt;
&lt;br /&gt;
== Desabilitar menu desde cualquier procedimiento ==&lt;br /&gt;
Una opcion seria usando NOTIFY.&lt;br /&gt;
En el Frame&lt;br /&gt;
 Window Events&lt;br /&gt;
    Notify&lt;br /&gt;
        DISABLE = VariableGlobal&lt;br /&gt;
&lt;br /&gt;
En cualquier procedimiento que se necesite deshabilitar un menu&lt;br /&gt;
&lt;br /&gt;
 VariableGlobal = Nro de Use del Menu&lt;br /&gt;
 NOTIFY (999, 1)&lt;br /&gt;
&lt;br /&gt;
Puede ser 999 o cualquier cosa ya que no estoy usando ese parametro.&lt;br /&gt;
(bueno, en realidad podria usar ese parametro en lugar de la global...)&lt;br /&gt;
1 es el Tread del Frame&lt;br /&gt;
&lt;br /&gt;
Como desde los otros procedimientos no existen los use del los items de menu&lt;br /&gt;
(o sea ?menuitem) lo que hay que hacer es ponerles un numero a cada uno.&lt;br /&gt;
Esto se logra poniendolos de esta manera ?use, numero en la definicion del&lt;br /&gt;
menu.&lt;br /&gt;
&lt;br /&gt;
[mailto:Fernando_Cerini@hotmail.com Fernando Cerini]&lt;br /&gt;
&lt;br /&gt;
==Formatear fechas en SQL ==&lt;br /&gt;
Les paso una función que utilizo para trabajar con las fechas en el SQL parecido al format de clarion. Es muy útil.&lt;br /&gt;
  &lt;br /&gt;
  CREATE FUNCTION dbo.FormatDateTime&lt;br /&gt;
  (&lt;br /&gt;
      @dt DATETIME,&lt;br /&gt;
      @format VARCHAR(16)&lt;br /&gt;
  )&lt;br /&gt;
  RETURNS VARCHAR(64)&lt;br /&gt;
  AS&lt;br /&gt;
  BEGIN&lt;br /&gt;
      DECLARE @dtVC VARCHAR(64)&lt;br /&gt;
      SELECT @dtVC = CASE @format&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;LONGDATE&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          DATENAME(dw, @dt)&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + DATENAME(m, @dt)&lt;br /&gt;
          + SPACE(1) + CAST(DAY(@dt) AS VARCHAR(2))&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + CAST(YEAR(@dt) AS CHAR(4))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;LONGDATEANDTIME&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          DATENAME(dw, @dt)&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + DATENAME(m, @dt)&lt;br /&gt;
          + SPACE(1) + CAST(DAY(@dt) AS VARCHAR(2))&lt;br /&gt;
          + &#039;,&#039; + SPACE(1) + CAST(YEAR(@dt) AS CHAR(4))&lt;br /&gt;
          + SPACE(1) + RIGHT(CONVERT(CHAR(20),&lt;br /&gt;
          @dt - CONVERT(DATETIME, CONVERT(CHAR(8),&lt;br /&gt;
          @dt, 112)), 22), 11)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;SHORTDATE&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LEFT(CONVERT(CHAR(19), @dt, 0), 11)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;SHORTDATEANDTIME&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(REPLACE(CONVERT(CHAR(19), @dt, 0),&lt;br /&gt;
              &#039;AM&#039;, &#039; AM&#039;), &#039;PM&#039;, &#039; PM&#039;)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;UNIXTIMESTAMP&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CAST(DATEDIFF(SECOND, &#039;19700101&#039;, @dt)&lt;br /&gt;
          AS VARCHAR(64))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YYYYMMDD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 112)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YYYY-MM-DD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(10), @dt, 23)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YYMMDD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(VARCHAR(8), @dt, 12)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;YY-MM-DD&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          STUFF(STUFF(CONVERT(VARCHAR(8), @dt, 12),&lt;br /&gt;
          5, 0, &#039;-&#039;), 3, 0, &#039;-&#039;)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MMDDYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(CONVERT(CHAR(8), @dt, 10), &#039;-&#039;, SPACE(0))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MM-DD-YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 10)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MM/DD/YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 1)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;MM/DD/YYYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(10), @dt, 101)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DDMMYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(CONVERT(CHAR(8), @dt, 3), &#039;/&#039;, SPACE(0))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DD-MM-YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          REPLACE(CONVERT(CHAR(8), @dt, 3), &#039;/&#039;, &#039;-&#039;)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DD/MM/YY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 3)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;DD/MM/YYYY&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(10), @dt, 103)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM:SS 24&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          CONVERT(CHAR(8), @dt, 8)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM 24&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LEFT(CONVERT(VARCHAR(8), @dt, 8), 5)&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM:SS 12&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LTRIM(RIGHT(CONVERT(VARCHAR(20), @dt, 22), 11))&lt;br /&gt;
  &lt;br /&gt;
      WHEN &#039;HH:MM 12&#039; THEN&lt;br /&gt;
  &lt;br /&gt;
          LTRIM(SUBSTRING(CONVERT(&lt;br /&gt;
          VARCHAR(20), @dt, 22), 10, 5)&lt;br /&gt;
          + RIGHT(CONVERT(VARCHAR(20), @dt, 22), 3))&lt;br /&gt;
  &lt;br /&gt;
      ELSE&lt;br /&gt;
  &lt;br /&gt;
          &#039;Invalid format specified&#039;&lt;br /&gt;
  &lt;br /&gt;
      END&lt;br /&gt;
      RETURN @dtVC&lt;br /&gt;
  END&lt;br /&gt;
  GO&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  Ejemplos:&lt;br /&gt;
  &lt;br /&gt;
  DECLARE @now DATETIME&lt;br /&gt;
  SET @now = GETDATE()&lt;br /&gt;
  &lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;LONGDATE&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;LONGDATEANDTIME&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;SHORTDATE&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;SHORTDATEANDTIME&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;UNIXTIMESTAMP&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YYYYMMDD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YYYY-MM-DD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YYMMDD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;YY-MM-DD&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MMDDYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MM-DD-YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MM/DD/YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;MM/DD/YYYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DDMMYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DD-MM-YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DD/MM/YY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;DD/MM/YYYY&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM:SS 24&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM 24&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM:SS 12&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;HH:MM 12&#039;)&lt;br /&gt;
  PRINT dbo.FormatDateTime(@now, &#039;goofy&#039;) &lt;br /&gt;
  &lt;br /&gt;
Posteado al foro por Omar Squiabro&lt;br /&gt;
&lt;br /&gt;
== Anular Tecla Escape ==&lt;br /&gt;
&lt;br /&gt;
Hay veces que se necesita que el usuario salga de una FORM o de una ventana solo cuando pulse un determinado boton o se halla completado alguna condicion. Y en estos casos puede suceder que si el usuario pulsa la tecla ESC cause algun problema&lt;br /&gt;
&lt;br /&gt;
Primero hay que activar la alerta de la tecla esc en el INIT de la ventana&lt;br /&gt;
&lt;br /&gt;
  ALERT(EscKey)&lt;br /&gt;
&lt;br /&gt;
Despues en el evento alertkey&lt;br /&gt;
&lt;br /&gt;
  IF KEYCODE() = EscKey&lt;br /&gt;
    SELECT(?UnDeterminadoCampo)  !1= al primero&lt;br /&gt;
    RETURN Level:Notify          &lt;br /&gt;
  END&lt;br /&gt;
&lt;br /&gt;
Si el codigo embebido es puesto despues del codigo generado por clarion reemplazar el RETURN por CYCLE&lt;br /&gt;
&lt;br /&gt;
Ruben Garcia [http://www.programaya.com (DiPS)]&lt;br /&gt;
&lt;br /&gt;
== Convertir Número Hexadecimal a Binario ==&lt;br /&gt;
A veces, y sobre todo al trabajar con estados de puertos de comunicaciones, necesitamos convertir numeros hexadecimales a binario para establecer errores o tratar envios y recepciones.&lt;br /&gt;
Esta función hace justamente esto de forma sencilla.&lt;br /&gt;
&lt;br /&gt;
 HexaABinario         PROCEDURE(PAR:Nhexa)   ! (String),String&lt;br /&gt;
 HBinario   String(4),dim(16) !Equivalencias&lt;br /&gt;
 RetuBina   String(100)       !Variable Retorno&lt;br /&gt;
&lt;br /&gt;
  CODE&lt;br /&gt;
   HBinario[01] = &#039;0000&#039; !     0h&lt;br /&gt;
   HBinario[02] = &#039;0001&#039; !     1h&lt;br /&gt;
   HBinario[03] = &#039;0010&#039; !     2h&lt;br /&gt;
   HBinario[04] = &#039;0011&#039; !     3h&lt;br /&gt;
   HBinario[05] = &#039;0100&#039; !     4h&lt;br /&gt;
   HBinario[06] = &#039;0101&#039; !     5h &lt;br /&gt;
   HBinario[07] = &#039;0110&#039; !     6h&lt;br /&gt;
   HBinario[08] = &#039;0111&#039; !     7h&lt;br /&gt;
   HBinario[09] = &#039;1000&#039; !     8h &lt;br /&gt;
   HBinario[10] = &#039;1001&#039; !     9h&lt;br /&gt;
   HBinario[11] = &#039;1010&#039; !     Ah  65&lt;br /&gt;
   HBinario[12] = &#039;1011&#039; !     Bh  66&lt;br /&gt;
   HBinario[13] = &#039;1100&#039; !     Ch  67&lt;br /&gt;
   HBinario[14] = &#039;1101&#039; !     Dh  68&lt;br /&gt;
   HBinario[15] = &#039;1110&#039; !     Eh  69&lt;br /&gt;
   HBinario[16] = &#039;1111&#039; !     Fh  70&lt;br /&gt;
   CLEAR(RetuBina)&lt;br /&gt;
   PAR:NHexa = UPPER(PAR:NHexa) !Aseguro letras en mayusculas&lt;br /&gt;
   LOOP I# = 1 to LEN(CLIP(PAR:NHexa))&lt;br /&gt;
     IF NUMERIC(PAR:NHexa[I#]) THEN  !Si es numero es &amp;lt;= 9&lt;br /&gt;
        !Asigno equivalente&lt;br /&gt;
        RetuBina = CLIP(RetuBina) &amp;amp; HBinario[PAR:NHexa[I#]+1]&lt;br /&gt;
     ELSE&lt;br /&gt;
       !Asigno equivalente tomando,por ejemplo, 65 - 54 = 11 para &amp;quot;A&amp;quot;&lt;br /&gt;
        RetuBina = CLIP(RetuBina) &amp;amp; HBinario[VAL(PAR:NHexa[I#])-54]&lt;br /&gt;
     END&lt;br /&gt;
   END&lt;br /&gt;
   !Retorno la cadena binaria sin ceros a la izquierda&lt;br /&gt;
   RETURN(SUB(RetuBina, INSTRING(&#039;1&#039;,RetuBina), LEN(CLIP(RetuBina)))) &lt;br /&gt;
&lt;br /&gt;
Bueno, espero les sea de utilidad!&lt;br /&gt;
&lt;br /&gt;
Mario A. Wojcik&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Transacciones, TCF, Logout, Commit, Rollback ==&lt;br /&gt;
&lt;br /&gt;
Solucion al tema del TCF, crearlo, verlo, etc.&lt;br /&gt;
&lt;br /&gt;
Paso 1&lt;br /&gt;
En el punto embebido del FRAME (windowsManager init (priority 6100) o sea antes que se abra ningun archivo, &lt;br /&gt;
Insertar el siguiente codigo:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GLO:Datos:TCF =  &#039;TCF=&#039; &amp;amp; CLIP(GLO:WorkDir) &amp;amp; &#039;\&#039; &amp;amp; &#039;AR.TCF&#039;&lt;br /&gt;
Send(CLIENTES,GLO:Datos:TCF)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nota 1:&lt;br /&gt;
No es necesario definir en ningun lado el archivo .TCF ni su estructura porque de todo se encarga el driver.&lt;br /&gt;
Nota 2: &lt;br /&gt;
no enviar (SEND) los datos para el .TCF  desde el diccionario, según los autores, esto no funciona.&lt;br /&gt;
Nota 3: Solo es necesario un archivo .TCF para toda la aplicación. &lt;br /&gt;
&lt;br /&gt;
Listo, el archivo de control de transacciones ya esta creado y sin dudas funcionara.&lt;br /&gt;
Para que todo quede completo deberiamos hacer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LOGOUT(1,CabeceraFactura,DetalleFactura)	!Comienza la transaccion&lt;br /&gt;
DO ErrHandler 				!Chequear errores&lt;br /&gt;
ADD(CabeceraFactura) 			!Agregar registro al padre&lt;br /&gt;
DO ErrHandler 				!&lt;br /&gt;
LOOP X# = 1 TO RECORDS(DetailQue) 	!Procesar los registros almacenados&lt;br /&gt;
GET(DetailQue,X#) 				!traer uno de la Cola&lt;br /&gt;
DO ErrHandler 				!&lt;br /&gt;
Det:Record = DetailQue 			!Asignar al record buffer&lt;br /&gt;
ADD(DetalleFactura) 			!Hacer el Add al archivo&lt;br /&gt;
DO ErrHandler 				!&lt;br /&gt;
END&lt;br /&gt;
COMMIT 					!Terminar la transaccion, todo OK&lt;br /&gt;
ASSERT(~ERRORCODE())&lt;br /&gt;
&lt;br /&gt;
ErrHandler ROUTINE 				!Runina de manejo de errores&lt;br /&gt;
IF NOT ERRORCODE() THEN EXIT. 		!Sale si no hay errores&lt;br /&gt;
Err&amp;quot; = ERROR() 				!Guarda el mensaje de error&lt;br /&gt;
ROLLBACK 					!Anula la transaccion&lt;br /&gt;
ASSERT(~ERRORCODE())&lt;br /&gt;
BEEP 						!Avisa al usuario&lt;br /&gt;
MESSAGE(&#039;Transaction Error - &#039; &amp;amp; Err&amp;quot;)&lt;br /&gt;
RETURN 					&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Lo anterior es copia de la ayuda del manual de Clarion 6.3&lt;br /&gt;
&lt;br /&gt;
Para verificar que exista realmente el archivo .TCF podemos usar en cualquer punto de la aplicación lo siguiente:&lt;br /&gt;
&lt;br /&gt;
Poner un string variable en pantalla y en un boton:&lt;br /&gt;
&lt;br /&gt;
Loc:Tcf = SEND(CLIENTES, &#039;TCF&#039;)&lt;br /&gt;
DISPLAY()&lt;br /&gt;
&lt;br /&gt;
Eso deberia ser todo. Por lo menos para mi funciona.&lt;/div&gt;</summary>
		<author><name>Carlos Barroso</name></author>
	</entry>
</feed>