KnowledgeBase Archive

An Archive of Early Microsoft KnowledgeBase Articles

View on GitHub

Q188977: HOWTO: Use the Win32 API to Access File Dates and Times

Article: Q188977
Product(s): Microsoft FoxPro
Version(s): WINDOWS:3.0,3.0b,5.0,5.0a
Operating System(s): 
Keyword(s): 
Last Modified: 30-JUL-1999

-------------------------------------------------------------------------------
The information in this article applies to:

- Microsoft Visual FoxPro for Windows, versions 3.0, 3.0b, 5.0, 5.0a 
-------------------------------------------------------------------------------

SUMMARY
=======

Visual FoxPro's FDATE() and FTIME() functions can be used to determine the date
and time that a file was last written. However, the Win32 file system also
stores the creation date and time in addition to the last access date and time.
To find these dates and times, it is necessary to use the Win32 functions
GetFileTime(), FileTimeToLocalFileTime() and FileTimeToSystemTime().

GetFileTime accepts a handle to an open file, and returns FILETIME structures for
the creation time, last access time and last write time. The FILETIME structure
is somewhat difficult to work with, because it contains two 32-bit values
representing the number of 100 nanosecond intervals since January 1, 1601.
Therefore, we want to convert it to a SYSTEMTIME structure. However, Win32
represents all times internally as Greenwich times, so you first apply a local
timezone correction by calling FileTimeToLocalFileTime().

Files written by or used by 16-bit Windows or DOS applications may not store all
these date and time values.

The FoxPro function GetFTime() illustrates the use of these calls.

MORE INFORMATION
================

1. Create and save the following program:

  

        *********************************************************
        * Function getftime
        *
        * Return the create date and time, last access date and time,
        *   last write date and time from a passed filename.
        *
        * Passed:
        *   lcFileName (by value): fully-qualified filename
        *
        * Returns:
        *   Logical .t. if completed successfully, .f. if not.
        *
        * Return variables:
        *   ldCreateDate (date, by reference): Creation date
        *   lcCreateTime (character, by reference): Creation time
        *      (HH:MM:SS.mm)
        *   ldAccessDate (date, by reference): Last access date
        *   lcAccessTime (character, by reference): Last access time
        *      (HH:MM:SS.mm)
        *   lcLastWriteDate (date, by reference): Last write date
        *   lcLastWriteTime (character, by reference): Last write time
        *      (HH:MM:SS.mm)
        *
        *
        * Usage example: (assumes C:\Test.txt exists)
        m.createdate = {}
        m.createtime = ""
        m.accessdate = {}
        m.accesstime = ""
        m.lastwritedate = {}
        m.lastwritetime = ""
        m.OK = getftime("C:\Test.txt", @m.createdate, @m.createtime, ;
              @m.accessdate, @m.accesstime, @m.lastwritedate, ;
              @m.lastwritetime)
        IF m.OK
              ? "Create date: ", m.createdate
              ? "Create time: " , m.createtime
              ? "Last access date: ", m.accessdate
              ? "Last access time: ", m.accesstime
              ? "Last write date: ", m.lastwritedate
              ? "Last write time: ", m.lastwritetime
        ELSE
              ? "Error getting file times"
        ENDIF
        *
        * End of usage example.

        FUNCTION getftime

        PARAMETERS lcFileName, ldCreateDate, lcCreateTime, ldAccessDate,;
              lcAccessTime, ldLastWriteDate, lcLastWriteTime

        * Blank filename or nonexistent file, no need to continue.
        IF EMPTY(lcFileName) OR NOT FILE(lcFileName)
              RETURN .F.
        ENDIF

        * DEFINEs for file share mode.
        #DEFINE FILE_SHARE_READ 1
        #DEFINE FILE_SHARE_WRITE 2

        * DEFINEs for access right
        #DEFINE GENERIC_READ hex2dec("80000000")
        #DEFINE GENERIC_WRITE hex2dec("40000000")

        * DEFINES for Create Mode.
        #DEFINE CREATE_NEW  1
        #DEFINE CREATE_ALWAYS 2
        #DEFINE OPEN_EXISTING 3
        #DEFINE OPEN_ALWAYS 4
        #DEFINE TRUNCATE_EXISTING 5

        * File flag.
        #DEFINE FILE_ATTRIBUTE_NORMAL hex2dec("80")

        DECLARE INTEGER CreateFile IN kernel32 ;
              STRING lpFileName, ;
              INTEGER dwDesiredAccess, ;
              INTEGER dwShareMode, ;
              INTEGER lpSecurityAttributes, ;
              INTEGER dwCreationDisposition, ;
              INTEGER dwFlagsAndAttributes, ;
              INTEGER hTemplateFile

        DECLARE INTEGER GetFileTime IN kernel32 ;
              INTEGER hFile, ;
              STRING @lpCreationTime, ;
              STRING @lpLastAccessTime, ;
              STRING @lpLastWriteTime

        DECLARE INTEGER FileTimeToLocalFileTime IN kernel32 ;
              STRING @lpFileTime, ;
              STRING @lpLocalFileTime

        DECLARE INTEGER FileTimeToSystemTime IN kernel32 ;
              STRING @lpFileTime, ;
              STRING @lpSystemTime

        DECLARE INTEGER GetLastError IN kernel32

        DECLARE INTEGER CloseHandle IN kernel32 ;
              INTEGER hObject

        * 2 DWORDs * 4 bytes each = an 8-byte buffer.
        CreationTime = SPACE(8)
        LastAccessTime = SPACE(8)
        lastwritetime = SPACE(8)

        * Same buffers for local time.
        LocalCreationTime = SPACE(8)
        LocalLastAccessTime = SPACE(8)
        LocalLastWriteTime = SPACE(8)

        * 16-byte buffers for when we convert from the FILETIME
        * structs to the more human-readable SYSTEMTIME structures.
        ctSystemTime = SPACE(16)
        laSystemTime = SPACE(16)
        lwSystemTime = SPACE(16)

        * Open an existing file.
        lhFileHandle = CreateFile(lcFileName, GENERIC_READ, ;
              FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)

        IF lhFileHandle <= 0
              =MESSAGEBOX ("Error in CreateFile: Error code - " + ;
                    LTRIM(STR(GetLastError())))
              RETURN .F.
        ENDIF

        * Make the GetFileTime call, to get Create, Access and Update times.
        m.retval = GetFileTime(lhFileHandle, CreationTime, ;
              @LastAccessTime, @lastwritetime)
        IF m.retval = 0
              =MESSAGEBOX("Error calling GetFileTime. Error code: " + ;
                    LTRIM(STR(GetLastError())))
              RETURN .F.
        ENDIF

        * Successfully performed the GetFileTime call -
        * now convert each of the three times to local time.
        * If any of them fail, then exit.
        IF FileTimeToLocalFileTime(@CreationTime, @LocalCreationTime)=0 OR ;
         FileTimeToLocalFileTime(@LastAccessTime,@LocalLastAccessTime)=0 OR ;
         FileTimeToLocalFileTime(@lastwritetime, @LocalLastWriteTime)=0
              =MESSAGEBOX("FileTimeToLocalFileTime Failed. Error code: " + ;
                    LTRIM(STR(GetLastError())))
              RETURN .F.
        ENDIF

        * Now you have a local filetime, convert each to a systemtime that
        * you can work with.
        * If any of the calls fail, then exit.
        IF FileTimeToSystemTime(@LocalCreationTime, @ctSystemTime) = 0 OR ;
         FileTimeToSystemTime(@LocalLastAccessTime, @laSystemTime) = 0 OR ;
         FileTimeToSystemTime(@LocalLastWriteTime, @lwSystemTime) = 0
              =MESSAGEBOX("FileTimeToSystemTime call failed. Error code: " ;
                    + LTRIM(STR(GetLastError())))
              RETURN .F.
        ENDIF

        * Unpack the integers in creation time SystemTime structure.
        ctstYear = Str2Word(LEFT(ctSystemTime, 2))
        ctstMonth = Str2Word(SUBSTR(ctSystemTime, 3, 2))
        ctstDayofWeek = Str2Word(SUBSTR(ctSystemTime, 5, 2))
        ctstDay = Str2Word(SUBSTR(ctSystemTime, 7, 2))
        ctstHour = Str2Word(SUBSTR(ctSystemTime, 9, 2))
        ctstMinute = Str2Word(SUBSTR(ctSystemTime, 11, 2))
        ctstSecond = Str2Word(SUBSTR(ctSystemTime, 13, 2))
        ctstMilliSecond = Str2Word(SUBSTR(ctSystemTime, 15, 2))

        * Unpack the integers in last access time SystemTime structure.
        lastYear = Str2Word(LEFT(laSystemTime, 2))
        lastMonth = Str2Word(SUBSTR(laSystemTime, 3, 2))
        lastDayofWeek = Str2Word(SUBSTR(laSystemTime, 5, 2))
        lastDay = Str2Word(SUBSTR(laSystemTime, 7, 2))
        lastHour = Str2Word(SUBSTR(laSystemTime, 9, 2))
        lastMinute = Str2Word(SUBSTR(laSystemTime, 11, 2))
        lastSecond = Str2Word(SUBSTR(laSystemTime, 13, 2))
        lastMilliSecond = Str2Word(SUBSTR(laSystemTime, 15, 2))

        * Unpack the integers in last write time SystemTime structure.
        lwstYear = Str2Word(LEFT(lwSystemTime, 2))
        lwstMonth = Str2Word(SUBSTR(lwSystemTime, 3, 2))
        lwstDayofWeek = Str2Word(SUBSTR(lwSystemTime, 5, 2))
        lwstDay = Str2Word(SUBSTR(lwSystemTime, 7, 2))
        lwstHour = Str2Word(SUBSTR(lwSystemTime, 9, 2))
        lwstMinute = Str2Word(SUBSTR(lwSystemTime, 11, 2))
        lwstSecond = Str2Word(SUBSTR(lwSystemTime, 13, 2))
        lwstMilliSecond = Str2Word(SUBSTR(lwSystemTime, 15, 2))

        * Close the file handle.
        llRetCode = CloseHandle(lhFileHandle)

        * Format the return variables.
        ldCreateDate = CTOD(LTRIM(STR(ctstMonth)) + "/" + ;
            LTRIM(STR(ctstDay))+ ;
            "/" + LTRIM(STR(ctstYear)))
        lcCreateTime = PADL(LTRIM(STR(ctstHour)), 2, "0") + ":" + ;
            PADL(LTRIM(STR(ctstMinute)), 2, "0") + ":" + ;
            PADL(LTRIM(STR(ctstSecond)), 2, "0") + "." + ;
            PADL(LTRIM(STR(ctstMilliSecond)), 3, "0")
        ldAccessDate = CTOD(LTRIM(STR(lastMonth)) + "/" + ;
            LTRIM(STR(lastDay))+ ;
            "/" + LTRIM(STR(lastYear)))
        lcAccessTime = PADL(LTRIM(STR(lastHour)), 2, "0") + ":" + ;
              PADL(LTRIM(STR(lastMinute)), 2, "0") + ":" + ;
              PADL(LTRIM(STR(lastSecond)), 2, "0") + "." + ;
              PADL(LTRIM(STR(lastMilliSecond)), 3, "0")
        ldLastWriteDate = CTOD(LTRIM(STR(lwstMonth))+ ;
              "/"+LTRIM(STR(lwstDay))+ ;
              "/" + LTRIM(STR(lwstYear)))
        lcLastWriteTime = PADL(LTRIM(STR(lwstHour)), 2, "0") + ":" + ;
              PADL(LTRIM(STR(lwstMinute)), 2, "0") + ":" + ;
              PADL(LTRIM(STR(lwstSecond)), 2, "0") + "." + ;
              PADL(LTRIM(STR(lwstMilliSecond)), 3, "0")

        * Function hex2dec - Convert a character hex value into decimal.
        * Handy for dealing with Hex declarations in API calls.
        * Passed: m.hex (character hex value)
        * Returns: Decimal value
        FUNCTION hex2dec

        PARAMETER m.hex

        m.decimal = 0
        FOR i = LEN(m.hex) TO 1 STEP -1
              m.decimal = m.decimal + (AT(SUBSTR(m.hex, i, 1), ;
                 "0123456789ABCDEF") - 1) * 16 ^ (i - 1)
        NEXT
        RETURN m.decimal

        * Function str2word - Converts low-high format string representation
        *    to a 16-bit integer (word) value. Useful for unrolling structure
        *    members containg WORD types.
        *
        * Passed: Low-high string representation of 16-bit integer
        * Returns: numeric value
        FUNCTION Str2Word

        PARAMETERS m.wordstr

        PRIVATE i, m.retval

        m.retval = 0
        FOR i = 0 TO 8 STEP 8
           m.retval = m.retval + (ASC(m.wordstr) * (2^i))
           m.wordstr = RIGHT(m.wordstr, LEN(m.wordstr) - 1)
        NEXT
        RETURN m.retval

2. Create a file named Test.txt in the C:\ root for testing purposes.

3. Run the program. It should display the creation, last access and last write
  dates and times on the Visual FoxPro desktop.

REFERENCES
==========

Win32 API Help; (included with the Platform SDK) topics: "GetFileTime",
"FileTimeToLocalTime", "FileTimeToSystemTime", "SYSTEMTIME" and "FILETIME"

Additional query words: GetFileTime FileTimeToLocalFileTime FileTimeToSystemTime FILETIME SYSTEMTIME time update kbnokeyword

======================================================================
Keywords          :  
Technology        : kbVFPsearch kbAudDeveloper kbVFP300 kbVFP300b kbVFP500 kbVFP500a
Version           : WINDOWS:3.0,3.0b,5.0,5.0a
Issue type        : kbhowto

=============================================================================

THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Copyright Microsoft Corporation 1986-2002.