Re: copyFileEx() hanging

new topic     » goto parent     » topic index » view thread      » older message » newer message

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

new topic     » goto parent     » topic index » view thread      » older message » newer message

Search



Quick Links

User menu

Not signed in.

Misc Menu