CWindow RC 13

Started by José Roca, July 15, 2016, 01:37:45 AM

Previous topic - Next topic

José Roca

Thanks very much. I have modified the code.

James Fuller

Jose,
  Would you please show a snippet using cWindow.UserData
Thank you,
James

Paul Squires

Quote from: James Fuller on July 17, 2016, 11:14:12 AM
Jose,
  Would you please show a snippet using cWindow.UserData
Thank you,
James


UserData is great place to store 32-bit values. I used it to store pointers. You just need to cast the value to the correct pointer before you use it. You can store handles to fonts that you created and then retrieve them in WM_DESTROY so you can delete them. Very versatile.

Here is a simple example where I am storing and later retrieving a manually created/allocated WSTRING ptr.


' ========================================================================================
' Save the base folder path that was set when user called ctlExTree_AddFolder
' ========================================================================================
Function ctlExTree_SetBaseFolder( ByVal hTree As HWND, ByRef wszFolder As WString ) As BOOLEAN
   Dim pWindow As CWindow Ptr = AfxCWindowOwnerPtr(hTree)
   If pWindow = 0 Then Exit Function
   Dim As WSTRING Ptr pwsz = CALLOCATE( (Len(wszFolder) * 2) + 2 )
   *pwsz = wszFolder
   pWindow->UserData(0) = Cast( ULONG_PTR, pwsz)
   Function = True
End Function

' ========================================================================================
' Retrieve the base folder path that was set when user called ctlExTree_AddFolder
' ========================================================================================
Function ctlExTree_GetBaseFolder(ByVal hTree As HWND) As CBSTR
   Dim pWindow As CWindow Ptr = AfxCWindowOwnerPtr(hTree)
   If pWindow = 0 Then Return ""
   Dim pwsz As WSTRING ptr = Cast(WString Ptr, pWindow->UserData(0))
   If pwsz Then Return *pwsz
End Function



Paul Squires
PlanetSquires Software

Paul Squires

Jose,

I'd like to propose adding a function to retrieve a DateTime value for a specified filename. FB has a function called FileDateTime but you need to use ** double indirection for CWSTR. (Likewise, for the function FileExists you need to use **, but for that function I have substituted AfxPathFileExists).

Maybe something like this (based somewhat on your code from CSED):


FUNCTION AfxFileDateTime( BYREF wszFileSpec AS WSTRING ) AS DOUBLE

   DIM fd      AS WIN32_FIND_DATA
   DIM ft      AS SYSTEMTIME
   DIM hFile   AS HANDLE

   hFile = FindFirstFile( wszFileSpec, @fd)
   IF hFile = INVALID_HANDLE_VALUE THEN EXIT FUNCTION
   FindClose hFile

   ' Convert the file to system time. The result value is not compatible
   ' with the FB function DateFileTime().
   FileTimeToSystemTime @fd.ftLastWriteTime, @ft

   FUNCTION = VAL( str(ft.wYear) + _
                   AfxStrRSet(str(ft.wMonth), 2, "0")  + _
                   AfxStrRSet(str(ft.wDay), 2, "0")    +  _
                   "." + _
                   AfxStrRSet(str(ft.wHour), 2, "0")   + _
                   AfxStrRSet(str(ft.wMinute), 2, "0") + _
                   AfxStrRSet(str(ft.wSecond), 2, "0") _
                   )
END FUNCTION

Paul Squires
PlanetSquires Software

José Roca

I have in my todo list an include file with wrappers for file functions. They will accept an optional datetime mask because each country has its own way to format dates and times. Besides the unicode problem, there is also the internationalization problem.

James Fuller

Paul,
  I hope it is a 64bit value in 64bit as all pointers are 64bit are they not?

James

Paul Squires

Quote from: James Fuller on July 17, 2016, 12:29:18 PM
Paul,
  I hope it is a 64bit value in 64bit as all pointers are 64bit are they not?

James


If you check Jose's CWindow source code you will see that UserData is defined as:

      DIM m_rgUserData(0 TO 99) AS LONG_PTR

A hundred slots of data! Lots of room to do things.

If you look in \inc\win\basetsd.bi you will see a conditional that defines LONG_PTR depending on whether the system is 32 or 64 bit. So, yes it is a 64 bit value on a 64 bit system.
Paul Squires
PlanetSquires Software

José Roca

#22
In an application, you can use an enumeration to remember more easily which data is stored, e.g.


ENUM AFX_USERDATA
   AFX_LAYOUTPTRIDX = 0
   ...
   ...
END ENUM


Using constants not only is easier to remember, but more difficult to overwrite it by accident.


DIM pLayout AS CLayout PTR = CAST(CLayout PTR, pWindow->UserData(AFX_LAYOUTPTRIDX))



pWindow.UserData(AFX_LAYOUTPTRIDX) = CAST(LONG_PTR, @pLayout)


In one example I'm using it instead of Set/Get/Remove Property.

José Roca

A variation of Paul's function:


FUNCTION AfxFileDateTime( BYREF wszFileSpec AS WSTRING ) AS DATE_

   DIM fd      AS WIN32_FIND_DATA
   DIM ft      AS SYSTEMTIME
   DIM hFile   AS HANDLE

   hFile = FindFirstFile( wszFileSpec, @fd)
   IF hFile = INVALID_HANDLE_VALUE THEN EXIT FUNCTION
   FindClose hFile

   ' Convert the file to system time. The result value is not compatible
   ' with the FB function DateFileTime().
   RETURN AfxFileTimeToVariantTime(fd.ftLastWriteTime)

END FUNCTION


The value is returned as a double in the same format that the old VB6 did, and it can ve converted to a string calling the AfxVariantDateToStr, AfxVariantTimeToStr or AfxVariantDateTimeToString.

I will add functions to allow the use of SYSTEMTIME and FILETIME, so the function can just return fd.ftLastWriteTime.

José Roca

For example:


FUNCTION AfxFileDateTime( BYREF wszFileSpec AS WSTRING ) AS FILETIME

   DIM fd      AS WIN32_FIND_DATAW
   DIM hFile   AS HANDLE

   hFile = FindFirstFileW( wszFileSpec, @fd)
   IF hFile = INVALID_HANDLE_VALUE THEN EXIT FUNCTION
   FindClose hFile

   ' Convert the file to system time. The result value is not compatible
   ' with the FB function DateFileTime().
   RETURN fd.ftLastWriteTime

END FUNCTION



' ========================================================================================
' Converts a FILETIME type to a string containing the date and the time. based on the
' specified mask, e.g. "dd-MM-yyyy"
' ========================================================================================
PRIVATE FUNCTION AfxFileTimeToDateStr (BYREF FT AS FILETIME, BYREF wszMask AS WSTRING, BYVAL lcid AS LCID = LOCALE_USER_DEFAULT) AS CWSTR
   DIM ST AS SYSTEMTIME, wszDateStr AS WSTRING * 260
   FileTimeToSystemTime(@FT, @ST)
   GetDateFormatW(lcid, NULL, @ST, wszMask, wszDateStr, SIZEOF(wszDateStr))
   RETURN wszDateStr
END FUNCTION
' ========================================================================================


We can call it as


AfxMsg AfxFileTimeToDateStr(AfxFileDateTime(AfxGetExeFullPath), "dd-MM-yyyy")


if we are Spanish

or


AfxMsg AfxFileTimeToDateStr(AfxFileDateTime(AfxGetExeFullPath), "yyyy/MM/dd")


etc.

José Roca

Later I will have to overload the wrappers that use GetDateFormatW and GetTimeFormatW to use GetDateFormatExW and GetTimeFormatExW and a string locale, such "es-ES", because Microsoft started to migrate toward the use of locale names instead of locale identifiers for new locales.

Quote
Note  For interoperability reasons, the application should prefer the GetDateFormatEx function to GetDateFormat because Microsoft is migrating toward the use of locale names instead of locale identifiers for new locales. Any application that will be run only on Windows Vista and later should use GetDateFormatEx.

José Roca

Quote
(Likewise, for the function FileExists you need to use **, but for that function I have substituted AfxPathF

You can also use AfxFileExists (in AfxWin.inc) if you dont need to include AfxPath.inc for other purposes.

José Roca

An update of AfxTime.inc.

I have added the following functions:


' ========================================================================================
' Converts a DATE_ (double) to a SYSTEMTIME.
' ========================================================================================
PRIVATE FUNCTION AfxVariantTimeToSystemTime (BYVAL dt AS DATE_) AS SYSTEMTIME
   DIM ST AS SYSTEMTIME
   VariantTimeToSystemTime dt, @ST
   RETURN ST
END FUNCTION
' ========================================================================================

' ========================================================================================
' Converts a FILETIME type to a string containing the date and the time. based on the
' specified mask, e.g. "dd-MM-yyyy"
' ========================================================================================
PRIVATE FUNCTION AfxFileTimeToDateStr (BYREF FT AS FILETIME, BYREF wszMask AS WSTRING, BYVAL lcid AS LCID = LOCALE_USER_DEFAULT) AS CWSTR
   DIM ST AS SYSTEMTIME, wszDateStr AS WSTRING * 260
   FileTimeToSystemTime(@FT, @ST)
   GetDateFormatW(lcid, NULL, @ST, wszMask, wszDateStr, SIZEOF(wszDateStr))
   RETURN wszDateStr
END FUNCTION
' ========================================================================================

' ========================================================================================
' Converts a FILETIME type to a string containing the date and the time. based on the
' specified mask, e.g. "hh':'mm':'ss".
' ========================================================================================
PRIVATE FUNCTION AfxFileTimeToTimeStr (BYREF FT AS FILETIME, BYREF wszMask AS WSTRING, BYVAL lcid AS LCID = LOCALE_USER_DEFAULT) AS CWSTR
   DIM ST AS SYSTEMTIME, wszDateStr AS WSTRING * 260
   FileTimeToSystemTime(@FT, @ST)
   GetTimeFormatW(lcid, NULL, @ST, wszMask, wszDateStr, SIZEOF(wszDateStr))
   RETURN wszDateStr
END FUNCTION
' ========================================================================================

' ========================================================================================
' Converts a SYSTEMTIME type to a string containing the date and the time. based on the
' specified mask, e.g. "dd-MM-yyyy"
' ========================================================================================
PRIVATE FUNCTION AfxSystemTimeToDateStr (BYREF ST AS SYSTEMTIME, BYREF wszMask AS WSTRING, BYVAL lcid AS LCID = LOCALE_USER_DEFAULT) AS CWSTR
   DIM wszDateStr AS WSTRING * 260
   GetDateFormatW(lcid, NULL, @ST, wszMask, wszDateStr, SIZEOF(wszDateStr))
   RETURN wszDateStr
END FUNCTION
' ========================================================================================

' ========================================================================================
' Converts a FILETIME type to a string containing the date and the time. based on the
' specified mask, e.g. "hh':'mm':'ss".
' ========================================================================================
PRIVATE FUNCTION AfxSystemTimeToTimeStr (BYREF ST AS SYSTEMTIME, BYREF wszMask AS WSTRING, BYVAL lcid AS LCID = LOCALE_USER_DEFAULT) AS CWSTR
   DIM wszDateStr AS WSTRING * 260
   GetTimeFormatW(lcid, NULL, @ST, wszMask, wszDateStr, SIZEOF(wszDateStr))
   RETURN wszDateStr
END FUNCTION
' ========================================================================================

José Roca

Hi Paul,

I have added some file functions to AfxWin.inc, among them


' ========================================================================================
' Returns the time the file was created.
' - wszFileSpec: The directory or path, and the file name, which can include wildcard characters,
'   for example, an asterisk (*) or a question mark (?).
'   This parameter should not be NULL, an invalid string (for example, an empty string or a
'   string that is missing the terminating null character), or end in a trailing backslash (\).
'   If the string ends with a wildcard, period (.), or directory name, the user must have access
'   permissions to the root and all subdirectories on the path.
'   To extend the limit from MAX_PATH to 32,767 wide characters, prepend "\\?\" to the path.
' - bUTC: Pass FALSE if you want to get the time in local time.
' Usage: AfxFileTimeToDateStr(AfxGetFileCreationTime("C:\Tests\test.bas", FALSE), "dd/MM/yyyy")
' ========================================================================================
PRIVATE FUNCTION AfxGetFileCreationTime (BYREF wszFileSpec AS WSTRING, BYVAL bUTC AS BOOLEAN = TRUE) AS FILETIME
   DIM fd AS WIN32_FIND_DATAW
   DIM hFind AS HANDLE = FindFirstFileW(wszFileSpec, @fd)
   IF hFind <> INVALID_HANDLE_VALUE THEN
      FindClose hFind
      IF bUTC = TRUE THEN
         RETURN fd.ftCreationTime
      ELSE
         DIM FT AS FILETIME
         FileTimeToLocalFileTime(@fd.ftCreationTime, @FT)
         RETURN FT
      END IF
   END IF
END FUNCTION
' ========================================================================================

' ========================================================================================
' Returns the time the file was las accessed.
' ========================================================================================
PRIVATE FUNCTION AfxGetFileLastAccessTime (BYREF wszFileSpec AS WSTRING, BYVAL bUTC AS BOOLEAN = TRUE) AS FILETIME
   DIM fd AS WIN32_FIND_DATAW
   DIM hFind AS HANDLE = FindFirstFileW(wszFileSpec, @fd)
   IF hFind <> INVALID_HANDLE_VALUE THEN
      FindClose hFind
      IF bUTC = TRUE THEN
         RETURN fd.ftLastAccessTime
      ELSE
         DIM FT AS FILETIME
         FileTimeToLocalFileTime(@fd.ftLastAccessTime, @FT)
         RETURN FT
      END IF
   END IF
END FUNCTION
' ========================================================================================

' ========================================================================================
' Returns the time the file was written to, truncated, or overwritten.
' ========================================================================================
PRIVATE FUNCTION AfxGetFileLastWriteTime (BYREF wszFileSpec AS WSTRING, BYVAL bUTC AS BOOLEAN = TRUE) AS FILETIME
   DIM fd AS WIN32_FIND_DATAW
   DIM hFind AS HANDLE = FindFirstFileW(wszFileSpec, @fd)
   IF hFind <> INVALID_HANDLE_VALUE THEN
      FindClose hFind
      IF bUTC = TRUE THEN
         RETURN fd.ftLastWriteTime
      ELSE
         DIM FT AS FILETIME
         FileTimeToLocalFileTime(@fd.ftLastWriteTime, @FT)
         RETURN FT
      END IF
   END IF
END FUNCTION
' ========================================================================================


They return a FILETIME that can be converted to a string using the AfxFileTimeToDateStr and AfxFileTimeToTimeStr using a date or time mask for localization. Can be used also with folders.

Paul Squires

Quote from: Jose Roca on July 17, 2016, 04:13:43 PM
Quote
(Likewise, for the function FileExists you need to use **, but for that function I have substituted AfxPathF

You can also use AfxFileExists (in AfxWin.inc) if you dont need to include AfxPath.inc for other purposes.


Ah, excellent. That will save me from having to Include the Path file.

For the Date/Time routines, I like the new functions. The only need I have right now is to get a value representing the file's last modified date and time. When the application regains focus it checks this value against the current disk file to see if modifications have occurred and the file needs to be reloaded. Storing FILETIME or SYSTEMTIME does not make it as easy to do the comparison. I could store the value as a formatted date/time string and then do the comparisons but simply  comparing two DOUBLES is a lot easier.
Paul Squires
PlanetSquires Software