Jump to content


Photo

Multihilos


  • Please log in to reply
31 replies to this topic

#1 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 07 January 2009 - 03:41 PM

Hola amigos

Yo he visto mucha información sobre multi hilos, sin embargo, sigo sin entender exactamente como funcionan, quiero decir, veo como se crean hilos y como se deben ejecutar, sin embargo aun no entiendo como hacer que un programa ya hecho, cambiarlo a ese concepto.

Les explico.

Tengo un producto donde se generan varias acciones como son Registro de Clientes, Registro de Ventas, Registro de Pagos, Facturación, pero por otro lado este programa está en constante comunicación con dos puertos seriales, donde genera eventos de entrada y salida.

Que pasa, mientras estoy realizando alguna actividad de registro o de facturación, los puertos seriales deben estar trabajando sin interrupción, la parte de salida del programa al puerto no tiene problema ya que se ejecuta dependiendo de el proceso que se haga, sin embargo, el problema surge cuando se recibe información del puerto.

Como hago para separar en hilos estos procesos, debo tener hilos solo para los puertos y las demas actividades no requieren estar dentro de un hilo?

O todos deben de tener su hilo correspondiente.

Espero que hay sido claro..... :s

Salud OS
  • 0

#2 seoane

seoane

    Advanced Member

  • Administrador
  • 1259 posts
  • LocationEspaña

Posted 07 January 2009 - 03:51 PM

Creo que es una estupenda idea que metas el manejo de puertos en un hilo diferente al principal. Y no necesitas meter cada cosa en un hilo, basta con que crees un hilo (o dos, uno por cada puerto) y este se ejecutara de forma paralela al hilo principal. Yo incluso iría un poco mas allá y no utilizaría componentes para manejar el puerto serie, con sus eventos y demás, usando hilos es mucho mas adecuado y sencillo acceder al puerto serie usando directamente las funciones de la API.
  • 0

#3 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 07 January 2009 - 04:01 PM

Creo que es una estupenda idea que metas el manejo de puertos en un hilo diferente al principal. Y no necesitas meter cada cosa en un hilo, basta con que crees un hilo (o dos, uno por cada puerto) y este se ejecutara de forma paralela al hilo principal. Yo incluso iría un poco mas allá y no utilizaría componentes para manejar el puerto serie, con sus eventos y demás, usando hilos es mucho mas adecuado y sencillo acceder al puerto serie usando directamente las funciones de la API.


:D :D :D, amigo, eso se lee muy fácil, solo que no se como leer puertos con API :s, me puedes enseñar :)

Salud OS

Edito: Me senti como cierto individuo que se hace llamar NOVATO jejejeje, :p
  • 0

#4 seoane

seoane

    Advanced Member

  • Administrador
  • 1259 posts
  • LocationEspaña

Posted 07 January 2009 - 04:08 PM

:D :D :D, amigo, eso se lee muy fácil, solo que no se como leer puertos con API :s, me puedes enseñar :)


Eso ni se pregunta  :D

Básicamente se debe de abrir el puerto como si de un archivo se tratase (usando la funciona CreateFile), configurar los parámetros de comunicación (velocidad, paridad, control de flujo, etc ...) y no queda mas que leer y escribir en el puerto usando las mismas funciones que utilizarías para leer y escribir un archivo (ReadFile, WriteFile).  Si me describes un poco el protocolo de comunicación que utilizas puedo hacerte un ejemplo un poco mas concreto.
  • 0

#5 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 07 January 2009 - 04:21 PM


Eso ni se pregunta  :D

Básicamente se debe de abrir el puerto como si de un archivo se tratase (usando la funciona CreateFile), configurar los parámetros de comunicación (velocidad, paridad, control de flujo, etc ...) y no queda mas que leer y escribir en el puerto usando las mismas funciones que utilizarías para leer y escribir un archivo (ReadFile, WriteFile).  Si me describes un poco el protocolo de comunicación que utilizas puedo hacerte un ejemplo un poco mas concreto.


Perfecto amigo,

El protocolo es diferente dependiendo del equipo al cual se conecta, esto quiere decir que se debe hacer una mascara por cada equipo.

Los más comunes son los siguientes:

Formato 1

STX = #2
ETX = #3
EOT = #4
ACK = #6
NAK = #21
BCC = Binary Code Check (XOR longitudinal)

<STX> [Cadena] <ETX> <BCC>

Al recibir la cadena incluyendo el BCC (que es diferente dependiendo de la cadena enviada) se debe de enviar ACK si es correcto o NAK si es incorrecto.

Este protocolo tiene la característica de que se debe enviar un HeartBit cada x segundos para verificar la sincronía con el equipo.

Además, el equipo es mandatorio, es decir, si tiene algo que enviar se debe esperar a que libere su buffer para poder enviarle datos.

Formato 2

[Cadena]<LF><CR>

Este formato es muy simple y es unidireccional, solo debo recibir datos y no requiere de contestación.

Si requieres mas información con gusto te la daré.

Salud OS
  • 0

#6 seoane

seoane

    Advanced Member

  • Administrador
  • 1259 posts
  • LocationEspaña

Posted 07 January 2009 - 04:25 PM

Un par de preguntas: Para que el equipo envíe datos hay que mandarle algún comando o lo hace cuando el quiere, y si es así, y manda datos cuando el ordenador esta apagado ¿los datos se pierden?
  • 0

#7 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 07 January 2009 - 04:29 PM

Un par de preguntas: Para que el equipo envíe datos hay que mandarle algún comando o lo hace cuando el quiere, y si es así, y manda datos cuando el ordenador esta apagado ¿los datos se pierden?


Bueno, en el formato 1 es una comunicación en tiempo real, si no estan "en linea" los equipos esa información se pierde, en el formato 2 algunos equipos tienen un buffer interno que almacena cierta cantidad de datos, pero una vez que se llena ese buffer va eliminando los mas viejos e ingresa los mas nuevos.

Referente a la pregunta de comandos, cuando yo rquiero enviar algo al equipo necesito enviarle una cadena para que el equipo me diga si está listo para recibir o tiene algo que enviarme, si tiene algo que enviarme, debo esperar a que lo envie y despues enviar lo que quiero.

Salud OS
  • 0

#8 seoane

seoane

    Advanced Member

  • Administrador
  • 1259 posts
  • LocationEspaña

Posted 07 January 2009 - 04:31 PM

Perdona si estoy muy pregunton  :p

¿De que dispositivos se trata?

¿Usaran las señales de control del puerto serie para saber si tienen que enviar? Es que me parece muy peligroso que envien los datos alegremente
  • 0

#9 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 07 January 2009 - 04:33 PM

Perdona si estoy muy pregunton  :p

¿De que dispositivos se trata?

¿Usaran las señales de control del puerto serie para saber si tienen que enviar? Es que me parece muy peligroso que envien los datos alegremente


Son conmutadores telefonicos, centrales telefonicas, PBX no se como lo conozcas.

No se usan señales de control, solo son datos (caracteres) a traves del puerto.

Salud OS
  • 0

#10 seoane

seoane

    Advanced Member

  • Administrador
  • 1259 posts
  • LocationEspaña

Posted 07 January 2009 - 04:42 PM

Pues tal como lo veo, el segundo caso es muy simple, hay que leer continuamente el puerto serie guardando el texto en un buffer (un simple string) y cuando se recibe el LF procesar la cadena (no se si la guardas en una base de datos, un fichero, ...).

El primer caso ya tiene mas complejidad, pero no parece complicado.

Dejame un poco de tiempo y a lo mejor te puedo mostrar algo de código
  • 0

#11 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 07 January 2009 - 04:44 PM

Pues tal como lo veo, el segundo caso es muy simple, hay que leer continuamente el puerto serie guardando el texto en un buffer (un simple string) y cuando se recibe el LF procesar la cadena (no se si la guardas en una base de datos, un fichero, ...).

El primer caso ya tiene mas complejidad, pero no parece complicado.

Dejame un poco de tiempo y a lo mejor te puedo mostrar algo de código


(y), gracias amigo, esto va a ser muy util para mi y para muchos mas

Salud OS
  • 0

#12 FGarcia

FGarcia

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 687 posts
  • LocationMéxico

Posted 08 January 2009 - 10:27 AM

Me interesa el hilo.
Eliseo sin usar componentes actualmente estoy trabajando en este codigo, aun hay problemas pero funciona:



delphi
  1. unit UComm;
  2.  
  3. interface
  4.  
  5. uses
  6.   Forms,
  7.   SysUtils,
  8.   Classes,
  9.   IniFiles,
  10.   Windows,
  11.   StrUtils,
  12.   Dialogs,
  13.   ComCFG; //esta es una unidad con un formulario
  14.  
  15.  
  16.  
  17. const
  18.   Archivo = 'ComPP.ini';
  19.   STX = #2;
  20.   CR = #13;
  21.   LF = #10;
  22.  
  23. var
  24.   nombrePuerto,paridadPuerto,flujoPuerto: string;
  25.   formato, baudPuerto, bitsPuerto, stopPuerto, HandlePuerto: integer;
  26.   Arranque, longCadena, Terminador: byte;
  27.  
  28.   function ShowComCFG: integer; stdcall;
  29.   function CapturaCadena: PChar; stdcall;
  30.   procedure CargaAjustes; stdcall;
  31.   procedure AbrePuerto; stdcall;
  32.   procedure CierraPuerto; stdcall;
  33.  
  34. implementation
  35.  
  36. function ShowComCFG:integer;stdcall;
  37. begin
  38.   fmComm := TfmComm.Create(nil);
  39.   Result := fmComm.ShowModal;
  40. end;
  41.  
  42. procedure CargaAjustes; stdcall;
  43. var
  44.   F: TIniFile;
  45. begin
  46.    F := TIniFile.Create(ExtractFilePath(ParamStr(0)) + Archivo);
  47.   with F do
  48.     try
  49.       nombrePuerto := ReadString('Puerto Serie','Puerto','COM1');
  50.       baudPuerto := ReadInteger('Puerto Serie','Baudios',9600);
  51.       bitsPuerto := ReadInteger('Puerto Serie','Bits',8);
  52.       paridadPuerto := ReadString('Puerto Serie','Paridad','Ninguna');
  53.       stopPuerto := ReadInteger('Puerto Serie','Bits Parada',1);
  54.       //flujoPuerto := ReadString('Puerto Serie','Control de flujo','Ninguno');
  55.       formato := ReadInteger('Puerto Serie','Formatos',0);
  56.     finally
  57.       Free ;
  58.     end;
  59.  
  60.   case formato of
  61.     0 : begin
  62.           Arranque := Ord(STX);
  63.           Terminador := Ord(CR);
  64.           longCadena := 13;
  65.         end;
  66.     1 : begin
  67.           Arranque := Ord(STX);
  68.           Terminador := Ord(LF);
  69.           longCadena := 10;
  70.         end;
  71.   end;
  72.  
  73. end;
  74.  
  75. procedure AbrePuerto; stdcall;
  76. var
  77.   Puerto: integer;
  78.   Mode: DCB; //estructura de windows.dcb para control de dispositivos serie
  79. begin
  80.   Mode.BaudRate := baudPuerto;
  81.   Mode.ByteSize := bitspuerto;
  82.  
  83.   if paridadPuerto = 'Impar' then
  84.     Mode.Parity := ODDPARITY
  85.   else if paridadPuerto = 'Par' then
  86.     Mode.Parity := EVENPARITY
  87.   else if paridadPuerto = 'Marca' then
  88.     Mode.Parity := MARKPARITY
  89.   else if ParidadPuerto = 'Espacio' then
  90.     Mode.Parity := SPACEPARITY
  91.   else
  92.     Mode.Parity := NOPARITY;
  93.    
  94.   case stopPuerto of
  95.     1 : Mode.StopBits := ONESTOPBIT;
  96.     2 : mode.StopBits := ONE5STOPBITS;
  97.     3 : Mode.StopBits := TWOSTOPBITS;
  98.   else
  99.     Mode.StopBits := ONESTOPBIT;
  100.   end;
  101.  
  102.   HandlePuerto:=FileOpen(nombrePuerto,fmOpenRead);//Abre el puerto
  103.  
  104.   if HandlePuerto > 0 then
  105.     SetCommState(HandlePuerto, Mode) //Ajusta los parámetros
  106.   else
  107.     ShowMessage('El puerto de comunicaciones ' + nombrePuerto +  ' esta ocupado o no existe');
  108. end;
  109.  
  110. function CapturaCadena: PChar; stdcall;
  111. var
  112.   n: integer;
  113.   num: byte;
  114.   Cad: string;
  115. begin
  116.   result := '';
  117.  
  118.   repeat
  119.     Application.ProcessMessages ;
  120.  
  121.     FileRead(HandlePuerto,Num,1); //AQUI se genera un error. Si no captura nada (el equipo esta desconectado) se bloquea la ejecucion
  122.  
  123.     if (num = Arranque) then
  124.       cad := ''
  125.     else
  126.       begin
  127.         if num >= 32 then
  128.           cad := cad + chr(num);
  129.       end;
  130.      
  131.     if (GetKeyState(VK_ESCAPE) and 128 = 128) then
  132.       begin
  133.         Cad := '0';
  134.         Break;
  135.       end;
  136.  
  137.   until (num = Terminador);
  138.  
  139.   result := PChar(Cad);
  140.  
  141. end;
  142.  
  143. procedure CierraPuerto; stdcall;
  144. begin
  145.   FileClose(HandlePuerto);  //Cierra el Puerto
  146. end;
  147.  
  148. end.



La parte de captura cadena es codigo tuyo de otro hilo que postee por aqui. Todo esto en si esta en un proyecto de dll. igual se puede mejorar.
  • 0

#13 Kipow

Kipow

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 228 posts
  • LocationGuatemala

Posted 08 January 2009 - 11:04 AM

Yo estoy iniciandome en el mundo de los multihilos y la verdad se complica dependiendo del caso, al menos el mio es para un proceso de replicacion de N sucursales (en este momento 19) y segun las pruebas que he hecho pues ya he logrado evitar los conflictos que tenia. Este tema de los Threads es bien pero bien interesante no se si existira ya una solucion o componente que facilite todo esto porque en ocasiones hay que rifarselas con los semaforos, mutex, areas criticas, etc,etc. seria super bueno poder crear una super clase para que una aplicacion fuera 100% multihilos.

Saludos.
  • 0

#14 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 08 January 2009 - 11:04 AM

Hola amigo, muchas gracias por el aporte, interesante que lo estes realizando en una dll, me parece que es una muy buena alternativa para que solo cambie la dll dependiendo del formato del equipo al cual se va a conectar.  (y)

Vamos a darle un vistazo, seguro va a salir algo muy bueno de aqui :)

Salud OS
  • 0

#15 MasterXP

MasterXP

    Member

  • Miembros
  • PipPip
  • 13 posts
  • LocationRepública Dominicana

Posted 08 January 2009 - 11:45 PM

Hola, ha decir verdad yo no se nada de multihilos mas que solo teoria, nunca los he puesto en practica, pero he investigado mucho anteriormente y llegue a descargarme unos componentes para manejar los hilos que pintan muy bien.

Una pequeñá descripcion de los componentes:
-------------------------------------------------------------------
TtdThreadWrapper
A component that allows you to just drop the component on the form, implement its OnExecute event handler,
and call the Start method to start the thread.

TtdFuture
A sort of thread-based function. You can start the thread normally, and when you call the Value method
to get the "function value," it will wait for the thread to finish, so you can start the thread in the background
and call the Value method when you need to have the value before continuing.
TtdThreadPool
A component for implementing a simple thread pool. You attach a "work....
--------------------------------------------------------------------------------

La descripcion completa aqui: http://www.twodesks....ts/threads.html

Y el link de descargar aqui: http://www.twodesks....onentsSetup.zip

Son componentes gratis.

Salu2.
  • 0

#16 Kipow

Kipow

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 228 posts
  • LocationGuatemala

Posted 09 January 2009 - 10:40 AM

Excelente aporte, buena idea para tomarla de base, mi idea principal seria crear una super clase para poder hacer una aplicaion 100% multihilos/multicores que seria lo ideal. pero vamos a ver si con un poco de tiempo ponemos a prueba estos componentes que al parecer pintan bien.
  • 0

#17 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 09 January 2009 - 10:47 AM

Hola, ha decir verdad yo no se nada de multihilos mas que solo teoria, nunca los he puesto en practica, pero he investigado mucho anteriormente y llegue a descargarme unos componentes para manejar los hilos que pintan muy bien.


Muchas gracias MasterXP les voy a dar un vistazo, debo comentar que he replicado algunas de las soluciones que he encontrado en internet pero mi problema realmente es que no logro entender el concepto, quiero decir, he creado multihilos pero sigo sin poder hacerlo en mi desarrollo, no logro conceptualizar como de una applicación ya hecha, hacerla multihilos.

Salud OS
  • 0

#18 seoane

seoane

    Advanced Member

  • Administrador
  • 1259 posts
  • LocationEspaña

Posted 09 January 2009 - 02:37 PM

Lo prometido es deuda  :D

Aquí tienes un ejemplo de como leer el puerto serie en un thread. He usado el formato 2, mas que nada porque es mas fácil de implementar  :p , pero el formato 1 no sería mucho mas complicado. Por ahora solo lee las lineas y las guarda en un fichero de texto, pero podemos hacer con ellas lo que queramos, solamente dime que hay que hacer con ellas  (h)



delphi
  1. unit UThreadCom;
  2.  
  3. interface
  4.  
  5. uses Windows, Sysutils, Classes;
  6.  
  7. type
  8.   EThreadCom = class(Exception);
  9.  
  10.   TThreadCom = class(TThread)
  11.   private
  12.     FHandle: THandle;
  13.     FPort: Integer;
  14.     procedure Procesar(Str: String);
  15.   protected
  16.     procedure Execute; override;
  17.   public
  18.     constructor Create(APort: Integer);
  19.     destructor Destroy; override;
  20.   end;
  21.  
  22. implementation
  23.  
  24. // Esta funcion solo esta para hacer pruebas
  25. procedure log(Mensaje: String);
  26. var
  27.   F: TextFile;
  28.   Filename: String;
  29.   Mutex: THandle;
  30.   SearchRec: TSearchRec;
  31. begin
  32.   // Insertamos la fecha y la hora
  33.   Mensaje:= FormatDateTime('[ddd dd mmm, hh:nn] ', Now) + Mensaje;
  34.   // El nombre del archivo es igual al del ejecutable, pero con la extension .log
  35.   Filename:= ChangeFileExt(ParamStr(0),'.log');
  36.   // Creamos un mutex, usando como identificador unico la ruta completa del ejecutable
  37.   Mutex:= CreateMutex(nil,FALSE,
  38.     PChar(StringReplace(ParamStr(0),'\','/',[rfReplaceAll])));
  39.   if Mutex <> 0 then
  40.   begin
  41.     // Esperamos nuestro turno para escribir
  42.     WaitForSingleObject(Mutex, INFINITE);
  43.     try
  44.       // Comprobamos el tamaño del archivo
  45.       if FindFirst(Filename,faAnyFile,SearchRec) = 0 then
  46.       begin
  47.         // Si es mayor de un mega lo copiamos a (nombre).log.1
  48.         if SearchRec.Size > (1024*1024) then
  49.           MoveFileEx(PChar(Filename),PChar(Filename + '.1'),
  50.             MOVEFILE_REPLACE_EXISTING);
  51.         FindClose(SearchRec);
  52.       end;
  53.       try
  54.         AssignFile(F, Filename);
  55.         {$I-}
  56.           Append(F);
  57.         if IOResult <> 0 then
  58.           Rewrite(F);
  59.         {$I+}
  60.         if IOResult = 0 then
  61.         begin
  62.           // Escribimos el mensaje
  63.           Writeln(F,Mensaje);
  64.           CloseFile(F);
  65.         end;
  66.       except
  67.         //
  68.       end;
  69.     finally
  70.       ReleaseMutex(Mutex);
  71.       CloseHandle(Mutex);
  72.     end;
  73.   end;
  74. end;
  75.  
  76. { TThreadCom }
  77.  
  78. constructor TThreadCom.Create(APort: Integer);
  79. var
  80.   DCB: TDCB;
  81. begin
  82.   FPort:= APort;
  83.   FHandle:= CreateFile(PChar('\\.\COM' + IntToStr(APort)), GENERIC_READ or
  84.     GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  85.   if FHandle = INVALID_HANDLE_VALUE then
  86.     raise EThreadCom.Create('No puedo abrir el puerto COM' + IntToStr(APort));
  87.   DCB.DCBlength:= Sizeof(DCB);
  88.   if not GetCommState(FHandle,DCB) then
  89.     raise EThreadCom.Create('Error al configurar el puerto');
  90.   // Aqui esta la configuracion del puerto. Se la podriamos pasar por parametros ...
  91.   with DCB do
  92.   begin
  93.     BaudRate := CBR_9600;
  94.     ByteSize := 8;
  95.     Parity  := NOPARITY;
  96.     StopBits := ONESTOPBIT;
  97.     Flags    := $01;
  98.   end;
  99.   if not SetCommState(FHandle, DCB) then
  100.     raise EThreadCom.Create('Error al configurar el puerto');
  101.   inherited Create(FALSE);
  102. end;
  103.  
  104. destructor TThreadCom.Destroy;
  105. begin
  106.   if FHandle = INVALID_HANDLE_VALUE then
  107.     CloseHandle(FHandle);
  108.   inherited;
  109. end;
  110.  
  111. const
  112.   CR = #13;
  113.   LF = #10;
  114.   BUFFERSIZE = 1024;
  115.  
  116. procedure TThreadCom.Execute;
  117. var
  118.   Err: DWORD;
  119.   COMSTAT: TCOMSTAT;
  120.   Buffer: PChar;
  121.   Str: String;
  122.   i: Integer;
  123. begin
  124.   Getmem(Buffer,BUFFERSIZE);
  125.   try
  126.     Str:= EmptyStr;
  127.     while not Terminated do
  128.       if ClearCommError(FHandle,Err,@COMSTAT) then
  129.         if COMSTAT.cbInQue > 0 then
  130.         begin
  131.           if COMSTAT.cbInQue >= BUFFERSIZE then
  132.             Err:= BUFFERSIZE - 1
  133.           else
  134.             Err:= COMSTAT.cbInQue;
  135.           FillChar(Buffer^,BUFFERSIZE,#0);
  136.           if ReadFile(FHandle,Buffer^,Err,Err,nil) then
  137.           begin
  138.             Str:= Str + String(Buffer);
  139.             i:= Pos(CR,Str);
  140.             while i > 0 do
  141.             begin
  142.               Procesar(Trim(Copy(Str,1,i-1)));
  143.               Delete(Str,1,i);
  144.               i:= Pos(CR,Str);
  145.             end;
  146.           end else
  147.           begin
  148.             // Aqui podemos guardar un error en el log
  149.           end;
  150.         end else
  151.           Sleep(10);
  152.   finally
  153.     FreeMem(Buffer);
  154.   end;
  155. end;
  156.  
  157. procedure TThreadCom.Procesar(Str: String);
  158. begin
  159.   // Aqui procesamos cada una de las lineas de texto
  160.   // Para hacer pruebas
  161.   log(Str);
  162. end;
  163.  
  164. end.



Para usar la unit anterior solamente tienes que añadirla a las uses y crear el thread:


delphi
  1.   MiThread:= TThreadCom.Create(2);



Y acuerdate de "terminarlo" cuando no lo necesites:


delphi
  1.   MiThread.FreeOnTerminate:= TRUE;
  2.   MiThread.Terminate;



Para probarlo he usado un puerto com virtual, te lo recomiendo si no tienes ningún hardware a mano con que probarlo:
http://www.eterlogic...ducts.VSPE.html

:^) Creo que no se me olvida nada ...
  • 0

#19 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 09 January 2009 - 02:39 PM

Muchas gracias seoane, precisamente esa era mi duda, he olvidado todos mis "utencilios" con un cliente y hasta que vaya a verlo podré recuperarlos, que bien que me has dado el "remedio y el trapito" :D (y)

Salud OS
  • 0

#20 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 09 January 2009 - 03:09 PM

Hola amigo,

Ya he probado el codigo, este es el resultado de los eventos



delphi
  1. [Vie 09 Ene, 15:04] 2958 100  001 00:00:27 07/01/09 16:58 O7320170            **    0          0
  2. [Vie 09 Ene, 15:04] 2959 100  001 00:00:58 07/01/09 17:00 O7321280            **    0          0
  3. [Vie 09 Ene, 15:04] 2960 100  001 00:00:09 07/01/09 17:01 O7321055            **    0          0
  4. [Vie 09 Ene, 15:05]
  5. [Vie 09 Ene, 15:05] // 102  Clean  09/06/07-21:50
  6. [Vie 09 Ene, 15:05]
  7. [Vie 09 Ene, 15:05] // 102  Dirty  09/06/07-21:51



Simplemente genial, :D (y)

Salud OS
  • 0




IP.Board spam blocked by CleanTalk.