# ...more kernel32 and more file attributes

This lesson is a continuation of the previous program. ( attrib.exw ) Attrib2.exw will allow you to type any program name into it's EditText entry box to get and set a file's attribute bits. It will also allow you to update the last-write time of the file, in the same way that the old DOS program, TOUCH.EXE did, using the current system time.

This lesson also introduces a few more DLL functions not in win32lib.ew yet.

The FindFirstFile( ) function queries a file, and returns some information about that file in a block of allocated memory. Since one of the elements in this structure is the file size, we no longer have to use GetFileSize( ), as we did in the previous lesson. This structure also contains information about the file's attributes, creation time, and the last-access and last-write times.

Since win32 handles file times in coordinated universal time, ( good grief ! ), to handle different time zones, we use FileTimeToLocalFileTime( ) to do a little 'time travelling', to our local time.

FileTimeToSystemTime( ) translates the 64 bit file time into the format described for the SystemTime structure, which uses 16 bits for each of eight time elements.

GetSystemTimeAsFileTime( ) simply does the reverse, converting from eight 16 bit elements, to the 64 bit value used in files.

Finally, SetFileTime( ) actually 'touches' the file, to update it's last-write time.

-- attrib2.exw

    --attrib2.exw--
    --start code--

    include win32lib.ew
    without warning

    sequence file_name
    -- for demonstration,  pre-set a file name
    file_name="attrib2.exw"
    atom junk,  file_id_ptr,  file_handle

    --declare some strings as constants
    constant
      str1 = "file size = ",
      str2 = "current attributes = ",
      str3 = "long filename  = ",
      str4 = "short filename = ",
      str5 = "filedate ( m/d/y  ) = ",
      str6 = "filetime ( last modified ) = ",

    --declare some constant values by name
      zOPEN_EXISTING = 3,
      zGENERIC_WRITE =  #40000000,
      zMAX_PATH = 260,
      zFILE_ATTRIBUTE_READONLY = 1,
      zFILE_ATTRIBUTE_HIDDEN = 2,
      zFILE_ATTRIBUTE_ARCHIVE = 32,
      zFILE_ATTRIBUTE_NORMAL = 128,

    --create our window and all it's controls
      Win     = create( Window, "File Attributes", 0, 0, 0, 285, 365, 0 ),
      Label0  = create( LText, "filename:", Win, 10, 12, 70, 20, 0 ),
      Entry   = create( EditText, "", Win, 75, 10, 190, 20, 0 ),
      Label1  = create( LText, "", Win, 10, 40, 180, 20, 0 ),
      Label2  = create( LText, "", Win, 10, 65, 180, 20, 0 ),
      Label3  = create( LText, "", Win, 10, 200, 290, 20, 0 ),
      Label4  = create( LText, "", Win, 10, 225, 290, 20, 0 ),
      Label5  = create( LText, "", Win, 10, 250, 290, 20, 0 ),
      Label6  = create( LText, "", Win, 10, 275, 290, 20, 0 ),
      Check1  = create( CheckBox, " Read-Only ( 1 )", Win, 10, 90, 170, 20, 0 ),
      Check2  = create( CheckBox, " Hidden    ( 2 )", Win, 10, 115, 170, 20, 0 ),
      Check3  = create( CheckBox, " Archive   ( 32 )", Win, 10, 140, 170, 20, 0 ),
      Button1 = create( PushButton, "Change Attributes", Win, 10, 165, 130, 25, 0 ),
      Button2 = create( PushButton, "Update FileTime", Win, 10, 300, 130, 25, 0 ),
    --**

    --declare all our 'links' to the DLL functions
    zGetFileAttributes=registerw32Function( kernel32, "GetFileAttributesA", {C_POINTER}, C_LONG ),
    zSetFileAttributes=registerw32Function( kernel32, "SetFileAttributesA", {C_POINTER, C_LONG}, C_LONG ),
    zFindFirstFile = registerw32Function( kernel32, "FindFirstFileA", {C_POINTER, C_POINTER}, C_LONG ),
    zFileTimeToLocalFileTime = registerw32Function( kernel32, "FileTimeToLocalFileTime", {C_POINTER, C_POINTER}, C_LONG ),
    zFileTimeToSystemTime = registerw32Function( kernel32, "FileTimeToSystemTime", {C_POINTER, C_POINTER}, C_LONG ),
    zGetSystemTimeAsFileTime = registerw32Procedure( kernel32, "GetSystemTimeAsFileTime", {C_POINTER} ),
    zSetFileTime = registerw32Function( kernel32, "SetFileTime", {C_LONG, C_POINTER, C_POINTER, C_POINTER}, C_LONG ),
    zCreateFile = registerw32Function( kernel32, "CreateFileA",
                         {C_POINTER, C_LONG, C_LONG, C_LONG, C_LONG, C_LONG, C_LONG}, C_LONG ),
    zCloseHandle = registerw32Function( kernel32, "CloseHandle", {C_LONG}, C_LONG ),

    --declare and 'allocate' all our structures

    -- LastWriteTemp structure
     size_of_LastWriteTemp = 8,
     Local=allocate( size_of_LastWriteTemp ),
     LowLocalTime = Local + 0,
     HighLocalTime = Local + 4,

    -- WIN32_FIND_DATA structure
     size_of_WIN32_FIND_DATA = 318,
     file_data=allocate( size_of_WIN32_FIND_DATA ),
     dwFileAttributes  = file_data + 0,
     ftCreationTime    = file_data + 4,
     ftLastAccessTime  = file_data + 12,
     ftLastWriteTime   = file_data + 20,
     nFileSizeHigh     = file_data + 28,
     nFileSizeLow      = file_data + 32,
     dwReserved0       = file_data + 36,
     dwReserved1       = file_data + 40,
     cFileName         = file_data + 44,
     cAlternateFileName = cFileName + zMAX_PATH,

    -- SystemTime structure
     size_of_SystemTime = 16,
     Time=allocate( size_of_SystemTime ),
     wYear = Time + 0,
     wMonth = Time + 2,
     wDayOfWeek = Time + 4,
     wDay = Time + 6,
     wHour= Time + 8,
     wMinute = Time + 10,
     wSecond = Time + 12,
     wMilliseconds = Time + 14

    --set the font for the check-box labels to 'fixed'
    --so everything stays lined up.
    for i=Check1 to Check3 do
      setFont(  i, "Courier New", 10, 1 )
    end for

    --give our window background some color
    setWindowBackColor( Win, rgb( 255, 255, 0 ) )

    --do our 'time-travelling'.
    procedure show_file_time()
      sequence t_string
      junk=w32Func( zFileTimeToLocalFileTime, {ftLastWriteTime, Local} )
      junk=w32Func( zFileTimeToSystemTime, {Local, Time} )
      t_string=sprintf( "%d", peek( wMonth ) ) & "/"
           & sprintf( "%d", peek( wDay ) ) & "/"
           --the year is four digits in two bytes
           & sprintf( "%d", peek( wYear )+( peek( wYear+1 )*256 ) )
      setText( Label5, str5 & sprintf( "%s", {t_string} ) )
      t_string=sprintf( "%2d", peek( wHour ) ) & ":"
            --always express minutes as two digits
            & sprintf( "%02d", peek( wMinute ) )
      setText( Label6, str6 & sprintf( "%s", {t_string} ) )
    end procedure

    --'touch' our file, to set current time.
    procedure set_file_time( )
      file_handle=w32Func( zCreateFile,
                  {file_id_ptr, zGENERIC_WRITE, 0, 0, zOPEN_EXISTING, 0, 0} )
      w32Proc( zGetSystemTimeAsFileTime, {Local} )
      junk=w32Func( zSetFileTime, {file_handle, NULL, NULL, Local} )
      junk=w32Func( zCloseHandle, {file_handle} )
      junk=w32Func( zFindFirstFile, {file_id_ptr, file_data} )
      show_file_time( )
    end procedure

    --fetch the file's information
    --and display it.
    procedure do_it(atom self, atom event, sequence params)
      setText( Entry, file_name )
      file_id_ptr=allocate_string( file_name )
      file_handle=w32Func( zCreateFile,
                  {file_id_ptr, 0, 0, 0, zOPEN_EXISTING, 0, 0} )
      --does this file exist ?
      if file_handle != -1 then
         junk=w32Func( zFindFirstFile, {file_id_ptr, file_data} )
         setText( Label1, str1 & sprintf( "%d", peek4u( nFileSizeLow ) ) )
         junk=peek4u( dwFileAttributes )
         setText( Label2, str2 & sprintf( "%d", junk ) )
         if junk < zFILE_ATTRIBUTE_NORMAL then
           if junk >= zFILE_ATTRIBUTE_ARCHIVE then
               setCheck( Check3, 1 ) junk=junk-zFILE_ATTRIBUTE_ARCHIVE
           end if
           if junk >= zFILE_ATTRIBUTE_HIDDEN then
               setCheck( Check2, 1 ) junk=junk-zFILE_ATTRIBUTE_HIDDEN
           end if
           if junk = zFILE_ATTRIBUTE_READONLY then
               setCheck( Check1, 1 )
           end if
         end if
         setText( Label3, str3 & sprintf( "%s", {peek_string( cFileName )} ) )
         setText( Label4, str4 & sprintf( "%s", {peek_string( cAlternateFileName )} ) )
         show_file_time( )
    --If NO SUCH FILE,  clean up the mess,
      else
        setText( Label1, str1 ) setText( Label2, str2 ) setText( Label3, str3 )
        setText( Label4, str4 ) setText( Label5, str5 ) setText( Label6, str6 )
        --and tell the user.
        junk = message_box(  "NO SUCH FILE", "ERROR !", #2000 )
      end if
      junk=w32Func( zCloseHandle, {file_handle} )
    end procedure

    --update the file's attribute flags
    procedure set_attribs(atom self, atom event, sequence params)
      atom attribs
      attribs=0
      if isChecked( Check1 ) then attribs=attribs+1 end if
      if isChecked( Check2 ) then attribs=attribs+2 end if
      if isChecked( Check3 ) then attribs=attribs+32 end if
      junk=w32Func( zSetFileAttributes, {file_id_ptr, attribs} )
      junk=w32Func( zGetFileAttributes, {file_id_ptr} )
      setText( Label2, str2 & sprintf( "%d", junk ) )
    end procedure

    --remove leading spaces,  just in case !
    function trim_left( sequence x )
      for i=1 to length( x ) do
        if x[i] != ' ' then return x[i..length( x )] end if
      end for
      return {}
    end function

    --if user hits the enter key,  look for new file
    procedure new_name( )
      setCheck( Check1, 0 )  setCheck( Check2, 0 )  setCheck( Check3, 0 )
      file_name=getText( Entry )
      file_name=trim_left( file_name )
      do_it(1,2,"")	-- arbitrary params
    end procedure

    procedure onkey_entry(atom self, atom event, sequence params)
      integer key  key = params[1]
      if key=13 then new_name( ) end if
    end procedure

    setHandler( Win, w32HOpen, routine_id("do_it") )
    setHandler( Button1, w32HClick, routine_id("set_attribs") )
    setHandler( Button2, w32HClick, routine_id("set_file_time") )
    setHandler( Entry, w32HKeyDown, routine_id("onkey_entry") )

    WinMain(  Win,  Normal  )
    --end code--

This program only handles file sizes up to 32 bits. I'll just leave it this way, for you to 'fix'.

...about structures.

Or really late at night, pointers to variables accessed through 'shared' allocated memory accessible to both Euphoria, and Win32 DLL's ... ;- )

Please note that I have declared my 'structure' constants in this program a little differently.

One can declare the WIN32_FIND_DATA structure in the usual way as:

            constant
             dwFileAttributes  =  0,
             ftCreationTime    =  4,
             ftLastAccessTime  =  12,
             ftLastWriteTime   =  20,
             nFileSizeHigh     =  28,
            ...etc:

...and then allocate the required space with:

             file_data = allocate( 318 )

However, since we are actually only declaring the offsets into the structure, we have to access it with a 'calculation' statement like:

            xyz = peek( file_data + nFileSizeHigh )
    ...to access the fifth element in the structure.

By declaring it 'backwards' to the above, one creates named constants which can be used with a little less effort.

            constant
             file_data = allocate( 318 ),
             dwFileAttributes  = file_data + 0,
             ftCreationTime    = file_data + 4,
             ftLastAccessTime  = file_data + 12,
             ftLastWriteTime   = file_data + 20,
             nFileSizeHigh     = file_data + 28,
            ...etc:

            xyz = peek( nFileSizeHigh )
    ...to access the fifth element in the structure.

This works just fine, as long as one doesn't ( free( file_data ) ) in the middle of the program's execution, because it, and all pointers to it, were declared as constants.

...end...

CONTENTS