Hello Mike,
I’ve actually got the callback working except that it GPF’s if I try to get to parameter values.
Thanks!!!
Hello Mike,
I’ve actually got the callback working except that it GPF’s if I try to get to parameter values.
Thanks!!!
As mentioned on Skype
You need to use the 8 byte LARGE ( or ULARGE) instead of the 4 byte LONG
There’s an include, something like i64.inc that has the LARGE data type (sorry I’m away from my computer ATM so I can’t look it up for you)
I would urge you to try ShellFileOperation. I posted the prototypes back in my prior post. Its not a simple call but it provides its own window with progress and does all the calculations.
For CopyFileExA() to work your prototypes look mostly all wrong to me, but I could be wrong. It has some complex stuff I’m not sure about. The only way to make it work is to dive in and try so if you want it to work just keep going.
Here’s how I would write the prototypes (not tested). I’m not sure what that pbCancel is about.
CopyFileExA PROCEDURE( | !BOOL WINAPI CopyFileExA(
*CSTRING lpExistingFileName , | !_In_ LPCSTR lpExistingFileName
*CSTRING lpNewFileName , | !_In_ LPCSTR lpNewFileName
LONG lpProgressRoutine=0 , | !_In_opt_ LPPROGRESS_ROUTINE lpProgressRoutine Address(CallBack)
UNSIGNED lpData=0 , | !_In_opt_ LPVOID lpData
<*BOOL pbCancel> , | !_Inout_opt_ LPBOOL pbCancel
UNSIGNED dwCopyFlags | !_In_ DWORD dwCopyFlags
),BOOL ,RAW,PASCAL,DLL(1),PROC,name('CopyFileExA')
LARGE_INTEGER_In EQUATE(REAL) !??? Not sure this is right ???
LPPROGRESS_ROUTINE PROCEDURE( | !DWORD WINAPI *LPPROGRESS_ROUTINE(
LARGE_INTEGER_In R_TotalFileSize , | !_In_ ??? LARGE_INTEGER TotalFileSize
LARGE_INTEGER_In R_TotalBytesTransferred , | !_In_ ??? LARGE_INTEGER TotalBytesTransferred
LARGE_INTEGER_In R_StreamSize , | !_In_ ??? LARGE_INTEGER StreamSize
LARGE_INTEGER_In R_StreamBytesTransferred , | !_In_ ??? LARGE_INTEGER StreamBytesTransferred
UNSIGNED dwStreamNumber , | !_In_ DWORD dwStreamNumber
UNSIGNED dwCallbackReason , | !_In_ DWORD dwCallbackReason
SIGNED hSourceFile , | !_In_ HANDLE hSourceFile
SIGNED hDestinationFile , | !_In_ HANDLE hDestinationFile
<UNSIGNED lpData> | !_In_opt_ LPVOID lpData
) ,UNSIGNED,PASCAL
TotalFileSize LIKE(INT64),OVER(R_TotalFileSize) !??? Not sure this is right
Ditto for other 3 R_xxx
A LARGE_INTEGER is 64 bits, do you know how those work in Clarion and the i64.INC file?
I would have to look at some of my code for Int64 but IIRC an INT64 passed by value uses the 64-bit Floating Point registers so in Clarion need to prototype as a REAL. But its not a REAL so put an INT64 OVER() it.
So I got it working.
Carl’s theory on using REAL’s is spot on.
MAP
CopyProgressRoutine(REAL TotalFileSize,REAL TotalBytesTransferred,REAL StreamSize,REAL StreamBytesTransferred,ULONG dwStreamNumber,ULONG dwCallbackReason,Long hSourceFile,Long hDestinationFile,Long lpData),ULONG,PASCAL,name('LpprogressRoutine')
MODULE('kernel32.dll')
drCopyFileExA(*CSTRING lpExistingFileName,*CSTRING lpNewFileName,ULONG lpProgressRoutine,Long lpData,BOOL pbCancel,DWORD dwCopyFlags),BOOL,PASCAL,RAW,name('CopyFileExA')
END
END
The Callback:
CopyProgressRoutine Procedure(REAL TotalFileSize,REAL TotalBytesTransferred,REAL StreamSize,REAL StreamBytesTransferred,ULONG dwStreamNumber,ULONG dwCallbackReason,Long hSourceFile,Long hDestinationFile,Long lpData)
myBase dwrBase
st StringTheory
TransferredG Group(drINT64),Over(TotalBytesTransferred).
FileSizeG Group(drINT64),Over(TotalFileSize).CODE !Get the progress myBase.SendDebug(myBase.GetProgress(TransferredG.lo,FileSizeG.lo))
…and the progress window
Dang … I made my own little example to prove the REAL worked.
Note this is only for a LARGE_INTEGER passed by Value, that I think would be rare,
and not passed by address P LARGE_INTEGER e.g. see GetDiskFreeSpaceExA()
!CopyFileEx with Callback test by Carl Barnes - Copyright 2022 - Released under the MIT License
PROGRAM
LARGE_INTEGER_In EQUATE(REAL) !64 bit INT passed by value Prototype as REAL
MAP
TestCopy PROCEDURE()
CopyFileEx_PPROGRESS PROCEDURE( | !DWORD WINAPI *LPPROGRESS_ROUTINE(
LARGE_INTEGER_In R_TotalFileSize , | !_In_ LARGE_INTEGER TotalFileSize
LARGE_INTEGER_In R_TotalBytesTransferred , | !_In_ LARGE_INTEGER TotalBytesTransferred
LARGE_INTEGER_In R_StreamSize , | !_In_ LARGE_INTEGER StreamSize
LARGE_INTEGER_In R_StreamBytesTransferred , | !_In_ LARGE_INTEGER StreamBytesTransferred
UNSIGNED dwStreamNumber , | !_In_ DWORD dwStreamNumber
UNSIGNED dwCallbackReason , | !_In_ DWORD dwCallbackReason
SIGNED hSourceFile , | !_In_ HANDLE hSourceFile
SIGNED hDestinationFile , | !_In_ HANDLE hDestinationFile
<UNSIGNED lpData> | !_In_opt_ LPVOID lpData
),UNSIGNED,PASCAL
MODULE('api')
OutputDebugString(*CSTRING cMsg),PASCAL,DLL(1),RAW,NAME('OutputDebugStringA')
GetLastError(),LONG,PASCAL,DLL(1)
CopyFileEx PROCEDURE( | !BOOL WINAPI CopyFileExA(
*CSTRING lpExistingFileName , | !_In_ LPCSTR lpExistingFileName
*CSTRING lpNewFileName , | !_In_ LPCSTR lpNewFileName
LONG lpProgressRoutine=0 , | !_In_opt_ LPPROGRESS_ROUTINE lpProgressRoutine Address(CallBack)
UNSIGNED lpData=0 , | !_In_opt_ LPVOID lpData
<*BOOL pbCancel> , | !_Inout_opt_ LPBOOL pbCancel
UNSIGNED dwCopyFlags | !_In_ DWORD dwCopyFlags
),BOOL ,RAW,PASCAL,DLL(1),PROC,name('CopyFileExA') !If the function succeeds, the return value is nonzero.
END !Module API
END
!https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-copyfileexa
COPY_FILE_FAIL_IF_EXISTS EQUATE(00000001h) !The copy operation fails immediately if the target file already exists.
COPY_FILE_NO_BUFFERING EQUATE(00001000h) !The copy operation is performed using unbuffered I/O, bypassing system I/O cache resources. Recommended for very large file transfers.
PROGRESS_CANCEL EQUATE(1) !Cancel the copy operation and delete the destination file.
PROGRESS_CONTINUE EQUATE(0) !Continue the copy operation.
PROGRESS_QUIET EQUATE(3) !Continue the copy operation, but stop invoking CopyProgressRoutine to report progress.
PROGRESS_STOP EQUATE(2) !Stop the copy operation. It can be restarted at a later time.
CODE
TestCopy()
TestCopy PROCEDURE()
ExistingFileName CSTRING('XXX.txt')
NewFileName CSTRING('XXX_Copy.txt')
ProgressRoutine LONG
lpData LONG
boolCancel BOOL
CopyFlags LONG
RetCopy BOOL
CODE
IF ~EXISTS(ExistingFileName) THEN COPY('CopyFileExTest.clw',ExistingFileName).
IF ~EXISTS(ExistingFileName) THEN
Message('You need to make a file to test copy named: ' & ExistingFileName, 'CopyFileEx Test File')
RETURN
END
REMOVE(NewFileName)
lpData=8312022
ProgressRoutine = ADDRESS( CopyFileEx_PPROGRESS )
RetCopy = CopyFileEx( |
ExistingFileName , | ! *CSTRING lpExistingFileName , | !_In_ LPCSTR lpExistingFileName
NewFileName , | ! *CSTRING lpNewFileName , | !_In_ LPCSTR lpNewFileName
ProgressRoutine , | ! LONG lpProgressRoutine=0 , | !_In_opt_ LPPROGRESS_ROUTINE lpProgressRoutine Address(CallBack)
lpData , | ! UNSIGNED lpData=0 , | !_In_opt_ LPVOID lpData
boolCancel , | ! <*BOOL pbCancel> , | !_Inout_opt_ LPBOOL pbCancel
CopyFlags ) ! UNSIGNED dwCopyFlags | !_In_ DWORD dwCopyFlags
Message('CopyFileEx return='& RetCopy & CHOOSE(~RetCopy,' FAILED',' Success!') & |
' <9>LastError='& GetLastError() &|
'||Copy From: <9>'& ExistingFileName & |
'|Copy To: <9>'& NewFileName & |
'','CopyFileEx Test')
RETURN
!=================
! https://docs.microsoft.com/en-us/windows/win32/api/winbase/nc-winbase-lpprogress_routine
CopyFileEx_PPROGRESS PROCEDURE( | !DWORD WINAPI *LPPROGRESS_ROUTINE(
LARGE_INTEGER_In R_TotalFileSize , | !_In_ LARGE_INTEGER TotalFileSize
LARGE_INTEGER_In R_TotalBytesTransferred , | !_In_ LARGE_INTEGER TotalBytesTransferred
LARGE_INTEGER_In R_StreamSize , | !_In_ LARGE_INTEGER StreamSize
LARGE_INTEGER_In R_StreamBytesTransferred , | !_In_ LARGE_INTEGER StreamBytesTransferred
UNSIGNED dwStreamNumber , | !_In_ DWORD dwStreamNumber
UNSIGNED dwCallbackReason , | !_In_ DWORD dwCallbackReason
SIGNED hSourceFile , | !_In_ HANDLE hSourceFile
SIGNED hDestinationFile , | !_In_ HANDLE hDestinationFile
<UNSIGNED lpData> ) !_In_opt_ LPVOID lpData ) !,UNSIGNED ,RAW,PASCAL
!Note: LARGE_INTEGER_In EQUATE(REAL) !64 bit INT passed by value Prototype as REAL
TotalFileSize LIKE(INT64),OVER(R_TotalFileSize) !Passed REAL but its really 64 bit INT
TotalBytesTransferred LIKE(INT64),OVER(R_TotalBytesTransferred)
StreamSize LIKE(INT64),OVER(R_StreamSize)
StreamBytesTransferred LIKE(INT64),OVER(R_StreamBytesTransferred)
ReturnProgress LONG
CODE
CASE Message('In callback lpData=' & lpData & |
'||TotalFileSize = ' & TotalFileSize.lo & |
' <9>TotalBytesTransferred = '& TotalBytesTransferred.lo & |
'|StreamSize = ' & StreamSize.lo & |
' <9>StreamBytesTransferred = ' & StreamBytesTransferred.lo & |
'||dwStreamNumber = ' & dwStreamNumber & |
'|dwCallbackReason = '& dwCallbackReason & |
'|hSourceFile = '& hSourceFile & |
' <9>hDestinationFile = '& hDestinationFile & |
'','CopyFileEx_PPROGRESS',ICON:Copy,'Continue|Quite|Cancel Copy')
OF 1 ; ReturnProgress = PROGRESS_CONTINUE
OF 2 ; ReturnProgress = PROGRESS_QUIET
OF 3 ; ReturnProgress = PROGRESS_CANCEL
END
RETURN ReturnProgress
CopyFileExTestProject.zip (2.5 KB)
Its the same trick I’m using here.
TLDR, the compiler isnt too fussy about the data types used in api declarations in clarion map modules() and windows isnt either provided the number of bytes of each parameter matches up in the right order.
A Real is an 8byte data type, so it gets its bytes and only later in the runtime does the code to process a Real get used.
A Real could be used where ever an 8byte parameter is specified, which would be alot of the 64bit api’s.
I should add, where you need to match the calling convention like whats detailed here x86 calling conventions - Wikipedia
just reverse the order of the parameters like I did, so these are the same
map
module('win32')
Pragma ('call(c_conv=> on)')
someWinAPIusingC(MatchingByteLengthDataType param1, MatchingByteLengthDataType param2),C
Pragma ('call(c_conv=> off)')
end
end
map
module('win32')
someWinAPIusingC(MatchingByteLengthDataType param2, MatchingByteLengthDataType param1)
end
end
I think its a few levels of progression up from a vintage tupperware shape sorter ball.
Hi ,
may it is a silly question but when I tried to compile the example in Clarion 9 I get these errors:
Thanks
You’ll have to add these types at the top. I think they were added in C11.
INT64 GROUP,TYPE
lo ULONG
hi LONG
END
UINT64 GROUP,TYPE
lo ULONG
hi ULONG
END
Sorry … feeling stupid today
got this error !
In C9 you must link win32ext.lib.
Which can do in code with:
PRAGMA('link(win32ext.lib)')
I put those PRAGMA up just above the MAP. I like that it moves with the code. Can OMIT('***', _C110_)
… or _c100_
Very good. thanks.
one last question, I tried to run it in clarion 6.3 and I got the same error “Unresolved External CopyFileExA in CopyFileExTest.obj” and I tried to link win32ext.lib but did not work.
thank you for your support.
Regards
The API doc shows it’s in shell32
So, using libmaker, create a .lib for this API
You can try adding the entire dll
But it’s likely that will lead to duplicates.
So delete everything you don’t need inside of libmaker before you save the .lib
Then link in the lib you created
With the shipping LibMaker it is a PITA to have to carefully delete all but 1 function. With the " @ArnorBld + Mark + my " LibMaker you can Search and Tag 1+ Functions then write just Tagged in the Lib.
A unique feature … You can Subtract the current Clarion Win32.Lib so just have left everything missing and will not have duplicate errors. This allows opening a new release ClaRun.dll then subtract a prior release to see new exports that may reveal new RTL features.
All set and working fine.
thank you all and best regards
the conclusion that after creating the new .lib, I compiled the project in Clarion 6.3 under windows 7 32 bit.
the application works fine under window 10 64 bit but crashes in window 7 32 bit
Hi,
Any suggestions to solve this issue; working on 64 bit but not 32 bit?
Regards
You’ve probably used the wrong copy of Shell32.DLL to create the .LIB file.
On a 64Bit system you must use the copy that’s in \Windows\SysWOW64 and not the copy that’s in \Windows\System32 (that, on a 64Bit system is 64Bit version)
Normally the 64Bit versions of DLLs won’t open with Clarion’s LibMaker, but in the case of Shell32 both 32Bit and 64Bit versions open.
Very cool! I’d forgotten I’d messed with that many years ago!
Thats a nice lib maker, the WinSxS search and load is a nice touch.
It is possible to work out the parameters and put them in the map automatically. Its a feature I have planned for my own apps which can make libs, which then makes searching for dll’s and producing libs a whole new game.
FYI.