From: RB Smissaert on
> You seemed to be using it as way to tell if a file exists.

No, I do use GetAttr for that, just as in the function you posted.
The idea was to try the simpler (and faster?) FileLen first before using the
API method.
In any case have this all sorted now and after a minor modification (see
comment in below code)
the API method is now about as fast as the FileLen method:

Public Function FileSizeAPI(strFilePath As String) As Variant

Dim hSearch As Long
Dim WFD As WIN32_FIND_DATA

On Error GoTo ERROROUT

hSearch = FindFirstFile(strFilePath, WFD)

If hSearch = -1 Then
FileSizeAPI = -1
Else
FindClose hSearch 'doing this before the next line makes it faster, not
sure why
FileSizeAPI = LargeInteger(WFD.nFileSizeLow, WFD.nFileSizeHigh)
End If

Exit Function

ERROROUT:
FileSizeAPI = -1

End Function


RBS


"Karl E. Peterson" <karl(a)exmvps.org> wrote in message
news:ORpeOexZKHA.740(a)TK2MSFTNGP04.phx.gbl...
> RB Smissaert wrote:
>> Thanks for looking at that and yes, should get a virtual machine really.
>>
>>> Why are you using that?
>> Great majority of files passed to the function will be small, so I
>> thought I
>> might save some time to try the FileLen first.
>
> Not the point. You seemed to be using it as way to tell if a file exists.
> The most commonly accepted method for that is to use GetAttr.
>
>>> I guess what you're asking is whether FileLen() will toss an error with
>>> a
>>> really huge file?
>> Exactly, as that would be no good.
>
> Nope, it definitely wouldn't.
>
>>> I'll try... Nope! Looks like it just wraps around and around...
>> OK, thanks and that is no good either, so will need to chuck FileLen out
>> then.
>> Maybe FileLen uses that API in any case, so trying FileLen first won't
>> gain
>> any time in that case.
>> Will do some timing tests to see if that might be the case.
>
> is this what you're looking for?
>
> Public Function FileExists(ByVal FileName As String) As Boolean
> Dim nAttr As Long
> ' Grab this files attributes, and make sure it isn't a folder.
> ' This test includes cases where file doesn't exist at all.
> On Error GoTo NoFile
> nAttr = GetAttr(FileName)
> If (nAttr And vbDirectory) <> vbDirectory Then
> FileExists = True
> End If
> NoFile:
> End Function
>
> --
> .NET: It's About Trust!
> http://vfred.mvps.org
>

From: Karl E. Peterson on
RB Smissaert wrote:
>> You seemed to be using it as way to tell if a file exists.
>
> No, I do use GetAttr for that, just as in the function you posted.
> The idea was to try the simpler (and faster?) FileLen first before using the
> API method.

Ah, I see. Sorry for the quick (mis)reading of the code, there.

> In any case have this all sorted now and after a minor modification (see
> comment in below code) the API method is now about as fast as the FileLen method:

Yeah, I would think. You'd probably see some gains using the W version, too, if
you're so inclined. How many files are you dealing with, that speed is a concern
here?


> Public Function FileSizeAPI(strFilePath As String) As Variant
>
> Dim hSearch As Long
> Dim WFD As WIN32_FIND_DATA
>
> On Error GoTo ERROROUT
>
> hSearch = FindFirstFile(strFilePath, WFD)
>
> If hSearch = -1 Then
> FileSizeAPI = -1
> Else
> FindClose hSearch 'doing this before the next line makes it faster, not
> sure why
> FileSizeAPI = LargeInteger(WFD.nFileSizeLow, WFD.nFileSizeHigh)
> End If
>
> Exit Function
>
> ERROROUT:
> FileSizeAPI = -1
>
> End Function

Another minor, perhaps even negligble, optimization would be to stop comparing a
Long with an Integer in your If test.

Hmmm, also, what's with the error trapping? If speed is an issue, get rid of that,
too. :-)
--
..NET: It's About Trust!
http://vfred.mvps.org



From: RB Smissaert on
Usually this will be used just to get the size of one file, so speed is not
an issue, but
I also use it in a loop to read many files, so the faster the better.
This is the optimized version then using your suggestions and it is some 15%
faster than
the posted code:

Private Declare Sub CopyMemory _
Lib "kernel32" _
Alias "RtlMoveMemory" (Dest As Any, _
Src As Any, _
ByVal cBytes As Long)

Private Declare Function FindFirstFile Lib "kernel32" _
Alias "FindFirstFileW" _
(ByVal lpFileName As Long, _
lpFindFileData As WIN32_FIND_DATA) As Long

Private Declare Function FindClose Lib "kernel32" _
(ByVal hFindFile As Long) As Long

Private Const MAX_PATH = 260

Private Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type

Private Type WIN32_FIND_DATA
dwFileAttributes As Long
ftCreationTime As FILETIME
ftLastAccessTime As FILETIME
ftLastWriteTime As FILETIME
nFileSizeHigh As Long
nFileSizeLow As Long
dwReserved0 As Long
dwReserved1 As Long
cFileName(0 To (MAX_PATH * 2) - 1) As Byte
cAlternate(0 To 27) As Byte
End Type

Private Function FileSizeAPI(strFilePath As String) As Variant

Dim hSearch As Long
Dim WFD As WIN32_FIND_DATA

hSearch = FindFirstFile(StrPtr(strFilePath), WFD)

If hSearch = -1& Then
FileSizeAPI = -1&
Else
FindClose hSearch
FileSizeAPI = LargeInteger(WFD.nFileSizeLow, WFD.nFileSizeHigh)
End If

End Function

Private Function LargeInteger(ByVal LoPart As Long, _
ByVal HiPart As Long) As Variant

Dim nResult As Currency

'Copy the parts, appropriately.
CopyMemory ByVal VarPtr(nResult), LoPart, 4
CopyMemory ByVal VarPtr(nResult) + 4, HiPart, 4

'Return the result as VarType Decimal(14).
LargeInteger = nResult * CDec(10000)

End Function


Just one thing I don't get and that is that I thought I could do this
(modification suggested by Peter Thornton)
to gain some speed:

If WFD.nFileSizeHigh Then
FileSizeAPI = LargeInteger(WFD.nFileSizeLow, WFD.nFileSizeHigh)
Else
FileSizeAPI = WFD.nFileSizeLow
End If

But that fails now on large files, giving negative figures due to
WFD.nFileSizeHigh = 0
Any idea what I might be doing wrong there?


RBS



"Karl E. Peterson" <karl(a)exmvps.org> wrote in message
news:OFM3mW7ZKHA.4688(a)TK2MSFTNGP06.phx.gbl...
> RB Smissaert wrote:
>>> You seemed to be using it as way to tell if a file exists.
>>
>> No, I do use GetAttr for that, just as in the function you posted.
>> The idea was to try the simpler (and faster?) FileLen first before using
>> the
>> API method.
>
> Ah, I see. Sorry for the quick (mis)reading of the code, there.
>
>> In any case have this all sorted now and after a minor modification (see
>> comment in below code) the API method is now about as fast as the FileLen
>> method:
>
> Yeah, I would think. You'd probably see some gains using the W version,
> too, if you're so inclined. How many files are you dealing with, that
> speed is a concern here?
>
>
>> Public Function FileSizeAPI(strFilePath As String) As Variant
>>
>> Dim hSearch As Long
>> Dim WFD As WIN32_FIND_DATA
>>
>> On Error GoTo ERROROUT
>>
>> hSearch = FindFirstFile(strFilePath, WFD)
>>
>> If hSearch = -1 Then
>> FileSizeAPI = -1
>> Else
>> FindClose hSearch 'doing this before the next line makes it faster,
>> not
>> sure why
>> FileSizeAPI = LargeInteger(WFD.nFileSizeLow, WFD.nFileSizeHigh)
>> End If
>>
>> Exit Function
>>
>> ERROROUT:
>> FileSizeAPI = -1
>>
>> End Function
>
> Another minor, perhaps even negligble, optimization would be to stop
> comparing a Long with an Integer in your If test.
>
> Hmmm, also, what's with the error trapping? If speed is an issue, get rid
> of that, too. :-)
> --
> .NET: It's About Trust!
> http://vfred.mvps.org
>
>
>

From: Karl E. Peterson on
RB Smissaert wrote:
> Usually this will be used just to get the size of one file, so speed is not
> an issue, but
> I also use it in a loop to read many files, so the faster the better.
> This is the optimized version then using your suggestions and it is some 15%
> faster than the posted code:

Nice!

>
> Private Declare Sub CopyMemory _
> Lib "kernel32" _
> Alias "RtlMoveMemory" (Dest As Any, _
> Src As Any, _
> ByVal cBytes As Long)
>
> Private Declare Function FindFirstFile Lib "kernel32" _
> Alias "FindFirstFileW" _
> (ByVal lpFileName As Long, _
> lpFindFileData As WIN32_FIND_DATA) As Long
>
> Private Declare Function FindClose Lib "kernel32" _
> (ByVal hFindFile As Long) As Long
>
> Private Const MAX_PATH = 260
>
> Private Type FILETIME
> dwLowDateTime As Long
> dwHighDateTime As Long
> End Type
>
> Private Type WIN32_FIND_DATA
> dwFileAttributes As Long
> ftCreationTime As FILETIME
> ftLastAccessTime As FILETIME
> ftLastWriteTime As FILETIME
> nFileSizeHigh As Long
> nFileSizeLow As Long
> dwReserved0 As Long
> dwReserved1 As Long
> cFileName(0 To (MAX_PATH * 2) - 1) As Byte
> cAlternate(0 To 27) As Byte
> End Type
>
> Private Function FileSizeAPI(strFilePath As String) As Variant
>
> Dim hSearch As Long
> Dim WFD As WIN32_FIND_DATA
>
> hSearch = FindFirstFile(StrPtr(strFilePath), WFD)
>
> If hSearch = -1& Then
> FileSizeAPI = -1&
> Else
> FindClose hSearch
> FileSizeAPI = LargeInteger(WFD.nFileSizeLow, WFD.nFileSizeHigh)
> End If
>
> End Function
>
> Private Function LargeInteger(ByVal LoPart As Long, _
> ByVal HiPart As Long) As Variant
>
> Dim nResult As Currency
>
> 'Copy the parts, appropriately.
> CopyMemory ByVal VarPtr(nResult), LoPart, 4
> CopyMemory ByVal VarPtr(nResult) + 4, HiPart, 4
>
> 'Return the result as VarType Decimal(14).
> LargeInteger = nResult * CDec(10000)
>
> End Function
>
>
> Just one thing I don't get and that is that I thought I could do this
> (modification suggested by Peter Thornton)
> to gain some speed:
>
> If WFD.nFileSizeHigh Then
> FileSizeAPI = LargeInteger(WFD.nFileSizeLow, WFD.nFileSizeHigh)
> Else
> FileSizeAPI = WFD.nFileSizeLow
> End If
>
> But that fails now on large files, giving negative figures due to
> WFD.nFileSizeHigh = 0
> Any idea what I might be doing wrong there?

You haven't accounted for the sign bit. You may be able to optimize it, but you'd
need to add 2^32 if it's <0. In the end, the extra tests and math may not pay off.
One possible compromise might be:

If (WFD.nFileSizeHigh = 0) And (WFD.nFileSizeLow > 0) Then
FileSizeAPI = WFD.nFileSizeLow
Else
FileSizeAPI = LargeInteger(WFD.nFileSizeLow, WFD.nFileSizeHigh)
End If

--
..NET: It's About Trust!
http://vfred.mvps.org


From: RB Smissaert on
One possible compromise might be:

If (WFD.nFileSizeHigh = 0) And (WFD.nFileSizeLow > 0) Then
FileSizeAPI = WFD.nFileSizeLow
Else
FileSizeAPI = LargeInteger(WFD.nFileSizeLow, WFD.nFileSizeHigh)
End If

Yes, that does knock a few percent off.
Actually, let's make that:
If (WFD.nFileSizeHigh = 0&) And (WFD.nFileSizeLow > 0&) Then

Looks we may have the perfect replacement then for FileLen ...


RBS




"Karl E. Peterson" <karl(a)exmvps.org> wrote in message
news:%23y%23xVy8ZKHA.2160(a)TK2MSFTNGP02.phx.gbl...
> RB Smissaert wrote:
>> Usually this will be used just to get the size of one file, so speed is
>> not
>> an issue, but
>> I also use it in a loop to read many files, so the faster the better.
>> This is the optimized version then using your suggestions and it is some
>> 15%
>> faster than the posted code:
>
> Nice!
>
>>
>> Private Declare Sub CopyMemory _
>> Lib "kernel32" _
>> Alias "RtlMoveMemory" (Dest As Any, _
>> Src As Any, _
>> ByVal cBytes As Long)
>>
>> Private Declare Function FindFirstFile Lib "kernel32" _
>> Alias "FindFirstFileW" _
>> (ByVal lpFileName As Long, _
>> lpFindFileData As WIN32_FIND_DATA) As Long
>>
>> Private Declare Function FindClose Lib "kernel32" _
>> (ByVal hFindFile As Long) As Long
>>
>> Private Const MAX_PATH = 260
>>
>> Private Type FILETIME
>> dwLowDateTime As Long
>> dwHighDateTime As Long
>> End Type
>>
>> Private Type WIN32_FIND_DATA
>> dwFileAttributes As Long
>> ftCreationTime As FILETIME
>> ftLastAccessTime As FILETIME
>> ftLastWriteTime As FILETIME
>> nFileSizeHigh As Long
>> nFileSizeLow As Long
>> dwReserved0 As Long
>> dwReserved1 As Long
>> cFileName(0 To (MAX_PATH * 2) - 1) As Byte
>> cAlternate(0 To 27) As Byte
>> End Type
>>
>> Private Function FileSizeAPI(strFilePath As String) As Variant
>>
>> Dim hSearch As Long
>> Dim WFD As WIN32_FIND_DATA
>>
>> hSearch = FindFirstFile(StrPtr(strFilePath), WFD)
>>
>> If hSearch = -1& Then
>> FileSizeAPI = -1&
>> Else
>> FindClose hSearch
>> FileSizeAPI = LargeInteger(WFD.nFileSizeLow, WFD.nFileSizeHigh)
>> End If
>>
>> End Function
>>
>> Private Function LargeInteger(ByVal LoPart As Long, _
>> ByVal HiPart As Long) As Variant
>>
>> Dim nResult As Currency
>>
>> 'Copy the parts, appropriately.
>> CopyMemory ByVal VarPtr(nResult), LoPart, 4
>> CopyMemory ByVal VarPtr(nResult) + 4, HiPart, 4
>>
>> 'Return the result as VarType Decimal(14).
>> LargeInteger = nResult * CDec(10000)
>>
>> End Function
>>
>>
>> Just one thing I don't get and that is that I thought I could do this
>> (modification suggested by Peter Thornton)
>> to gain some speed:
>>
>> If WFD.nFileSizeHigh Then
>> FileSizeAPI = LargeInteger(WFD.nFileSizeLow, WFD.nFileSizeHigh)
>> Else
>> FileSizeAPI = WFD.nFileSizeLow
>> End If
>>
>> But that fails now on large files, giving negative figures due to
>> WFD.nFileSizeHigh = 0
>> Any idea what I might be doing wrong there?
>
> You haven't accounted for the sign bit. You may be able to optimize it,
> but you'd need to add 2^32 if it's <0. In the end, the extra tests and
> math may not pay off. One possible compromise might be:
>
> If (WFD.nFileSizeHigh = 0) And (WFD.nFileSizeLow > 0) Then
> FileSizeAPI = WFD.nFileSizeLow
> Else
> FileSizeAPI = LargeInteger(WFD.nFileSizeLow, WFD.nFileSizeHigh)
> End If
>
> --
> .NET: It's About Trust!
> http://vfred.mvps.org
>