Sign in to follow this  
Followers 0
JerryD

GetFileSize function in Kernel32.dll

10 posts in this topic

In a script, I'm running WinZip command line to zip up some files. What I wanted to do was use RUN and then while the process ran use FileGetSize() to get the (temporary) zip file's size so I could display the progress. However, even though the file size regularly updates if I view the file in Windows Explorer, FileGetSize() reports the files size as zero.

I thought I'd try using DLLCall and use the GetFileSize function in Kernel32.dll, but haven't been able to get it to work.

At MSDN, it defines GetFileSize as follows:

DWORD GetFileSize(
  HANDLE hFile,
  LPDWORD lpFileSizeHigh
);

Return Values:

If the function succeeds, the return value is the low-order doubleword of the file size, and, if lpFileSizeHigh is non-NULL, the function puts the high-order doubleword of the file size into the variable pointed to by that parameter.

If the function fails and lpFileSizeHigh is NULL, the return value is INVALID_FILE_SIZE. To get extended error information, call GetLastError. When lpFileSizeHigh is NULL, the results returned for large files are ambiguous, and you will not be able to determine the actual size of the file. It is recommended that you use GetFileSizeEx instead.

I tried translating this into AutoIt as follows:

$sFile = 'C:\psinfo.txt'
$iSize=0
$Ret = DllCall ( 'Kernel32.dll', 'long', 'GetFileSize', 'int_ptr', $sFile, 'long_ptr', $iSize )

Although I'm not getting any error, I'm not getting any data either! I've tried a lot of different variable types for the different values with no success.

Specific question - is there anyplace that has VBCode variable type to DLLCall variable type translation? For example, what should I be using for the 'HANDLE' type? I assume 'LPDWORD' would translate to 'long_ptr' in an AutoIt DLLCall.

Can anyone help? Thanks in advance.

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

Hmm I gues the problem is that you need a filehandle not the filename.

Maybe using the fileopen thing in autoit to get a filehandle might work as a filehandle. That only works if autoit uses externel handles for files in scripts.

Maybe this helps you further

I tested it and no difference.

But

lpFileSize

[out] Pointer to a LARGE_INTEGER structure that receives the file size.

That is a pointer

I think you need to use dllstruct to get the data I'll check how that works(never tried it)

Tried this but doesn't change the 345 I set. But its more the way it should be using the struct

$str        = "int64"
$a        = DllStructCreate($str)

;$sFile = FileOpen ( "debug.log", 0)
$sFile = "debug.log"
$iSize=0
DllStructSetData ( $a, 1, 345) 
$Ret = DllCall ( 'Kernel32.dll', 'long', 'GetFileSize', 'str', $sFile, 'long_ptr', DllStructGetPtr ( $a ))

msgbox(0,"",DllStructGetData ( $a, 1 ))

Maybe Its not the right handle

try looking for CreateFile in the msdn and create a struct etc for that to get a windows filehandle

Edited by MrSpacely

Share this post


Link to post
Share on other sites

Hmm I gues the problem is that you need a filehandle not the filename.

Maybe using the fileopen thing in autoit to get a filehandle might work as a filehandle. That only works if autoit uses externel handles for files in scripts.

My understanding is that AutoIt provides the scripter with pseudo-handles when they manipulate files. The actual Windows handles are kept hidden away.

The v3.1.1 source uses a method that claims to work with 'in-use files'; it doesn't take the same approach that you are taking though.

Share this post


Link to post
Share on other sites

My understanding is that AutoIt provides the scripter with pseudo-handles when they manipulate files. The actual Windows handles are kept hidden away.

The v3.1.1 source uses a method that claims to work with 'in-use files'; it doesn't take the same approach that you are taking though.

hmm

I noticed I tried using the kernel32 to open a file and getting the handle but there is no working way to put that handle in variable and sending it to the next dllcall:S

Is there a way to add HANDLE in the possible varibles or dllcall param types?

hwnd is allready in there

Share this post


Link to post
Share on other sites

Question: Are you seeing the actual temporary file that the ZIP program creates during the zipping process when you check the filesize? Isn't the filesize updated in the FAT/MFT table when the file is closed?

Share this post


Link to post
Share on other sites

Question: Are you seeing the actual temporary file that the ZIP program creates during the zipping process when you check the filesize? Isn't the filesize updated in the FAT/MFT table when the file is closed?

See the original post:

However, even though the file size regularly updates if I view the file in Windows Explorer, FileGetSize() reports the files size as zero.

So apparently not.

Share this post


Link to post
Share on other sites

Hmm I gues the problem is that you need a filehandle not the filename.

Maybe using the fileopen thing in autoit to get a filehandle might work as a filehandle. That only works if autoit uses externel handles for files in scripts.

Maybe this helps you further

I tested it and no difference.

But

lpFileSize

[out] Pointer to a LARGE_INTEGER structure that receives the file size.

That is a pointer

I think you need to use dllstruct to get the data I'll check how that works(never tried it)

Tried this but doesn't change the 345 I set. But its more the way it should be using the struct

$str        = "int64"
$a        = DllStructCreate($str)

;$sFile = FileOpen ( "debug.log", 0)
$sFile = "debug.log"
$iSize=0
DllStructSetData ( $a, 1, 345) 
$Ret = DllCall ( 'Kernel32.dll', 'long', 'GetFileSize', 'str', $sFile, 'long_ptr', DllStructGetPtr ( $a ))

msgbox(0,"",DllStructGetData ( $a, 1 ))

Maybe Its not the right handle

try looking for CreateFile in the msdn and create a struct etc for that to get a windows filehandle

No matter what file name I use for $sFile, $Ret returns nothing, and DllStructGetData ( $a, 1 ) returns 340.

Share this post


Link to post
Share on other sites

No matter what file name I use for $sFile, $Ret returns nothing, and DllStructGetData ( $a, 1 ) returns 340.

I noticed it doesn't work.

But ret should only return something in a array. os $ret would only show the size in $Ret[2] if it did works the problem is the file handle the internal filehandle of autoit is a pseudo file handle not a windows file handle so its not possible yet in my mind because you cannot transfer the windows filehandle in a autoit variable at least I think not

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

Well I took a different approach, and after plowing though documentation at MSDN, found a way to use a shell.application COM object. Here's what I came up with:

#include-once
;===============================================================================
;
; Function Name:    _GetFileSize ( $sFileFullPath )
; Description:    Rerturns file size of file even if it's open and/or changing
; Parameter(s):  $sFileFullPath - String: Full path to file
; Requirement(s):   AutoIt 3.1.1.97+ (Requires COM object capability)
; Return Value(s):  On Success - Returns the size of the file specified
;                  On Failure Returns:
;                       -1 - Couldn't create Shell object (don't know why this would happen)
;                       -2 - Couldn't get Folder object (typically because the directory doesn't exist)
;                       -3 - Couldn't get File object (typically because the file doesn't exist)
; Notes:            $oShellObj.NameSpace($sDrive & $sPath) - doesn't appear to care if there
;                       is a trailing backslash after the path name
;                   Function can find hidden and system files.  Untested if it can
;                       actually return their changing size though, but does return
;                       the size of pagefile.sys for instance.
; Author(s):        JerryD
;
;===============================================================================
#include <File.au3>
Func _GetFileSize ( $sFileFullPath )
    Local $sDrive, $sPath, $sName, $sExt    ; for call to _PathSplit
    Local $oShellObj, $oFolderObj, $oFolderItem; all Objects used in this function
    Local $RetErr_NoShellObj   = -1         ; don't know what would cause this, but...
    Local $RetErr_NoFolderObj  = -2         ; typically directory does not exist
    Local $RetErr_NoItemObj = -3            ; typically file does not exist
    
    _PathSplit ( $sFileFullPath, $sDrive, $sPath, $sName, $sExt ); This requires #include <File.au3>
    
    $oShellObj  = ObjCreate ( 'shell.application' )
    If NOT IsObj ( $oShellObj ) Then Return $RetErr_NoShellObj
    
    $oFolderObj = $oShellObj.NameSpace($sDrive & $sPath)
    If NOT IsObj ( $oFolderObj ) Then Return $RetErr_NoFolderObj
    
    $oFolderItem = $oFolderObj.ParseName ( $sName & $sExt )
    If NOT IsObj ( $oFolderItem ) Then Return $RetErr_NoItemObj
    
    Return $oFolderItem.Size
EndFunc; _GetFileSize()
I'm using it in the script where I'm zipping up a gig of data, and instead of using RunWait in the script, I now use Run and then While ProcessExists ( 'wzzip.exe' ) and call the function within the While Loop to update a Progress bar and text. It's working exactly the way I need it to, and seems to work pretty quickly. Little or no degradation in the scripts performance in spite of the function being called every 1/4 second.

Thanks to all for your help.

UDF: http://www.autoitscript.com/fileman/users/public/JerryD/_GetFileSize.au3

Edited by JerryD

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0