Solicito ayuda para crear un Thread para mover archivos entre dos pc´s
#1
Escrito 14 mayo 2010 - 09:49
Gracias amigos y un saludo a todos.
#4
Escrito 14 mayo 2010 - 10:17
funcion MueveArchivo(Origen, Destino: string):Integer; begin Result:= 1; if Existe Origen y NoEstaBloqueado(Origen) then Copia(Origen, Destino) else Exit; if not Existe(Destino) then begin Result:= 2; Exit end; if Borrar(Origen) = ok then Result:= 0 else Result:= 3 end;
Disculpen mi pseudocódigo, pero creo que se entiende... bueno pues necesito una función que me mueva el archivo, pero me regrese un valor de retorno para saber si falló y en qué y necesito saber si el archivo origen no está bloqueado o siendo usado apenas creado por la aplicación que lo genera y de ser posible, alguna forma de saber que la copia fue fiel...
Gracias y de nuevo discúlpenme por ser tan latoso
#6
Escrito 14 mayo 2010 - 10:41
¿cómo puedo saber si el archivo no está siendo apenas creado por la aplicación que lo genera? Porque como son archivos un poco grandes (imágenes de 3 a 30 Mb) me he fijado en el explorador de windows que primero aparece el archivo con 0 kb actualizo el explorador con F5 y veo como va creciendo el archivo hasta que llega a su tamaño final, supongo que ese tiempo que el archivo va creciendo la aplicación que lo genera lo tiene bloqueado de alguna manera y si no lo tiene bloqueado como puedo saber cuando ha llegado a su tamaño máximo para llevar al cabo la operación de copiado de la imagen final o definitiva y borrar la original.
Gracias por tan prontas respuestas escafandra, te debo unas
#7
Escrito 14 mayo 2010 - 01:19
¿cómo puedo saber si el archivo no está siendo apenas creado por la aplicación que lo genera?
Se me ocurre intentar abrirlo de manera exclusiva, si la otra aplicación lo tiene abierto la apertura fallara.
Algo así:
function EstaEnUso(Ruta: String): Boolean; var Handle: Integer; begin Handle:= FileOpen(Ruta,fmOpenRead or fmShareExclusive); if Handle < 0 then begin Result:= TRUE; end else begin CloseHandle(Handle); Result:= FALSE; end; end;
Aunque yo en estos casos prefiero comprobar ademas la fecha de modificación del fichero, si la otra aplicación lleva 5 segundos (o 30 depende de la aplicación) sin escribir en el archivo considero que ya no va a escribir mas.
function EstaEnUso(Ruta: String): Boolean; var Handle: Integer; begin Handle:= FileOpen(Ruta,fmOpenRead or fmShareExclusive); if Handle < 0 then begin Result:= TRUE; end else begin CloseHandle(Handle); Result:= FALSE; end; end; function GetFileModifyDate(FileName: string): TDateTime; var SearchRec: TSearchRec; begin if FindFirst(Filename,faAnyFile,SearchRec) = 0 then Result:= FileDateToDateTime(SearchRec.Time) else Result:= 0; FindClose(SearchRec); end; function EstaEnUso2(Ruta: String): Boolean; begin Result:= EstaEnUso(Ruta); if not Result then Result:= (Now - GetFileModifyDate(Ruta)) < EncodeTime(0,0,5,0); end; // Por ejemplo ShowMessage(BoolToStr(EstaEnUso2('c:\archivo.txt'),TRUE));
#8
Escrito 14 mayo 2010 - 02:36
por el momento llevo lo siguiente a manera de prueba a ver que les parece:
unit Unit2; interface uses Classes, Messages, Windows; type TestThread = class(TThread) private { Private declarations } FSource, FDestiny: string; FErrorCode: Integer; function ThMoveFile(Source, Destiny: string): Integer; function FCopy(Source, Destiny: string): Boolean; public constructor Create(ASource, ADestiny: string); reintroduce; overload; procedure Execute; override; protected end; implementation uses SysUtils, Unit1; { TestThread } constructor TestThread.Create(ASource, ADestiny: string); begin inherited Create(True); FSource:= ASource; FDestiny:= ADestiny; FreeOnTerminate:= True end; function TestThread.FCopy(Source, Destiny: string): Boolean; var S, D: TFileStream; begin Result:= True; try s:= TFileStream.Create(Source, fmOpenRead + fmShareExclusive); try D:= TFileStream.Create(Destiny, fmCreate); try D.CopyFrom(S, S.Size); finally D.Free; end; finally S.Free end except Result:= False end end; function TestThread.ThMoveFile(Source, Destiny: string):Integer; begin Result:= 1; if not FileExists(Source) then Exit; Result:= 2; if not DirectoryExists(ExtractFileDir(Destiny)) then Exit; ///////////// Copiar archivo /////////////////////////// Result:= 3; SendMessage(Form1.Handle, WM_USER + 100, 0, Integer(PChar('Iniciando copia'))); if not CopyFile(PChar(Source), PChar(Destiny), False) then Exit; ///////////// Verificar copia /////////////////////////// Result:= 4; Result:= 5; if not FileExists(Source) then Exit; Result:= 6; if not DeleteFile(PChar(Source)) then Exit; Result:= 0 // Verificar que exista el archivo origen // Verificar que exista el directorio destino // Copiar archivo // Si no lo copia enviar ERROR y SALIR // Archivo copiado, se continua // Verificar archivo copiado // Hay problemas con el archivo copiado ERROR y SALIR // Todo OK con el archivo Destino // Borrar el archivo Destino // No se borro ERROR SALIR // Si se borró EXITO TOTAL valor de retorno 0 end; procedure TestThread.Execute; begin FErrorCode:= ThMoveFile(FSource, FDestiny); SendMessage(Form1.Handle, WM_SETTEXT, 0, Integer(PChar('Salida = ' + IntToStr(FErrorcode)))) end; end.
He usado mensajes estándar de windows para comunicarme por el momento con el form principal y utilizé el método FCopy que saqué de alguna web también para hacer pruebas...
La función que mueve le puse un valor de retorno porque necesito saber exactamente lo que sucede en caso de que no se se obtenga éxito total.
Gracias por su apoyo y saludos
#9
Escrito 17 mayo 2010 - 08:53
Más o menos algo así
type TMainTh = class(TThread) private { Private declarations } FSource, FTarget: string; FMaxThreads: Integer; procedure OnNewThread(var Msg: TMessage); message TM_NEWTHREAD; procedure OnDieThread(var Msg: TMessage); message TM_DIETHREAD; procedure OnErrThread(var Msg: TMessage); message TM_ERRTHREAD; protected procedure Execute; override; end;
Claro, con su respectivo código en implementation.
Gracias por su apoyo camaradas.
#10
Escrito 17 mayo 2010 - 09:49
...¿es conveniente usar mensajes de Windows para llevar al cabo esta comunicación? he probado los mensajes entre el thread de copiado y el form principal de mi aplicación, y va de maravilla, ¿lo recomiendan?
Es una forma bastante común para controlar lo que pasa en el thread que éste envía mensajes a una ventana o formulario. También se pueden mandar mensajes al Thread con PostThreadMessage. Otro truco puede ser el uso de una variable global. Creo que lo mejor son los mensajes.
Saludos.
#11
Escrito 17 mayo 2010 - 10:07
...¿es conveniente usar mensajes de Windows para llevar al cabo esta comunicación? he probado los mensajes entre el thread de copiado y el form principal de mi aplicación, y va de maravilla, ¿lo recomiendan?
Es una forma bastante común para controlar lo que pasa en el thread que éste envía mensajes a una ventana o formulario. También se pueden mandar mensajes al Thread con PostThreadMessage. Otro truco puede ser el uso de una variable global. Creo que lo mejor son los mensajes.
Saludos.
Eso me tranquiliza, ya llevo algo avanzado con los mensajes, he leido sobre SendMesage del API de Windows y PostThreadMessage, y tengo algunas dudillas (si, ya sé, ahí voy de nuevo), al diferencia entre ambas es clara, SendMessage espera a que sea procesado el mensaje y PosThreadMessage no se espera ¿cúal de los dos es mejor para el uso que les estoy dando? y la otra duda es PostThreadMessage recibe como primer parámetro idThread, ¿es este el handle del Thread?
Gracias por tu respuesta escafandra y prometo seguir molestando hasta aclarar mis dudas.
#12
Escrito 17 mayo 2010 - 11:58
...SendMessage espera a que sea procesado el mensaje y PosThreadMessage no se espera ¿cúal de los dos es mejor para el uso que les estoy dando?
Por el contexto de tu pregunta creo que confundes PostMessage con PosThreadMessage. Ninguno de los dos espera contestación, pero mientras que PostMessage envía un mensaje a una ventana PosThreadMessage lo hace a un Thread. Si es mejos esperar o no depende de cada caso en particular. Ten encuenta que algunos mensajes devuelven un resultado. SendMessage devuelve un LRESULT.
... otra duda es PostThreadMessage recibe como primer parámetro idThread, ¿es este el handle del Thread?
El idThread es el identificador del Thread, es un entero no un Handle. En la clase TThread es el miembro ThreadID.
Saludos.
#13
Escrito 17 mayo 2010 - 04:21
SendMessage(Form1.Handle, WM_MYERROR, 0, Integer(PChar('Salida = ' + IntToStr(FErrorcode))))
Al capturar el evento en Form1, como saco de regreso el texto que introduje en lParam del mensaje, es un puntero, ¿pero como le hago? eso lo vi en un ejemplo.
Gracias
#14
Escrito 17 mayo 2010 - 04:33
PCHAR(lParam);
Saludos.
#15
Escrito 18 mayo 2010 - 08:12
Gracias de nuevo
#16
Escrito 18 mayo 2010 - 11:13
Ahora lo siguiente: envío de un thread a otro thread un mensaje con SendMessage y PostThreadMessage y pareciera que no le llegan...
¿Qué puede ser, lo estaré escribiendo mal?
Ahí va un poco de código:
SendMessage(FMainThHandle, TM_NEWTHREAD, Integer(PChar(Source)), Self.Handle);
Este mensaje es enviado desde un thread a otro thread. Donde FMainThHandle es el Handle del Thread destino, Self.Handle es el Handle del emisor, esto lo hago por motivos informativos.
FMainThHandle recibe y procesa el mensaje en:
type TMainTh = class(TThread) private { Private declarations } ... // Mensajes provenientes de los threads de copiado procedure OnNewThread(var Msg: TMessage); message TM_NEWTHREAD;
Aquí esta en la declaración del thread receptor como capturo el mensaje y en la implementación su código.
Pero pareciera que el thread receptor no recibe los mensajes que envío del thread emisor con SendMessage, o el thread receptor no los recibe de la manera en que estoy implementando para recibir el mensaje, seguro estoy haciendo algo mal...
Ayuda por favor..
#17
Escrito 18 mayo 2010 - 03:47
Para enviar un Mensaje a un thread debes usar PostThreadMessage, pero no basta. Debes implementar un bucle de mensajes en el thread que los gestione. Te pongo un ejemplo sobre tu código:
type TMainTh = class(TThread) private { Private declarations } ... // Mensajes provenientes de los threads de copiado procedure OnNewThread(var Msg: TMessage);
procedure TMainTh.Execute; var Msg: TMsg; begin while (not Terminated) do begin if PeekMessage(Msg,0,0,0,PM_REMOVE) then begin if(Msg.message = WM_QUIT) then Terminate; if (Msg.message = TM_NEWTHREAD) then OnNewThread(var Msg: TMessage); end; end; end;
Irías implementando todas las respuestas a los mensajes.
En delphi es mas fácil mandar un mensaje desde un thread a un Form que al revés. Tienes otras formas de comunicarte con el Thread como funciones miembro del mismo y Variables globales pero en este caso no te olvides de usar el método Synchronize.
Saludos.
#18
Escrito 18 mayo 2010 - 04:07
Sabía que algo estaba haciendo mal, pero no que, con esto ya tengo otro avance.
Gracias de nuevo.
#19
Escrito 19 mayo 2010 - 02:52
Saludos.
#20
Escrito 20 mayo 2010 - 10:54