I have a 16GB flash drive which I use to back up (verified) a laptop.
The file size is ~9GB. I want to find the file size from VB. So far ...
Debug.Print Format$(FileLen("h:\laptop031209.bkf"), "scientific")
1.34E+09
Wrong answer, next...
ff = FreeFile
Open "h:\laptop031209.bkf" For Binary As #ff
Debug.Print LOF(ff)
Crash, Error 52, bad file name or number
Next...
Dim indata As Long, ff As Long
ff = FreeFile
Open "h:\laptop031209.bkf" For Binary As #ff
For x = 1 To 5 Step 0.1
Get #ff, x * 10 ^ 9, indata
Debug.Print x, indata
Next x
Debug output...
1 -2063613314
1.1 -2146805854
1.2 3014915
1.3 1815045485
1.4 1733515305
1.5 386916337
1.6 984385904
1.7 1525640198
1.8 462482
1.9 3538995
2 50529027
2.1 -59401213
Run-time error '6', Overflow
Utterly lost, help appreciated TIA
Martin
Take a look at the GetFileSizeEx API function. Declare it as returning
a Currency variable, then multiply the result by 10000 to get the
correct size.
--
Jim Mack
Twisted tees at http://www.cafepress.com/2050inc
"We sew confusion"
> I have a 16GB flash drive which I use to back up (verified)
> a laptop. The file size is ~9GB. I want to find the file
> size from VB. So far ...
> Debug.Print Format$(FileLen("h:\laptop031209.bkf"), "scientific")
> 1.34E+09
> Wrong answer
That's because the VB FileLen function returns a Long. The largest
number that can be held in an unsigned Long is 4GB, although because
VB interprets the contents of a Long as a signed number and so it
interprets values between 2GB and 4GB as negative numbers. The actual
range of values that can be held in a long as far as VB is concerned
is from -2,147,483,648 to + 2,147,483,647. Therefore, the VB FileLen
function is good for files up to just under 2GB. For files between 2BG
and 4GB VB FileLen will return a negative value, and for files greater
than 4GB VB FileLen will return whatever value is left over after
effectively dividing the size by 4GB. So, for example, in the case of
a 9.8GB file VB FileLen will return 1.8GB, which is obviously wrong.
As another example, for 10.8GB VB FileLen would return 2.8GB, except
of course 2.8GB is more than the maximum positive value that can be
held in a VB Long (as already explained) and so it will be interpreted
by VB as a negative number, which again is obviously wrong.
Anyway, all that is just a preamble really so that you can see what is
going on, and why VB is returning about 1.25GB in your specific case
of a 9.25GB file. What you need to do to get the correct result is use
a function which uses something much larger than a Long, such as the
API GetFileSizeEx function, which returns a Large_Integer (which is a
64 bit number as opposed to a 32 bit Long). Unfortunatelty VB does not
have such a data type. It does have the Currency data type though,
which is a 64 bit number but which VB interprets as though it had a
fixed four decimal places. So, if we pass it a 32 bit VB Currency data
type the getFileSizeEx function will simply see it as a Large_Integer
(it has the same number of bits) and so it will dump the correct
return value into it, except that of course VB will interpret it as
though it had four decimal digits and so we need to multiply the value
returned by GetFileSize (in the Currency data type) by 10000 in order
to get our correct answer.
For example, if the GetFileSize function dumped the file size
123456789 into it then VB would treat the result as 12345.6789. We
then simply multiply the returned Currency value by 10000 and we will
have our correct answer of 123456789. There is still a limit on the
file size that can be properly handled by a Currency data type of
course, which is less than the file size that can be handled by the
GetFileSize API itself, because Currency is also a signed number as
far as VB is concerned, and also because we have to multiply the
result by 10000 in order to get rid of the decimal point, but that
limit is very large, about 858993GB, and it will be a few years yet
before we start seeing files that large! (And, of course, there are
ways to overcome that problem anyway). Here's an example of the
method.
Mike
Option Explicit
Private Declare Function CreateFile Lib "kernel32" _
Alias "CreateFileA" (ByVal lpFileName As String, _
ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, _
lpSecurityAttributes As Any, _
ByVal dwCreationDisposition As Long, _
ByVal dwFlagsAndAttributes As Long, _
ByVal hTemplateFile As Long) As Long
Private Declare Function GetFileSizeEx Lib "kernel32" _
(ByVal hFile As Long, lpFileSize As Currency) As Boolean
Private Declare Function CloseHandle Lib "kernel32" _
(ByVal hObject As Long) As Long
Private Const GENERIC_READ As Long = &H80000000
Private Const FILE_SHARE_READ As Long = &H1
Private Const OPEN_EXISTING As Long = 3
Private Const INVALID_HANDLE_VALUE As Long = -1
Private Function FileLength(s1 As String) As Currency
Dim hFile As Long, FileSize As Currency
hFile = CreateFile(s1, _
GENERIC_READ, FILE_SHARE_READ, ByVal 0&, _
OPEN_EXISTING, ByVal 0&, ByVal 0&)
If hFile <> INVALID_HANDLE_VALUE Then
GetFileSizeEx hFile, FileLength
CloseHandle hFile
FileLength = FileLength * 10000@
Else
FileLength = -1
End If
End Function
Private Sub Command1_Click()
Print FileLength("c:\temp\jessica1.jpg")
End Sub
I'm using it in a prog that's bit like Win Explorer, File Properties. I
guess I'll have to use that every time since I don't know what the file
size is going to be.
Thanks Mike, works perfectly (of course :-)
However I don't understand the use of :-
> hFile = CreateFile(s1, _
> GENERIC_READ, FILE_SHARE_READ, ByVal 0&, _
> OPEN_EXISTING, ByVal 0&, ByVal 0&)
I would appreciate a brief explanation. Not much gain in blindly copying
code. TIA
Martin
> Thanks Mike, works perfectly (of course :-)
> However I don't understand the use of :-
>
> > hFile = CreateFile(s1, _
> > GENERIC_READ, FILE_SHARE_READ, ByVal 0&, _
> > OPEN_EXISTING, ByVal 0&, ByVal 0&)
>
> I would appreciate a brief explanation. Not
> much gain in blindly copying code. TIA
Well, for a start the name of the API function itself is misleading
(CreateFile) because it is used either for creating new files or for
opening existing files. In fact CreateFile has evolved into a "jack of
all trades" because it can handle all sorts of I/O devices other than
straightforward files. As far as the parameters are concerned the
first parameter is of course the file path/name. The second one
(GENERIC_READ in the example) specifies the kind of access we want to
the file (read or write or both). The third one (SHARED_READ in the
example) specifies the kind of access other processes are allowed to
have to the same file whilst we have it open. Four our purposes (just
checking its size) we don't mind anything else reading it, but we
don't normally want anything to write to it whilst we have it open
(even though it is only for an extremely short time), at least not
until we have finished examining its length. The fifth one
(OPEN_EXISTING in the example) specifies that we want to open an
existing file rather than create a new one. Using this specific value
(OPEN_EXISTING) the CreateFile function will return a failure code if
the file does not already exist, which is exactly what we want since
we are attempting to read the size of an already existing file. The
remaining three parameters (all set to byVal zero in the example) are
for other things that are not important for the job we are doing
(reading the size of an existing file). You can read full details of
the CreateFile function at the following link:
http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx
Mike