FATTR32 - File manipulation for Microsoft Access 7.0
====================================================

This is a small library of file functions that you can call from Microsoft Access
to perform a number of file manipulation tasks.

The 16-bit version of this (for Microsoft Access 1.X/2.0) was a callable DLL,
but that is no longer necessary due to the enhanced set of file functions in VBA
and the powerful Win32 API.

Here are the functions that come in this library.  If you have used the 16-bit DLL
in the past, take note of the functions with an asterisk next to them -- they have
changed in how you call them:

GetFileSize() - gets the size of a specified file
*GetFileDateTime() - gets the timestamp of a specified file
GetDiskFree() - returns the amount of free disk space on a specifie drive
*DosFindFirst() - returns the first file matching a specification
*DosFindNext() - returns the next file from the original DosFindFirst()
ConvertDosTime() - converts the date/time information from DOS time to
		   Access time format
GetFileAttr() - Retrieve a specific file's DOS attribute (one or more of the 
		ATTR_*, constants listed below, added together
SetFileAttr() - Set a specific file's DOS attribute, using the sum of one
		or more of the ATTR_* constants.
*SetFileDateTime() - Set the file date/time for any DOS file.


To use
------

From Microsoft Access version 7.0, open up your database, open any module, and
select Insert | File from the menu system.  Choose FATTR.BAS from the dialog to
read it in.

The file FATTR.BAS contains the following constants, types, and declarations:

'Note that the vb file attribute constants are the same
Public Const ATTR_NORMAL = 0
Public Const ATTR_READONLY = 1
Public Const ATTR_HIDDEN = 2
Public Const ATTR_SYSTEM = 4
Public Const ATTR_LABEL = 8
Public Const ATTR_SUBDIR = 16
Public Const ATTR_ARCHIVE = 32

Type FILETIME
    dwLowDateTime As Long
    dwHighDateTime As Long
End Type
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 As String * 260
    cAlternateFileName As String * 14
End Type
Type SYSTEMTIME
    wYear As Integer
    wMonth As Integer
    wDayOfWeek As Integer
    wDay As Integer
    wHour As Integer
    wMinute As Integer
    wSecond As Integer
    wMilliseconds As Integer
End Type

Declare Function apiGetDiskFreeSpace& Lib "Kernel32" Alias "GetDiskFreeSpaceA" _
  (ByVal lpRootPathName As String, lpSectorsPerCluster As Long, _
   lpBytesPerSector As Long, lpNumberOfFreeClusters As Long, _
   lpTotalNumberOfClusters As Long)
Declare Function apiFindFirstFile& Lib "Kernel32" Alias "FindFirstFileA" _
  (ByVal lpFileName As String, lpFindFileData As WIN32_FIND_DATA)
Declare Function apiFindNextFile& Lib "Kernel32" Alias "FindNextFileA" _
  (ByVal handle As Long, lpFindFileData As WIN32_FIND_DATA)
Declare Function apiFindClose& Lib "Kernel32" Alias "FindClose" (ByVal handle As Long)
Declare Function apiSetFileTime& Lib "Kernel32" Alias "SetFileTime" _
  (ByVal hFile As Long, lpCreationTime As FILETIME, lpLastAccessTime As FILETIME, _
   lpLastWriteTime As FILETIME)
Declare Function apiSystemTimeToFileTime& Lib "Kernel32" Alias "SystemTimeToFileTime" _
  (lpSystemTime As SYSTEMTIME, lpFileTime As FILETIME)
Declare Function apiFileTimeToSystemTime& Lib "Kernel32" Alias "FileTimeToSystemTime" _
  (lpFileTime As FILETIME, lpSystemTime As SYSTEMTIME)
Declare Function apiCreateFile& Lib "Kernel32" Alias "CreateFileA" _
  (ByVal strFileName As String, ByVal dwDesiredAccess As Long, _
   ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, _
   ByVal dwCreationDistribution As Long, ByVal dwFlagsAndAttributes As Long, _
   ByVal hTemplateFile As Long)
Declare Function apiCloseHandle& Lib "Kernel32" Alias "CloseHandle" (ByVal handle As Long)


Reference to Functions
======================

GetFileAttr()
-------------

Syntax    GetFileAttr(strFileName)

	  strFileName is the name of a file to examine.

Returns   an integer representing the file's attribute.
	  -2 if the file wasn't found.
	  -5 if permission was denied.

Notes	  This uses the VBA intrinsic function GetAttr().

	  Use the ATTR_* attribute constants when checking 
	  and setting file attributes.
	  To see if a file is hidden:

	  intAttr = GetFileAttr("someFileName")
	  isHidden = intAttr AND ATTR_HIDDEN

SetFileAttr()
-------------

Syntax    SetFileAttr(strFileName, intAttrib)

	  strFileName is the name of a file for which to set the attribute.
	  intAttrib is the attribute to set for the file

Returns   The original file attribute.
	  -2 if the file wasn't found.
	  -5 if permission was denied.

Notes	  This uses the VBA intrinsic function SetAttr().

	  To set a file to be hidden:
 
	  intAttr = GetFileAttr("someFileName")
	  intAttr = intAttr OR ATTR_HIDDEN
	  intAttr = SetFileAttr("someFileName", intAttr)
 
	  or

	  intAttr = SetFileAttr("someFileName", GetFileAttr("someFileName") OR ATTR_HIDDEN)

GetFileSize()
-------------

Syntax    GetFileSize(strFileName)

	  strFileName is the name of a file to examine.

Returns   a long integer representing the size of the file.

Notes	  This uses the VBA intrinsic function FileLen().

          A full path may be necessary.  The function only looks for files
	  with normal attributes.  Hidden or system files will not be
	  searched for with this function.

GetDiskFree()
-------------

Syntax    GetDiskFree(intDrive)

	  intDrive is a number, where 1=A, 2=B, 3=C, etc.

Returns   a long integer representing the amount of disk space available on
	  the designated drive.  Returns -1 if intDrive is invalid.

Notes     Huge drives may not be representable with a long integer, since longs
	  are signed under Access.  You can also specify intDrive = 0, which
          means the default drive.

GetFileDateTime()
-----------------

Syntax    GetFileDateTime(strFileName)

	  strFileName is the name of a file to examine.

Returns   a variant containing an Access date. -1 on error.

Notes	  This uses the VBA intrinsic function FileDateTime().
	  A full path may be necessary.

Changes	  The 16-bit version returned a double, which was the internal
          representation of a date.  This version uses the VBA function
          which can return Date datatypes.

SetFileDateTime()
-----------------

Syntax	SetFileDateTime(sFileName, intYear, intMonth, intDay, intHour, intMinute, intSecond)

Returns	True/False, depending on its success

Notes	Don't call this function directly.  You can call the function SetFileDate()
        (also included) which accepts a file name and an Access date/time value and
        parses it and calls this function.  It's much easier.  For example:

	intSuccess = SetFileDate(strFileName, varDate)

	You CAN call SetFileDateTime() directly, but it's a bit more work.

DosFindFirst()
--------------

Syntax    DosFindFirst(strFileSpec, ffStruct)

	  sFileSpec is a valid DOS filespec, possibly including wildcards.
	  ffStruct is a variable of type WIN32_FIND_DATA.

Returns   -1 on error
          a find "handle" (a Long integer) that you use in subsequent searches
          with the DosFindNext() function.

Notes	  You could use the built-in Dir() function in VBA, which does basically
          the same thing as this function and takes file attributes when searching.
          The only problem with Dir() is that it doesn't support recursion, so
          traversing directories recursively can be difficult.

	  ffStruct is filled in with the first file that meets the search
	  specification.  To retrieve the next file, use DosFindNext().

	  ffStruct contains the following elements:

	    cFileName - an ASCII 0 terminated filename
	    dwFileAttributes - a character attribute
	    ftCreationTime - the time the file was created
            ftLastAccessTime - the time the file was last accessed
            ftLastWriteTime - the time the file was last written to
	    nFileSizeHigh - the file size high 32-bits
	    nFileSizeLow - the file size low 32-bits

	  To convert any of the dates into something readable, you should call
          ConvertDosTime() on any of the date/time elements.

	  The attrib element will contain any file attributes OR'd together.
	  To extract a particular attribute, AND it with the attribute you
	  are querying.  For example, to determine if a file found is a
	  subdirectory, you can say something like

	    If (ffStruct.dwFileAttributes AND ATTR_SUBDIR) Then
	      MsgBox "It is a subdirectory!"
	    End If

	  The name element is zero terminated, which means you will need to
	  remove the ASCII 0 from the string.  The following code shows how
	  to do that:

	    If InStr(ffStruct.cFileName, Chr(0)) > 0) Then
	       MyVar = Left(ffStruct.cFileName, InStr(ffStruct.cFileName, Chr(0)) - 1)
	    End If

          Typically, you only need the nFileSizeLow to get the size of a file, as most
          file sizes fit within 32-bits (1.4 gigs).

Changes   This function is quite a bit different than the previous version.  You may need
          to convert quite a bit of your code to work with it.

          The most important difference is that this function no longer takes an attribute
          to search for.  This is a change in the Win32 API.  You'll need to manually 
          compare file attributes.  Also the return value is now a Long that you must use
          in calls to DosFindNext(), the WIN32_FIND_DATA structure is quite different.  And
          the return value is different too.

DosFindNext()
-------------

Syntax    DosFindNext(handle, ffStruct)

	  handle is the handle returned from the call to DosFindFirst().
          ffStruct is a variable of type WIN32_FIND_DATA.  It should have been
	  initialized by calling DosFindFirst() first.

Returns   the handle if successful, -1 if no more find found or error

Notes     DosFindNext() goes hand-in-hand with DosFindFirst().  A common way
	  to traverse a directory is as follows:

	    Dim handle, ffStruct As WIN32_FIND_DATA
	    handle = DosFindFirst("*.*", ffStruct)
	    Do While handle <> -1
	      handle = DosFindNext(handle, ffStruct)
	    Loop

Changes	   Like DosFindFirst(), this has changed significantly.  Unlike the 16-bit
           version you must pass a handle returned from DosFindFirst() to this function.
           The return value is different too, but mainly to make it easier to write
           a loop with DosFindFirst() and DosFindNext().

ConvertDosTime()
----------------

Syntax    ConvertDosTime(ftime)

	  ftime is a variable of type FILETIME.  The WIN32_FIND_DATA structure
          filled by DosFindFirst() or DosFindNext() stores dates in this format.

Returns   an Access date/time datatype.

Notes     ConvertDosTime uses an API call to convert FILETIME into a more readable
          format and then the CDate() VBA function to convert it to a date.

Changes   ConvertDosTime used to take a find file structure as an argument.  Now that
          the new WIN32_FIND_DATA structure actually has three dates in it (Creation,
          Last Modified, Last Write), you may want to look at any of these elements
          individually.

Examples
=========

The following examples are included in FATTR.BAS.  The first example is a "whereis" 
program written in Access Basic that uses the FATTR functions.  To use it, go to 
the Access 7.0 Debug Window and type the following:

  ? whereis("C:\WINDOWS\", "*.DLL")

The program will then dump all files matching the file specification to the
Debug Window.  whereis() takes two arguments:  the first argument is the
directory to start from.  It must be a valid directory and MUST have a
backslash as the last character.  The second argument is the filespec to
search for.  It can contain the DOS wildcard characters "*" and "?".

The above example finds all DLLs in the WINDOWS directory all directories
underneath.

Function WhereIs(strDirSpec As String, strSpec As String)
    Dim ff As WIN32_FIND_DATA
    Dim lngHandle As Long
    Dim varRetval As Variant
    Dim varTemp As Variant

    'look for subdirectories first
    lngHandle = DosFindFirst(strDirSpec & "*.*", ff)
    Do While (lngHandle <> -1)
        If (ff.dwFileAttributes And ATTR_SUBDIR) And (Left(ff.cFileName, 1) <> ".") Then
            'clean ff.cFileName
            If (InStr(ff.cFileName, Chr(0)) > 0) Then
                varTemp = Left(ff.cFileName, InStr(ff.cFileName, Chr(0)) - 1)
            Else
                varTemp = ff.cFileName
            End If
            varRetval = WhereIs(strDirSpec & varTemp & "\", strSpec)
        End If
        lngHandle = DosFindNext(lngHandle, ff)
    Loop

    'look specifically for a file type
    Debug.Print "Searching " & strDirSpec & strSpec
    lngHandle = DosFindFirst(strDirSpec & strSpec, ff)
    Do While (lngHandle <> -1)
        If Not (ff.dwFileAttributes And ATTR_SUBDIR) Then
            If (InStr(ff.cFileName, Chr(0)) > 0) Then
                varTemp = Left(ff.cFileName, InStr(ff.cFileName, Chr(0)) - 1)
            Else
                varTemp = ff.cFileName
            End If
            Debug.Print varTemp, ConvertDosTime(ff.ftLastWriteTime)
        End If
        lngHandle = DosFindNext(lngHandle, ff)
    Loop
    'allow other things to happen
    DoEvents
End Function

This example uses the Dir() function to list all the files in the C:\ directory,
along with their file attributes.

Function TestAttrib()
    ' Print out a list of files in your root
    ' directory, along with the file attribute of each.

    Dim strName As String

    ' Tell Dir we want to see ALL files
    strName = Dir("C:\", vbNormal + vbHidden + vbSystem + vbVolume + vbDirectory)
    Do While strName <> ""
        Debug.Print strName, GetFileAttr("C:\" & strName)
        strName = Dir
    Loop
End Function


Legal stuff
===========

Please note that this sample is totally unsupported, and the software is
PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND and in no event shall MS be
liable for any damages whatsoever in connection with your use of the
sample.

However, I'm willing to answer questions about sample and programming DLLs
for Access in general.

Please feel free to copy this and use it, sell it, whatever.

Roger Harui [MSFT], 71742,1177
Ken Getz 76137,3650

