A little help calling Windows APIs

BlitzMax Forums/BlitzMax Programming/A little help calling Windows APIs

TaskMaster(Posted 2013) [#1]
I have this API call to copy a file, and it works fine.

Extern "win32"
	Function CopyFileExW:Int(SrcFile$W, DestFile$W, CallBack:Byte Ptr, Data:Int Ptr, Cancel:Int Ptr, Flags:Int)
	Function GetLastError:Int()
End Extern

Local ret:Int = CopyFileExW(src, dst, Null, Null, Null, 0)
If Not ret
	ret = GetLastError()
	DebugLog ret
End If



This works.

And here is a link to the CopyFileExW function:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa363852(v=vs.85).aspx

If you look in the function, there is a CallBack you can make, that will give your progress as it run. I am having a hard time figuring out how to implement that.

Here is what I tried:

Extern "win32"
	Function CopyFileExW:Int(SrcFile$W, DestFile$W, CallBack:Byte Ptr, Data:Int Ptr, Cancel:Int Ptr, Flags:Int)
	Function GetLastError:Int()
End Extern

Function CopyProgress:Int (TotalSize:Long Ptr, TotalTransferred:Long Ptr, StreamSize:Long Ptr, StreamTransferred:Long Ptr, StreamNumber:Int Ptr, CallbackReason:Int Ptr, hSourceFile:Int Ptr, hDestFile:Int Ptr, Data:Int Ptr) 
	
DebugLog Int(StreamNumber)
Return 0

End Function

Local ret:Int = CopyFileExW(src, dst, CopyProgress, Null, Null, 0)
If Not ret
	ret = GetLastError()
	DebugLog ret
End If



This causes an access violation error.

It is partially working though, as it seems to work until it reaches the last block of the copy stream. If it takes 5 copy blocks, it will perform the first 4 then crash after the 5th one.

Also, if I return a 1 which is a copy cancel, then it will cancel and go on to the next file and not give the error either. So I am not 100% sure what I am doing wrong.

Here you can find the definition of the callback:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa363854(v=vs.85).aspx

Any help would be appreciated.

Thanks.


col(Posted 2013) [#2]
First thing I notice is that you need to use the Win32 calling convention for Win32API functions and callbacks, so just stick a little "Win32" at the end of the function callback after the parameters.

ie

Function CopyProgress([.....])"Win32"


TaskMaster(Posted 2013) [#3]
Thanks col. I added that, does not seem to have changed a thing. Still does the same thing.

It seems the copy goes all the way til the end, then fails to finish because of the callback. I am unsure why.


col(Posted 2013) [#4]
Ok, I'll get the function running here and post back in 10 mins. It may because of using the *W and not *A version of the function.....


col(Posted 2013) [#5]
Extern"Win32"
	Function CopyFileExA(lpExistingFileName$z,lpNewFileName$z,lpProgressRoutine:Byte Ptr,lpData:Byte Ptr,lpCancel:Int,dwCopyFlags:Int)
	Function GetLastError:Int()
EndExtern

Function CopyProgress(TotalFileSize:Long,TotalBytesTransferred:Long,SizeStream:Long,StreamBytesTransferred:Long,dwStreamNumber:Int,dwCallbackReason:Int,hSourceFile:Int,hDestinationFile:Int,lpData:Byte Ptr)"Win32"
	Return 0
EndFunction

'Test

Local ThisFile$ = AppFile.Replace(".debug","").Replace(".exe",".bmx")
Local Destfile$ = StripExt(ThisFile)+"_copy"+".bmx" ' Rename this source file to *_copy.bmx

If CopyFileExA(ThisFile,DestFile,CopyProgress,Null,False,0) = 0
	DebugLog "Failed with error code: " + GetLastError()
EndIf


This works ok. When run it copies itself ( the source file ) to the same directory with _copy appended to the filename before the file extension.


TaskMaster(Posted 2013) [#6]
Excellent, works great.

Thanks col.

So, did having all of the pointers in my callback screw it up?

What is the difference between the CopyFileExA and the CopyFileExW functions?


col(Posted 2013) [#7]
*A is the Ansi version, ie 1 byte per character, *W is for the unicode version which is 2 bytes. You just need to make sure that the parameters match up correctly specifically for strings. I tend to use *A as *W gives me problems that I've not investigated into being as *A works without problems.


So, did having all of the pointers in my callback screw it up?



I would think so.
But not because they were pointers being used as pointers - its not as though youre accessing them in the callback which is when EAVs would have occured ( referencing memory ranges out of range ie address $00000000 ). But because a * Ptr is a 32bit value and a Long is a 64 bit value.

A longer explanation...
Parameters are passed via the stack. When a 64 bit value is pushed to the stack then the stack pointer will move back the equivalent of a 64 bit value ie 2x32 values. Because youre using a *Ptr in the function parameters and "Win32" functions rearrange the stack before they return then this will cause the stack to be misaligned, which will in turn return control to the wrong address after the function has exited.

And more specifically...
The CopyFileEx* function will push the equivalent of 13 integers onto the stack just before calling the BMax callback function which will be made up of..

4 x Long values ( equivalent of 8 x 32bit integers in memory size )
4 x Int ( 4 x 32bit integers )
1 x *ptr ( 1 x 32bit integer )

so the CALLED function ( because ALL windows functions use and expect other functions that it calls to use the _stdcall calling convention ) should itself pop the equivalent back off the stack before returning ( or as it returns ). In this case the called function is the BMax function. Because initially your function was using all *ptr(s) in the parameters which are 1 x 32bit integers each, when the function exits the equivalent of 9 x 32bit integers are popped off but it should pop the equivalent of 13 integers, hence you get a stack misalignment and a crash.

I'm not sure if you understand any if that. I find it difficult explaining things so they are easily understood :-)


TaskMaster(Posted 2013) [#8]
Makes perfect sense.

Thanks again for the help.