Clipper 5.x

Respuestas del Gurú.

 

Índice general  He notado que en muchas oportunidades me consultan una y otra vez lo mismo, por lo que decidí abrir este sector donde a medida que el tiempo me lo permita iré publicando las preguntas mas frecuentes que me realizan los programadores.
Los textos de color
verde indican la respuesta.

 


(14-Abr-2003)
Agrandar la letra usando PCL 5

>Bien resulta que esta impresora ya ha dado todo lo que podía y mas y ahora
>he comprado un HP cuya única emulación es "PCL 5" he mirado de adaptar el
>programa a este lenguaje pero me encuentro con lo siguiente.
>

Si, PCL es correcto.

>En IBM PROP. podía crear caracteres mas grandes con solo una instrucción de
>escape y bajo PCL no encuentro esta secuencia.
>

Es muy simple, debes buscar en mi página:
http://www.mundoprogramacion.com/Clipper/pcl.htm


>Por lo que la pregunta es obvia ¿conoces la forma de conseguir que los
>caracteres se puedan hacer mas grandes ?

Por las dudas que no comprendas como se utilizan, te adjunto un ejemplo mas
claro.


>En el manual que tengo de IBM esta posibilidad se llama "ENLARGED"
>

Haaaaggg el manual, no entiendo porque no ponen los códigos PCL allí.


Archivo ejemprn.prg (- de 1 Kb)


(08-Abr-2003)
Un buen ejemplo de como enlazar .PRG's

>amigo, estoy programando, pero todo lo tengo que concentar en un solo
>archivo (fuente), Pues si los trabajo por modulos, al compilar, me manda
>Error, recuerdo que hay un comando para hacerlos llamar internamente cada
>uno de los modulos
>por ejemplo:
>
>mi programa se llama cotización.prg, pero en ese archivo, hay un menu
>principal, que llama al segundo o tercer modolu, pero es lo que no recuerdo
>como hacerlo, si es con "DO", me puede ayudar amigo, y al momento de Linkar
>solo llamar al principal, y los demás en automatico se compilan

Me imagino.

Bueno, te adjunto un ejemplo en 4 archivos, espero lo entiendas.
Lo compilas así:
Clipper menu
blinker fi menu

y listo.

Tienes el programa principal : MENU.PRG
Dos programas que son llamados PROG1.PRG y PROG2.PRG
y el archivo de funciones Funcs.prg

Archivo prgs.zip (3 Kb)


(07-Abr-2003)
Encriptar bases de datos con Clipper puro.

>mi pregunta es si hay la posibilidad
>de encriptar los archivos en dbf y como hacerlo.
>

Hay dos maneras de hacerlo, una fácil pero cara y un poquito mas laboriosa
aunque gratis, y con la ventaja de tener el fuente del encriptor, ¿Cuál
supones que yo prefiera?.

La fácil es con librerías de terceros como la DriverSix, donde te olvidas
del tema ya que la encripción/desencripción se realiza de manera desatendida
y el programador solo debe abrir las bases de datos en forma encriptada y
olvidarse del tema.
Estas librerías son pagas y muy caras por cierto.

Lo que hago es utilizar una rutina de encripción/desencripción que yo mismo
creé. La misma encripta datos de cualquier tipo con claves de cualquier tipo
y devuelve un valor de la misma longitud.

Cript(xDatos, xClave)
Encripta una variable de cualquier tipo ya sea numérica, caracter, de fecha
o lógica y devuelve los datos del mismo tipo y largo que la variable
original, ideal para encriptar bases de datos.

xDatos = Datos a encrptar puede ser numérica, lógica, caracter o fecha.
xClave = Clave que puede ser numérica, lógica, caracter o fecha.

Devuelve un valor encriptado del mismo tipo y ancho que el original.

Ejemplo:

Cript("Esto es una prueba", 1479)
===> Una serie de caracteres ilegibles
Cript("Esto es una prueba", "JAQUECA")
===> Una serie de caracteres ilegibles
Cript(1677, "CLAVE")
===> 8402
Cript(.T., 1479)
===> .F.
Cript(.T., 1478)
===> .T.

DeCript(xDatos, xClave)
Desencripta los datos encriptados por la función Cript()

xDatos = Datos a desencrptar puede ser numérica, lógica, caracter o fecha.
xClave = Clave que puede ser numérica, lógica, caracter o fecha.

Devuelve el valor original desencriptado.

Y los uso de la siguiente manera:

Ha la hora de grabar datos en una base :
[...]
Replace Base->Campo With Cript(xDato, xClave)
[...]

En un TBrow:

oTBrow:AddColumn(TbColumnNew("Nombre" ,;
{|| deCript(Base->Dato, xClave) }))


Y de la misma manera debes encriptar los datos antes de guardarlos y
desencriptarlos al mostrarlos.

Te adjunto un archivo con las rutinas, espero haberte ayudado.

Archivo encdec.prg (3 Kb)


(04-Abr-2003)
Pintar registros condicionalmente con la función DbEdit()

>Maestro, existe alguna forma en que el dbEdit se sombree a nivel de todo el registro, no solo por campo de registro?
>
>Es decir, cuando cambie de registro, el dbEdit debe de seleccionar o sombrear siempre TODO el registro actual.

Sabes hace algunos años hice la misma pregunta a varios programadores Clipper que se encontraban en una exposición a la que asistí.
La respuesta unánime fue : "NO".
No me convenció y le busqué la vuelta al asunto y así fue que la encontré. Es una chapuza pero ayuda mucho porque si bien prefiero los objetos TBrow, la función DBEdit es mucho menos laboriosa.
Te adjunto un ejemplo donde aparecen pintados ciertos registros de distintos campos según condiciones, por ejemplo saldos negativos y pagos en efectivo.

Archivo Ejemdbe.ZIP (2 Kb)


(27-Mar-2003)
Leer datos desde un lector de códigos de barras.

>necesito agregar en un sistema de facturación de mi propiedad la lectura de codigos barra mediante alguna pistola u otro lector de codigos de barra. ya baje el que imprime lo que da una ventaja, pero, como obtengo la lectura para que la reconzca mi sistema.
>
>Espero puedas responder a mi inquietud.
>

No entiendo por que pero recibo esta consulta muy a menudo, está muy bien que pregunten si no conocen la respuesta, lo que no entiendo es porque piensan que hay que hacer algo en especial para leer la información proveniente desde el lector de códigos de barra.
El tema es así, un lector de códigos de barra deposita el código leído directamente en el buffer de teclado, lo cual sería equivalente a escribir en el teclado el código manualmente.
En pocas palabras, si tu lees con el lector un código de barras que sea : 123456789 sería lo mismo que escribir directamente en el teclado la secuencia numérica 132456789.
La única diferencia es que el lector seguramente lo hará algo mas rápido.
:-)
Lo que si debes hacer es adaptar tu sistema de facturación para que cuando se presione una tecla numérica se abra un "Read" y lea la secuencia, de dar <Enter> al final se encarga también el lector, el cual algunas veces, dependiendo de la marca" hay que configurarlo para que lo haga, esto viene muy bien explicado en el manual del usuario.
Ejemplo de rutina para que efectúe el "Read":

Inkey(0)
[...]
Case LastKey() >= 48 .And. LastKey() <= 57
Keyb(Chr(LastKey())
@... Get CodBar Pict [999999999999999]
Read
[...]

Lo que hago es cargar en el buffer la primer tecla presionada para que el "Read" continúe cargando el resto del código.


(26-Mar-2003)
La función DbEval()

>Necesito aprender a utilizar DBEVAL() con ejemplos prácticos. Me ayudas?

Pero por supuesto, este es el tipo de consultas que contesto con mucho gusto.

Ok, ejemplos prácticos... veamos, la verdad es que los manuales y guías que tengo no tienen ejemplos prácticos sobre esta función, ni mucho menos la NG's (Norton Guides), es un horror el ejemplo que ahí proponen.

Bueno, voy a explicarte la función por partes, espero que lo puedas comprender, si te pierdes me lo dices y te explico mejor algún punto.
Partamos de la funcionalidad, básicamente lo que hace es ejecutar un "bloque de código", que puede ser una función tuya, el incremento de una variable, etc., evaluando para cada registro se cumplan ciertas condiciones.
¿La compliqué mucho verdad?
Bueno, vamos a ver cada uno de los parámetros en un ejemplo por separado para que lo entiendas.
La sintaxis sería así:

DbEval( {|| Bloque de código, <Condición "For">, <Condición "While">, <nNext>, <Número de Registro>, <lRest>)

Como podrás observar exceptuando el bloque de código todos los demás parámetros son opcionales, con lo
cual podemos hacer mas simples los ejemplos.
Partiremos por un ejemplo donde solo se use el Bloque de código, como no usamos condiciones lo que vamos a hacer es contar la cantidad de registros de una base de datos.
En este ejemplo el bloque de código será "nCount++" lo que sería igual a nCount = nCount + 1, o sea que se incremente la variable nCount en uno cada vez que se ejecuta el bloque, como está libre de condiciones será en cada registro.

Ejemplo (1):
LOCAL nCount := 0
Use EJEMCLI.DBF
DbEval( {|| nCount++}, , , , , )
? "Total de Clientes :", nCount

Respuesta ==> Cantidad de registros en la base de datos
Nota importante: dbEval() se ajusta a los filtros preestablecidos como ser:
Set Dele (on/off)
Set Filter to
Y otros que afecten a la base de datos.

Ahora veamos un ejemplo con una condición "For"
El resultado será la cantidad de clientes cuyo saldo sea menor a cero.

LOCAL nCount := 0
dbUseArea(.F., , "EJEMCLI.DBF")
DbEval( {|| nCount++}, {|| Saldo < 0} , , , , )
? "Total de Clientes morosos :", nCount

Respuesta ==> Cantidad de registros en la base de datos que cumplan con la condición Saldo < 0.

Ahora veamos un ejemplo con una condición "While"

LOCAL nCount := 0
dbUseArea(.F., , "EJEMCLI.DBF")
DbEval( {|| Contador(nCount)}, {|| Saldo < 0} ,{||LastKey() # 27 }, , , )
? "Total de Clientes morosos :", nCount

********************
Func Contador(nCount)
********************
Inkey()
Retu nCount++

Respuesta ==> Cantidad de registros en la base de datos que cumplan con la condición Saldo < 0 hasta que se toque la tecla Escape (lastkey() = 27) ó termine la base lo primero que suceda.

El parámetro nNext lo que hace ejecutar el código de bloque sobre una cantidad de registros determinada, en el siguiente ejemplo dará como resultado la cantidad de registros dentro de los siguientes 150 que cumplan con la condición Saldo < 0 hasta que se toque la tecla Escape (lastkey() = 27)

LOCAL nCount := 0
dbUseArea(.F., , "EJEMCLI.DBF")
DbEval( {|| Contador(nCount)}, {|| Saldo < 0} ,{||LastKey() # 27 }, 150, , )
? "Total de Clientes morosos :", nCount

********************
Func Contador(nCount)
********************
Inkey()
Retu nCount++

El parámetro correspondiente al número de registro hace que el código de bloque se ejecute solo sobre el registro mencionado, no voy a poner un ejemplo porque considero es demasiado simple y no hace falta.

Y Por último el parámetro lRest es un valor lógico que indica si la evaluación será desde el primer registro en el caso que sea .F. ó desde el registro actual .T.
El siguiente ejemplo muestra la cantidad de cantidad de registros en la base de datos que cumplan con la condición Saldo < 0 a partir del registro 150

LOCAL nCount := 0
dbUseArea(.F., , "EJEMCLI.DBF")
Goto 150
DbEval( {|| Contador(nCount)}, {|| Saldo < 0} , , , , .T.)
? "Total de Clientes morosos :", nCount


(22-Mar-2003)
El Comando Get con el parámetro "when".

>Se supone que el @ ... get Variable message "Hola"
>
>tiene que mostrarte el mensaje al final de la pantalla, esta con SET MESSAGE TO 23 center pero no sale nada, acaso hay que setear otra cosa ??

No, el commando "message" solo sirve para usar con el comando "Prompt", no con "Say" o "Get".
Te adjunto un ejemplo que realizé exclusivamente para responder a tu pregunta, en el hay 4 "Get's" y un mensaje en la línea 23 que cambia cada vez que accedes a un campo.

Archivo ejemread.prg (977 bytes)


(19-Mar-2003)
Manejo de errores desde Clipper.

>quisiera saber cual es la manera de controlar los errores por ejemplo
>use tabla exclusive    && en este momento se genera un error y me manda el mensaje
>neterror()   && el neterror me manda el mensaje pero antes ya se genero un error
>

Esto es muy simple, debes utilizar la función ErrorBlock() para controlar los errores que desees.
Por ejemplo, el error correspondiente a hacer un USE en exclusivo de una base de datos ya abierta es el 21.
Un ejemplo de programa sería:  

Local bErrorHandler // Defino como local la variable bErrorHandler   bLastHandler := ErrorBlock(bErrorHandler)

// Antes de asignar mi manejador de errores grabo el actual en la variable bLastHandler   ErrorBlock( {|e| SysError( E )} )
// Antes del comando USE para controlar los errores desde mi función Syserror()   use tabla exclusive // Abro la tabla
? neterr()  // Muestro si hay error
ErrorBlock(bLastHandler) // Devuelvo el control de errores al manejador original.  

Y la función Syserror() debería ser mas o menos así:  

FUNCTION SYSERROR( e )
If e:genCode = 21
   //21 es el código para este error, en el archivo de cabecera ERROR.CH encontraras los códigos para cada error.
  NetErr(.t.) // Asigno NetErr = .T. (verdadero)
  Retu .F.  // Devuelvo el control al programa.
EndI 
Return (.F.)  


>quisisera saber cual es la forma para que clipper no mande los mensajes de error y yo los pueda codificar.

Usando ErrorBlock() puedes asignar funciones para controlar tu mismo los errores, grabarlos en un archivo .LOG, reintentar una impresión hasta que la impresora esté lista o se presione una tecla, y muchas otras cosas mas.  


(18-Mar-2003)
Imprimir en Red desde Clipper

>perdona que me dirija a ti directamente, he estado mirando la web y no he visto lo que busco.

Es que esta sección es para eso, así que procede con tu pregunta.

>Me gustaría saber si es posible imprimir en RED, con clipper a una impresora USB bajo Windows.
>
>Vamos lo que quiero es saber si es posible imprimir bajo windows, a una impresora en red y que además es USB. Todo esto usando clipper, claro.
>

Bien, tu pregunta se ha hecho tan común en estos últimos tiempos que publicaré mi respuesta en mi página.
Imprimir en Red desde Windows en cualquier impresora es muy fácil.
Primero debes instalar la impresora en la máquina adecuada con sus correspondientes "Drivers" no importa si esta es USB, paralela ó IR (Transmisión de datos por infrarrojo).
Luego en cada una de las PC's donde vas a correr el programa debes seguir los siguientes pasos:
Hacer "Click" en el botón de Inicio, configuración, Impresoras.
"Doble Click" en "Agregar Impresora".
Hacer "Click" en el botón "Siguiente", Seleccionar impresora en Red y luego "Click" en el botón "Siguiente" nuevamente.
Hacer "Click" en el botón "Examinar" y buscar la PC que contiene la impresora, "Click" en la impresora y aceptar, nos habrá traído la dirección UNC en el cuadro de texto, algo así como : \\Facturación\HP4100
Hacer "Click" en el botón "Siguiente", Seleccionar "deseo que los programas DOS impriman en esta impresora"
Luego hacemos click en el botón siguiente hasta terminar.
Eso es todo, incluso si deseamos que nuestro programa dirija la salida de impresión a dos ó mas impresoras distintas solo tenemos que repetir los pasos para agregar otra impresora y luego en las propiedades de cada una de las impresoras en la solapa "General" hacemos "Click" en capturar puerto.
Windows 95, 98 y Me nos proveen de 9 puertos virtuales por lo que podemos asignar hasta 9 impresoras distintas.
Luego en nuestro programa utilizamos el comando:
Set Printer to <cPuerto>
Donde cPuerto será LPT1 para la impresora que asignamos a ese puerto, LPT2 para siguiente y así...


(18-Mar-2003)
Error interno 9002 (Internal error 9002)

>Hola!
>Tengo un problema. Compilo un programa con Clipper 5.2e y lo Linkeo con
>Blinker 3.0 sin problemas.
>Cuando lo ejecuto no entra al programa y me da el siguiente error:
>
>RDDREGISTE (0) Internal Error 9002
>
>Por qué se produce? Cómo lo soluciono? HELP!!!

Si leemos el manual de Clipper nos dice que un error interno 9002 se produce en tiempo de ejecución cuando el sistema no puede detectar la RDD para el manejo de bases de datos e índices usados por la aplicación.
Esto en la teoría puede ser cierto, no obstante a la práctica, los años me enseñaron que este poco descriptivo error corresponde a diferentes cosas que paso a detallar:
En primer lugar es probable que se esté utilizando una biblioteca u objeto que no sean compatibles con la versión de Clipper con la que se ha compilado el programa.
Otra posibilidad es que el Linkeditor (Bliker en este caso) no reconozca la biblioteca que contiene el manejador de bases de datos (RDD).
También es posible que no sea apropiado el orden de inclusión de los objetos y/o bibliotecas en el "Script" de Linkeditado.
Y por último que algún .OBJ se encuentre corrupto.
¿Soluciones?
Empecemos por el principio, primero hay que verificar si todas las versiones de bibliotecas y objetos son compatibles con el compilador y linkeditor actual, luego si hay un "Script" de linkeditado verificar el orden en que son llamados los objetos y/o bibliotecas y por último, si esto está bien, proceder a borrar y a regenerar cada uno de los .OBJ que conforman al programa.


(14-Mar-2003)
Leer datos desde COM1, COM2, COM3 ó COM4.


>Estoy haciendo un programa y necesito leer del puerto com1 o com2 el peso en KGS que me marca la bascula.
>Cuales son los comandos con lo cual puedo hacerlo en Clipper.

Bien, he tenido que realizar este tipo de comunicaciones con distintos dispositivos en varias oportunidades, antes, con Clipper Summer '87 he tenido que recurrir a un programa hecho en C#, ya que no existía ninguna biblioteca o función que lo permitiera.
¿Sabes lo que sería "Clipper Summer '87" sin la existencia de C?
Sería lipper Summer '87.
:-) Un poco de humor no viene mal, ¿no?

Bueno, continuando con el tema, Clipper 5 trae unas funciones especiales para manejar la comunicación mediante los puertos Com, se encuentran en la librería Clipper Tools, (CT y CTP53)
Igualmente el tema es bastante complicado ya que el programa debe interpretar los valores que el dispositivo transmite al puerto.
En la dirección http://www.clipx.net/ng/tools1-3/ng3261e.php (inglés) están perfectamente explicados estos comandos.

Los comandos son:
COM_OPEN(), COM_INIT() y COM_READ()

COM_OPEN()
Abre el puerto he inicializa el Buffer.

Sintaxis

COM_OPEN(<nComPort>,<nBufferIn>,<nBufferOut>,
[<lTrapMode>]) --> lStatus


Argumentos:

<nComPort> Designa el número de puerto "Com" a ser leído, este puede ser 1, 2, 3 ó 4.

<nBufferIn> Define el tamaño del buffer de entrada, hasta 64535 Bytes (64 Kb), si se omite el valor será 100 Bytes.

<nBufferOut> Define el tamaño del buffer de salida, hasta 64535 Bytes (64 Kb), si se omite el valor será 100 Bytes.

<lTrapMode> Si el valor es .T. (verdadero) el puerto solo disparará una interrupción cuando recibe datos, y estos no son el resultado de una falla en la transmisión, si se omite el valor será .F. (Falso)

Devuelve .T. (Verdadero) si se abrió el puerto con éxito.


COM_INIT()
Inicializa los parámetros del puerto Com.

Sintaxis

COM_INIT(<nComPort>,[<nBaudRate>],[<cParity>],
[<nDataLength>],[<nStopBits>]) --> lInitialized

Argumentos

<nComPort> Designa el número de puerto "Com" a ser leído, este puede ser 1, 2, 3 ó 4.

<nBaudRate> Determina 300, 600, 1200, 2400, 4800, 9600 ó 19200 baudios, si se omite se tomaran 300 baudios, yo particularmente uso 1200.

<cParity> Determina la paridad : (E)ven, (O)dd, (M)ark, (S)pace, or (N)one. si se omite se tomara "N".

<nDataLength> Largo de datos, 7 u 8 Bits, el valor por default es 8.

<nStopBits> Bits de parada, si se omite el valor será 1.

Devuelve : .T. si se inicializa Ok.


COM_READ()
Lee una cadena de caracteres desde el buffer de recepción.

Sintaxis:
COM_READ(<nComPort>,[<nLargo>],[<lNoBorrar>])
--> cCadena

Argumentos:

<nComPort> Designa el número de puerto "Com" a ser leído, este puede ser 1, 2, 3 ó 4.

<nLargo> Determina la cantidad de caracteres a leer desde el buffer, si no incluyes este parámetro serán leídos todos los caracteres.

<lNoBorrar> .T. (Verdadero) Hace que no se borre la cadena leída en el buffer, si no se incluye se borrarán.

Devuelve una cadena de caracteres correspondientes al buffer de recepción.

Ejemplo:

cInput := COM_READ(1, 10) // Lee Diez caracteres desde el puerto 1
? cInput // Los muestra en la pantalla


Es conveniente cerrar el puerto luego de usarlo mediante Com_Close(nComPort)


(12-Mar-2003)
(DOS Error 4) En Windows 2000/ME/NT.

>Tengo una aplicación hecha con alguna versión de clipper, cuando la ejecuto en una máquina con W95 ó W98 la aplicación funciona bien, pero cuando la ejecuto en una maquina con W2000 me manda el siguiente error:
>
>Error DBFNTX/1003 Open error:
>\sistema\datsis\ofertax1.ntx
>(DOS Error 4)
>

Ok, Este error también sucede en Windows ME y NT.
En primer lugar un "DOS Error 4" se produce cuando se agotaron los manejadores de archivos disponibles, o sea que hay mas archivos abiertos de los que se asignaron mediante el comando "Files" en el archivo "CONFIG.SYS".
Windows XP, NT y W2K (2000) no utilizan los archivos CONFIG.SYS y AUTOEXEC.BAT como si lo hacen las versiones anteriores, en su lugar utilizan CONFIG.NT y AUTOEXEC.NT ubicados en la carpeta (directorio) "\WINDOWS\System32\", por lo que para aumentar la cantidad de manejadores de archivos (file handlers) es necesario incluir las siguientes líneas:

En el archivo \WINDOWS\System32\Config.nt
la línea :
FILES=100

En el archivo \WINDOWS\System32\Autoexec.nt
la línea :
SET CLIPPER=F100

Esta última línea aveces no es suficiente con colocarla solo en el archivo AUTOEXEC.NT, sino que es necesario ejecutarla en cada una de las sesiones DOS que se abran, para lo cual es conveniente hacer un .BAT que incluya este comando y luego ejecute el programa.
Ejemplo:
SET CLIPPER=F100
MYPROG.EXE

Siendo MYPROG.EXE el programa en Clipper a ejecutar.


(12-Mar-2003)
Windows tarda mucho en imprimir desde Clipper.

>Estoy tratando de imprimir en una impresora de matrix de punto, en un sistema programado en lenguaje clipper bajo un sistema operativo windows XP.
>El reporte tarda mucho en ser enviado a la impresora, la unica manera que he logrado que salga rapido es saliendome del sistema y volviendo a entrar.
>

Mucho es como dos minutos y puedes también presionar Alt-Tab para que salga de inmediato.

>Supongo que el buffer de impresión esta esperando alguna instrucción
>para cerrarse.

Si, es un error de Windows heredado desde la versión '98, no de Clipper.

>
>Si tiene algun concocimiento  sobre esto le sabria agradecer mucho su ayuda.
>

En Internet encontré mas de cien foros donde se comenta el tema, personalmente probé cada una de las soluciones allí propuestas y no puedo dar con la solución, cambié mil cosas en los .INI de Windows, modifique mi programa de impresión, pero nada.
Este problema me está rompiendo el coco a mi también, te pido que si logras solucionarlo me avises.
Gracias y disculpa el no poder ayudarte.


(10-Mar-2003)
Evitar Clipper dos veces ejecutadose.

>Te quiero preguntar si es posible con Clipper 5.3, puro hacer que cuando ya este abierto el sistema y te lo vuelven a llamar se abra la ventana del sistema que ya estaba abierto?

Bueno, la cosa es así, Clipper puro, si, pero ejecutando una función de Windows 95', 98', ME, 2K, NT, ME (prácticamente todos) llamada desde Clipper, para poder mostrar la ventana.
Hay quienes dices, no los culpo, que efectuar este tipo de llamadas no es una ejecución de Clipper puro, no los culpo aunque yo particularmente considero que como bien todos saben Windows es un sistema operativo y si yo realizo un programa en Clipper el cual no incluye librerías externas y solo hace una llamada a una función del OS, entonces ese programa es Clipper puro.
Te adjunto un ejemplo, muy simple por cierto, pero funcional, el cual realicé exclusivamente para ti, como para explicarte mejor tal llamada. Para probarlo baja todos los archivos en cualquier carpeta y trata de
ejecutarlo mas de una vez.

Archivo YA.ZIP (109 Kb)

Esta sección sobre CA-Clipper está coordinada íntegramente por Diego Lucio D'Onofrio


la Luna del Guille o... el Guille que está en la Luna... tanto monta...

Estadísticas desde el 01/Nov/2002 23:15