FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour CopyFileEx
Posts: 731
Joined: Fri Oct 07, 2005 07:42 AM

CopyFileEx

Posted: Wed Jul 25, 2007 04:01 PM
Usando CopyFileEx, con fivewin , por ejemplo.

Bueno, en un proyecto he tenido que copiar un fichero de un sitio a otro,
si bueno teniamos la funcion MyCopyFile() que teniamos implementada en
Fivewin de la siguiente manera:
DLL32 FUNCTION MYCOPYFILE( lpExistingFileName AS STRING, lpNewFileName AS STRING, bFailIfExists AS LONG) AS LONG;
PASCAL FROM "CopyFileA" LIB "kernel32.dll"


Dicha funcion, para copiar ficheros de 1 o 2 megas, funciona muy bien, porque
da tiempo a nuestra aplicaci贸n que no se vuelva con un '( No responde )'.

Si no lo crees, muy simple, intenta copiar un fichero de 100 Megas, y me cuentas :-)

B谩sicamente, es porque nosotros HEMOS PERDIDO TOTALMENTE el control sobre nuestra
aplicacion, y no podemos recibir/enviar mensajes a Windows en que punto estamos.

Esto tiene f谩cil soluci贸n, nos hacemos nuestra propia funci贸n CopyFile().
El mayor inconveniente es que tenemos que hacerla :-), y eso, es un paso atr谩s.

驴 Como podemos hacerlo entonces ? Muy simple usando la funcion CopyFileEx(), disponible en el API win32.

Dicha funci贸n no funciona bajo los Windows 98 o similares, est谩is advertidos, o eso dicen las malas lenguas.

Bien, sin m谩s dilaciones vamos a realizar la implementaci贸n, y eso si,
aqui vamos a usar C, por cuesti贸n de velocidad.

Nuestro prototipo
CopyFileEx( Fichero_Origen, Fichero_Destino, bCodeblock ) --> nResult
Fichero_Origen y Fichero_Destino no necesitan explicaci贸n.
bCodeblock , es un bloque de c贸digo que nos va a pasar ;
{ |nPorcentaje, nTotal, nTransferido | MyFunction( nPorcentaje, nTotal, nTransferido ) }

Un ejemplo de llamada;
 if ( COPYFILEEX ( cFile_Path_MDB , cFile_Path_Repara+"\TPVMAL.MDB", {|x,y,z| pasa(x,y,z) } ) == 0 )
    ? "MAL"
 endif


Ah!, Ahora puedes ver, que encima vamos a poder montar una progressbar ,
por ejemplo, para ir mostrando el % que va quedando, es el primer par谩metro,
que simplemente resulta de ;
nPorcentaje = nTransferido * 100 / nTotal

Lo he pasado como par谩metro al codeblock, porque de esta manera, ya lo tienes
calculado a nivel de C, no perdiendo m谩s tiempo.

Lo importante es que puedes crear una funcion como;

STATIC s_lCancel := .F.

#define PROGRESS_CONTINUE 0
#define PROGRESS_CANCEL   1 

STATIC FUNCTION PASA( nPorcentaje, nTotal, nTransferido )
    static nPasa := 0
    
    if nPasa > 10
       SysRefresh()
       nPasa := 0
    endif
    nPasa++
    oProgress:SetPos( nPorcentaje )
    
return( if( s_lCancel, PROGRESS_CANCEL, PROGRESS_CONTINUE  ) )


Uy! 驴 Pero que ven tus ojitos!! ? Si , PUEDES CANCELAR tambi茅n lo que
estas copiando, :-) , muy 煤til si quieres cancelar un archivo de 500 Megas,
por ejemplo, cr茅eme.

En este caso, por ejemplo, puedes poner un bot贸n similar a esto;
REDEFINE BUTTON oBtn ACTION ( s_lCancel := .T. ) ID 110 OF oWnd

El cambiar el estado de la variable static s_lCancel, simplemente har谩
que cuando el codeblock se vuelva a ejecutar, lo cancelar谩.

Ahora bien, todo esto no es posible sin el codigo fuente de C, asi
que dejo paso al codigo fuente;
#pragma BEGINDUMP
#include <windows.h>
#include <stdio.h>
#include "hbapi.h"
#include "item.api"
#include "hbapiitm.h"
#include "hbvm.h"
#include "hbapiitm.h"

/*
 Convertimos un valor LARGE_INTEGER a double
*/
 double clarge2int( DWORD Lo, DWORD Hi )
 {
  double dblLo, dblHi;
  double ret;

  if( Lo < 0  ){
     dblLo = 2 ^ 32 + Lo ;
  } else {
     dblLo = Lo;
  }

  if( Hi < 0 ) {
    dblHi = 2 ^ 32 + Hi;
  } else {
    dblHi = Hi;
  }
  
  ret = ( dblLo + dblHi);

  return( ret );
 }

DWORD CALLBACK CopyProgressRoutine(
  LARGE_INTEGER TotalFileSize,
  LARGE_INTEGER TotalBytesTransferred,
  LARGE_INTEGER StreamSize,
  LARGE_INTEGER StreamBytesTransferred,
  DWORD dwStreamNumber,
  DWORD dwCallbackReason,
  HANDLE hSourceFile,
  HANDLE hDestinationFile,
  LPVOID pCallback_Progress
)
{
   double TotalSize = clarge2int( TotalFileSize.u.LowPart, TotalFileSize.u.HighPart );
   double TotalBytesTrans = clarge2int( TotalBytesTransferred.u.LowPart, TotalBytesTransferred.u.HighPart );
   double percent =  TotalBytesTrans * 100 / TotalSize ;
                                            
    if( pCallback_Progress ) {
        hb_vmPushSymbol( &hb_symEval );
        hb_vmPush( pCallback_Progress );
        hb_vmPushDouble( percent,1 );
        hb_vmPushDouble( TotalSize,1 );
        hb_vmPushDouble( TotalBytesTrans,1 );
        hb_vmSend( 3 );     
        return( hb_parni( -1 ) );
     }

 return PROGRESS_CONTINUE;
 }


HB_FUNC( COPYFILEEX )
{
  LPCTSTR lpExistingFileName = hb_parc( 1 );
  LPCTSTR lpNewFileName = hb_parc( 2 );
  LPPROGRESS_ROUTINE lpProgressRoutine = ( void * )CopyProgressRoutine ;
  LPBOOL pbCancel = NULL;
  DWORD dwCopyFlags;
  BOOL ret; 
  PHB_ITEM pCallback_Progress;
  
  if( ! ISNIL( 3 ) ) {
     pCallback_Progress = hb_itemNew( hb_param( 3, HB_IT_ANY ) );
     }

  ret = CopyFileEx( lpExistingFileName, lpNewFileName, lpProgressRoutine, pCallback_Progress, pbCancel, NULL );
  hb_retni( ret );
  hb_itemRelease( (PHB_ITEM) pCallback_Progress );

 }

#pragma ENDDUMP

Lo que m谩s me a costado no a sido la implementaci贸n de la funcion en si
misma, si no, convertir un LARGE_INTEGER en un valor double.

No es una funci贸n portable del API de Windows para Harbour, es una adaptaci贸n
a una necesidad en concreto, si lo quereis hacer compatible con el API de
Windows, ahi ten茅is un punto de partida, por mi parte no pienso mejorarla
m谩s.

Antonio, seria interesante portar esta funcion a Fivewin.
Los warnings de C, son porque no se usan ciertos parametros de la callback, no se como decirle al compilador que no los muestre.
Saludos

Rafa Carmona ( rafa.thefullARROBAgmail.com___quitalineas__)
Posts: 428
Joined: Thu Oct 19, 2006 12:28 PM

Re: CopyFileEx

Posted: Mon Feb 02, 2009 11:48 AM
Buenos dias

Me gustaria saber si se ha implementado ya algo parecido a 茅ste post un poco ya antiguo de The Full, ya que necesito copiar ficheros de unos 50 megas aproximadamente.

De todas las maneras, he hecho lo que pone en el post y copia los ficheros correctamente, pero se me queda colgado en la barra de progreso.

Ser谩 que estoy construyendo el dialogo de progreso mal, ya que en el ejemplo que pone The full:
    STATIC FUNCTION PASA( nPorcentaje, nTotal, nTransferido )
        static nPasa := 0
       
        if nPasa > 10
           SysRefresh()
           nPasa := 0
        endif
        nPasa++
        oProgress:SetPos( nPorcentaje )
       
    return( if( s_lCancel, PROGRESS_CANCEL, PROGRESS_CONTINUE  ) )


Da un error en oProgress, y lo que deduzco es que hay que hacer un dialogo progress, y hago esto:
STATIC FUNCTION PASA( nPorcentaje, nTotal, nTransferido )
    static nPasa := 0
    local oDlg1,oprogress
    DEFINE DIALOG oDlg1 TITLE "Espera por favor" SIZE 250,50 PIXEL STYLE (WS_CAPTION)
    @ 5, 3 PROGRESS oProgress POSITION 0 SIZE 120, 10 PIXEL
    if nPasa > 10
       SysRefresh()
       nPasa := 0
    endif
    nPasa++
    oProgress:SetPos( nPorcentaje )

    ACTIVATE DIALOG oDlg1 CENTERED 

  
return( if( s_lCancel, PROGRESS_CANCEL, PROGRESS_CONTINUE  ) )


Hago algo mal?

Saludos
--------------------------

Saludos



Jose Luis
Posts: 2170
Joined: Fri Jul 18, 2008 01:24 AM

Re: CopyFileEx

Posted: Mon Feb 02, 2009 02:41 PM

Esta funcion la he usado durante a帽os, desde FW 2.0, y bueno... hasta el momento me ha funcionado.
Talvez quieran probarla (si aun no lo han hecho). No es mia, venia como ejemplo en FW
//----------------------------------------------------------------------------//

function CopyFiles( aSource, aTarget, nBufSize )

local oDlg, oSay1, oSay2, oSay3, oBtnCancel
local oMeter1, oMeter2
local nAmount1, nAmount2
local lEnd := .f.

DEFAULT nBufSize := 4000

DEFINE DIALOG oDlg RESOURCE "CopyFiles"

REDEFINE SAY oSay1 ID 110 OF oDlg
REDEFINE SAY oSay2 ID 120 OF oDlg

REDEFINE METER oMeter1 VAR nAmount1 ID 130 OF oDlg

REDEFINE SAY oSay3 ID 140 OF oDlg
REDEFINE METER oMeter2 VAR nAmount2 ID 150 OF oDlg

REDEFINE BUTTON oBtnCancel ID 2 OF oDlg ;
ACTION ( lEnd := .t., SysRefresh(), oDlg:End() )

oDlg:bStart := { || StartCopy( aSource, aTarget, nBufSize,;
oSay1, oSay2, oMeter1, oSay3, oMeter2,;
@lEnd, oDlg ),;
oBtnCancel:SetText( "&Ok" ) }

ACTIVATE DIALOG oDlg CENTERED
return nil

//----------------------------------------------------------------------------//

static function StartCopy( aSource, aTarget, nBufSize, oSay1, oSay2,;
oMeter1, oSay3, oMeter2, lEnd, oDlg )

local n
local hSource, hTarget
local cBuffer := Space( nBufSize )
local nBytes, nFile := 0, nTotal := 0
local nTotSize := 0

for n = 1 to Len( aSource )
if ! File( aSource[ n ] )
MsgInfo( "Fichero no encontrado: " + aSource[ n ], "Advertencia" )
else
hSource = FOpen( aSource[ n ] )
nTotSize += FSeek( hSource, 0, 2 )
FClose( hSource )
endif
SysRefresh()
next

oMeter2:nTotal = nTotSize

for n = 1 to Len( aSource )
IF File( aSource[ n ] )
hSource = FOpen( aSource[ n ] )
hTarget = FCreate( aTarget[ n ] )
oSay1:SetText( "Fuente : " + aSource[ n ] )
oSay2:SetText( "Destino: " + aTarget[ n ] )
oMeter1:Set( 0 )
oMeter1:nTotal = FSeek( hSource, 0, 2 )
FSeek( hSource, 0, 0 )
nFile := 0
SysRefresh()
while ( nBytes := FRead( hSource, @cBuffer, nBufSize ) ) > 0
FWrite( hTarget, cBuffer, nBytes )
oSay3:SetText( "Bytes copiados: " + ;
AllTrim( Str( nTotal += nBytes ) ) )
oMeter1:Set( nFile += nBytes )
oMeter2:Set( nTotal )
SysRefresh()
end
FClose( hSource )
FClose( hTarget )
if lEnd
exit
endif
ENDIF
next

oDlg:End()

return nil

Saludos.

Francisco J. Alegr铆a P.

Chinandega, Nicaragua.



Fwxh-MySql-TMySql
Posts: 731
Joined: Fri Oct 07, 2005 07:42 AM

Re: CopyFileEx

Posted: Mon Feb 02, 2009 04:09 PM

Es que estas llamando cada vez a la creaci贸n del dialogo.

Lo que tienes que hacer es lo siguiente, seudo codigo a pi帽on;

STATIC oProgressBar // Porque la funci贸n PASA va a actualizartelo.

FUNCTION DlgMio()
Local oDlg
DEFINE DIALOG oDlg
@1,1 PROGRESSBAR oProgressBar
@2,1 BUTTON CopyDlg()
@3,1 BUTTON CancelDlg()

ACTIVATE DIALOG
RETURN NIL

FUNCTION CopyDlg()
if ( COPYFILEEX ( cFile_Path_MDB , cFile_Path_Repara+"\TPVMAL.MDB", {|x,y,z| pasa(x,y,z) } ) == 0 )
? "MAL"
endif
return nil

STATIC FUNCTION PASA( nPorcentaje, nTotal, nTransferido )
static nPasa := 0

if nPasa &gt; 10
   SysRefresh()
   nPasa := 0
endif
nPasa++
oProgress:SetPos( nPorcentaje )  // Esto es una variable static

return( if( s_lCancel, PROGRESS_CANCEL, PROGRESS_CONTINUE ) )

Lo pillas ahora mejor ?

Saludos

Rafa Carmona ( rafa.thefullARROBAgmail.com___quitalineas__)
Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM

Re: CopyFileEx

Posted: Mon Feb 02, 2009 04:58 PM

Usando FOpen(), FCreate(), FRead(), FWrite() y FClose() tenemos control total y podemos hacer lo que queramos :-)
Ambas soluciones son validas:

  1. Pedirle al API de Windows que lo haga, usando una funcion tipo callback como ha hecho Rafa.

  2. Hacerlo a "mano" usando estas funciones mencionadas de Clipper de toda la vida :-)

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 428
Joined: Thu Oct 19, 2006 12:28 PM

Re: CopyFileEx

Posted: Mon Feb 02, 2009 07:37 PM

Hola Rafa

Gracias por responder

Tenia encima la empanadilla de M贸stoles

Solucionado

Gracias

Jose Luis

--------------------------

Saludos



Jose Luis

Continue the discussion