...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