26 de Julio 2007

CopyFileEx. API Win32

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:
Code:

DLL32 FUNCTION MYCOPYFILE( lpExistingFileName AS STRING, lpNewFileName AS STRING, bFailIfExists AS LONG) AS LONG;
PASCAL FROM "CopyFileA" LIB "kernel32.dll"


Dicha función, 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 Wink

Básicamente, es porque nosotros HEMOS PERDIDO TOTALMENTE el control sobre nuestra
aplicación, y no podemos recibir/enviar mensajes a Window 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 Wink, y eso, es un paso atrás.

¿ Como podemos hacerlo entonces ? Muy simple usando la función 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;
Code:

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;

Code:
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, Wink , 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;
Code:

#pragma BEGINDUMP
#include
#include
#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 queréis hacer compatible con el API de
Windows, ahí tenéis un punto de partida, por mi parte no pienso mejorarla
más, simplemente necesitaba esa funcionalidad y el APi de Window me la proporciona.

Escrito por Rafa Carmona a las 26 de Julio 2007 a las 12:15 AM | TrackBack
Comentarios
Escribir un comentario









¿Recordar informacion personal?