Re: copyFileEx() hanging
- Posted by Matt Lewis <matthewwalkerlewis at gmail.com> Jul 25, 2005
- 881 views
Greg Haberek wrote: > > I'm almost done with my backup program for work. One person requested > a file-copy progress bar along with a total count progress bar. The > total count bar works fine, but when attempting to use copyFileEx() > with call-back routine, it just hangs the app. copyFileEx() still > copies the file successfully, however. So I am led to believe this is > a call-back issue. The call-back routine accepts 9 parameters, which I > believe is the limit, so I should be fine. > > I've whipped up a small demo that demonstates this hanging behavior. > It may be downloaded here: > <a > href="http://www.freewebtown.com/ghaberek/copy_crash.zip">http://www.freewebtown.com/ghaberek/copy_crash.zip</a> > > Any advice is welcome. There are a few problems with your code. First, pbCancel is really a pointer to a boolean. You're supposed to be able to set that at any time during progress to cancel the transfer. Seems like it's duplicating the purpose of the callback, but there you go. (Technically, lpData is supposed to be a pointer, but it probably doesn't really matter since Windows doesn't use it for anything). Second, the first 4 parameters are defined as LARGE_INTEGERs, which means that they're actually 64-bit integers. You need to use 2 32-bit parameters to properly accept them. Then you can multiply the high value by #100000000 and add together. But this means that you're over the 9-parameter limit, so you can use fptr.e (http://www.rapideuphoria.com/fptr.zip) to get around this. Finally, the callback should actually be a cdecl callback. Here's how I reworked the file so that it works (however, even copying a 10MB file, it happens so quickly that no progress calls are actually made)--I've omitted the createDir() routine, since I didn't change anything there:
include windir.ew as WD include fptr.e global constant xCopyFileExW = registerw32Function( kernel32, "CopyFileExW", {C_POINTER,C_POINTER,C_POINTER,C_POINTER,C_POINTER,C_LONG}, C_INT ) global constant xCreateDirectoryW = registerw32Function( kernel32, "CreateDirectoryW", {C_POINTER,C_POINTER}, C_INT ) global constant -- values for dwCallbackReason CALLBACK_CHUNK_FINISHED = #00000000, CALLBACK_STREAM_SWITCH = #00000001, -- return values from CopyProgressRoutine PROGRESS_CONTINUE = 0, PROGRESS_CANCEL = 1, PROGRESS_STOP = 2, PROGRESS_QUIET = 3 atom pbCancel pbCancel = allocate( 4 ) global function copyFileEx( sequence pExistingFileName, sequence pNewFileName, object pFlags ) -- pFlags may be dwCopyFlags or {lpProgressRoutine, lpData, pbCancel, dwCopyFlags} atom lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, dwCopyFlags integer retv -- \\?\ forces Windows to turn of directory parsing, effectively -- increasing the path length from 255 to 32,767 characters pExistingFileName = "\\\\?\\" & pExistingFileName pNewFileName = "\\\\?\\" & pNewFileName lpExistingFileName = WD:allocate_unicode( pExistingFileName ) lpNewFileName = WD:allocate_unicode( pNewFileName ) pbCancel = allocate(4) if sequence(pFlags) and length(pFlags) = 4 then lpProgressRoutine = pFlags[1] lpData = allocate(4) poke4( lpData, pFlags[2]) --pbCancel = pFlags[3] poke4( pbCancel, pFlags[3] ) dwCopyFlags = pFlags[4] else lpProgressRoutine = 0 lpData = 0 --pbCancel = 0 poke4( pbCancel, 0 ) dwCopyFlags = pFlags end if retv = w32Func( xCopyFileExW, {lpExistingFileName,lpNewFileName,lpProgressRoutine,lpData,pbCancel,dwCopyFlags} ) free( lpExistingFileName ) free( lpNewFileName ) return retv end function -- set CopyCancel to w32True to stop the copy process global integer CopyCancel CopyCancel = w32False global function CopyProgressRoutine( atom TotalFileSizeLow, atom TotalFileSizeHigh, atom TotalBytesTransferredLow, atom TotalBytesTransferredHigh, atom StreamSizeLow, atom StreamSizeHigh, atom StreamBytesTransferredHigh,atom StreamBytesTransferredLow, object params ) atom dwStreamNumber, dwCallbackReason, hSourceFile, hDestinationFile, lpData atom TotalFileSize, TotalBytesTransferred, StreamSize, StreamBytesTransferred TotalFileSize = TotalFileSizeHigh * #100000000 + TotalFileSizeLow TotalBytesTransferred = TotalBytesTransferredHigh * #100000000 + TotalBytesTransferredLow StreamSize = StreamSizeHigh * #100000000 + StreamSizeLow StreamBytesTransferred = StreamBytesTransferredHigh * #100000000 + StreamBytesTransferredLow params = peek4u( params & 5 ) dwStreamNumber = params[1] dwCallbackReason = params[2] hSourceFile = params[3] hDestinationFile = params[4] lpData = params[5] -- This routine will update your scroll bar -- with the status of a file copy progress. -- Your progress bar ID should be the lpData -- parameter of copyFileEx() -- -- Example: -- i = copyFileEx( "Myfile1.ext", "Myfile2.ext", {CopyProgressRoutine_CB, ProgBar1, 0, 0} ) if CopyCancel = w32True then return PROGRESS_CANCEL end if if dwCallbackReason = CALLBACK_STREAM_SWITCH then -- call for first time, setup scroll bar if lpData != 0 then lpData = peek4u( lpData ) setScrollRange( lpData, 0, TotalFileSize ) setScrollPos( lpData, 0 ) end if CopyCancel = w32False elsif dwCallbackReason = CALLBACK_CHUNK_FINISHED then -- call to update scroll bar if lpData != 0 then lpData = peek4u( lpData ) setScrollPos( lpData, TotalBytesTransferred ) doEvents( 0 ) end if end if return PROGRESS_CONTINUE end function global constant CopyProgressRoutine_ID = routine_id( "CopyProgressRoutine" ), CopyProgressRoutine_CB = call_back_cdecl( CopyProgressRoutine_ID, 13 )
Matt Lewis