CWindow RC 13

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

Previous topic - Next topic

José Roca

Filetimes can be stored as quads


DIM ull AS ULONGLONG = FT.dwHighDateTime shl 32 OR FT.dwLowDateTime


The problem is that the 32-bit compiler complains about shl 32.

If you want a double, you can use this function (in AfxTime.inc):


' ========================================================================================
' Converts a FILETIME to a DATE_ (double).
' ========================================================================================
PRIVATE FUNCTION AfxFileTimeToVariantTime (BYREF FT AS FILETIME) AS DATE_
   DIM dt AS DATE_, ST AS SYSTEMTIME
   FileTimeToSystemTime(@FT, @ST)
   SystemTimeToVariantTime @ST, @dt
   RETURN dt
END FUNCTION
' ========================================================================================


DATE_ is defined as double. It is the format used by VB6.

José Roca

If this format is not what you need, you can write an ad hoc function.

José Roca

I think that VB6 dates can be compared, don't they?

See: http://www.freevbcode.com/ShowCode.asp?ID=573

Paul Squires

The original function that I wrote for AfxFileDateTime works well but I want to use your standard functions in order to make it easier to maintain the code over the long run. I have attempted to store the datetime values using AfxGetFileLastWriteTime. I am then doing the comparison using AfxFileTimeToVariantTime. I can not compile the application because of warnings for:

1)  The shl 32 error you mentioned that occurs in AfxQuadDateTime.

2)  Error at parameter 4 in:
   VarBstrFromDate(vbDate, lcid, VAR_DATEVALUEONLY, @bstrOut)
Which is found in AfxVariantDateToStr, AfxVariantTimeToStr, and AfxVariantDateTimeToString
Paul Squires
PlanetSquires Software

José Roca

#34
You aren't using the latest version. I reuploaded it in post #3 fixing these issues.

This is the fixed version with the new four functions added.

Regarding the shl problem with the 32 bit compiler, I will see if I find a way of doing it without using shl. Apparently, I can't use an union as I did with PB because of alignment issues. For the moment, I have wrapped the three functions between #ifdef __FB_64BIT__ #endif waiting for a solution.

José Roca

Well, I have find a workaround for the shl and alignment problems, using and intermediate ULARGE_INTEGER structure.


' ========================================================================================
' Returns the curent date and time as a QUAD (8 bytes). IN FB, a QUAD is an ULONGLONG.
' ========================================================================================
PRIVATE FUNCTION AfxQuadDateTime () AS ULONGLONG
   DIM ST AS SYSTEMTIME, FT AS FILETIME
   GetLocalTime @ST
   SystemTimeToFileTime @ST, @FT
   DIM uli AS ULARGE_INTEGER
   uli.LowPart = FT.dwLowDateTime
   uli.HighPart = FT.dwHighDateTime
   RETURN uli.QuadPart
END FUNCTION
' ========================================================================================


The attached file contains the latest version.



José Roca

Quote
The original function that I wrote for AfxFileDateTime works well but I want to use your standard functions in order to make it easier to maintain the code over the long run.

The problem with custom formats is that they can't be used for general purpose progamming. You can do it to solve a particular problem in your application, but for general purpose we have to use the standard formats: FILETIME, SYSTEMTIME, DATE_, ULONGLONG/ULONGINT.

Paul Squires

Thanks Jose, the new file worked perfectly.

The date comparison worked perfectly as well:
            If AfxFileTimeToVariantTime(ft) <> AfxFileTimeToVariantTime(pDoc->DateFileTime) Then

Paul Squires
PlanetSquires Software

José Roca

#38
I'm going to add to AfxWin.inc the following function:


' ========================================================================================
' Retrieves the path of an special folder. Requires Windows Vista/Windows 7 or superior.
' - rfid: A reference to the KNOWNFOLDERID that identifies the folder. The folders associated
'   with the known folder IDs might not exist on a particular system.
' - dwFlags: Flags that specify special retrieval options. This value can be 0; otherwise,
'   it is one or more of the KNOWN_FOLDER_FLAG values.
' - hToken: An access token used to represent a particular user. This parameter is usually
'   set to NULL, in which case the function tries to access the current user's instance of
'   the folder. However, you may need to assign a value to hToken for those folders that can
'   have multiple users but are treated as belonging to a single user. The most commonly used
'   folder of this type is Documents.
'   The calling application is responsible for correct impersonation when hToken is non-null.
'   It must have appropriate security privileges for the particular user, including TOKEN_QUERY
'   and TOKEN_IMPERSONATE, and the user's registry hive must be currently mounted. See Access
'   Control for further discussion of access control issues.
'   https://msdn.microsoft.com/en-us/library/windows/desktop/aa374860(v=vs.85).aspx
'   Assigning the hToken parameter a value of -1 indicates the Default User. This allows
'   clients of SHGetKnownFolderIDList to find folder locations (such as the Desktop folder)
'   for the Default User. The Default User user profile is duplicated when any new user
'   account is created, and includes special folders such as Documents and Desktop.
'   Any items added to the Default User folder also appear in any new user account.
'   Note that access to the Default User folders requires administrator privileges.
' Return value:
'   The path of the requested folder on success, or an empty string on failure.
' Remarks: For a list of KNOWNFOLDERID constants see:
'   https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457(v=vs.85).aspx
' Usage example: AfxGetKnowFolderPath(@FOLDERID_CommonPrograms)
' ========================================================================================
PRIVATE FUNCTION AfxGetKnowFolderPath (BYVAL rfid AS CONST KNOWNFOLDERID CONST PTR, BYVAL dwFlags AS DWORD = 0, BYVAL hToken AS HANDLE = NULL) AS CWSTR
   DIM pidl AS ITEMIDLIST PTR          ' // Pointer to an item identifier list (PIDL)
   DIM wszPath AS WSTRING * MAX_PATH   ' // Folder's path
   IF SHGetKnownFolderIDList(rfid, dwFlags, hToken, @pidl) = S_OK THEN
      SHGetPathFromIDListW pidl, @wszPath
      CoTaskMemFree pidl
      RETURN wszPath
   END IF
END FUNCTION
' ========================================================================================


For a list of known folders, see:
https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457(v=vs.85).aspx

It uses SHGetKnownFolderIDList, that requires Vista or superior and replaces the deprecated SHGetSpecialFolderLocation.

José Roca

For XP users


' ========================================================================================
' Retrieves the path of an special folder.
' - nFolder: A CSIDL value that identifies the folder of interest.
' For a list of CSIDL values see:
' https://msdn.microsoft.com/en-us/library/windows/desktop/bb762494(v=vs.85).aspx
' ========================================================================================
PRIVATE FUNCTION AfxGetSpecialFolderLocation (BYVAL nFolder AS LONG) AS CWSTR
   DIM pidl AS ITEMIDLIST PTR          ' // Pointer to an item identifier list (PIDL)
   DIM wszPath AS WSTRING * MAX_PATH   ' // Folder's path
   IF SHGetSpecialFolderLocation(0, nFolder, @pidl) = S_OK THEN
      SHGetPathFromIDListW pidl, @wszPath
      CoTaskMemFree pidl
      RETURN wszPath
   END IF
END FUNCTION
' ========================================================================================


José Roca

An update of AfxWin.inc with the new functions and the AfxBrowseForFolder dialog.

Marc Pons

Jose,
1 question on : PRIVATE FUNCTION AfxUcode OVERLOAD (BYREF ansiStr AS CONST STRING, BYVAL nCodePage AS LONG = 0) AS AFX_BSTR
   DIM pbstr AS AFX_BSTR
   IF nCodePage = CP_UTF8 THEN
      DIM dwLen AS DWORD = MultiByteToWideChar(CP_UTF8, 0, STRPTR(ansiStr), LEN(ansiStr), NULL, 0)
      IF dwLen THEN
         pbstr = SysAllocString(WSTR(SPACE(dwLen)))
         dwLen = MultiByteToWideChar(CP_UTF8, 0, STRPTR(ansiStr), LEN(ansiStr), pbstr, dwLen * 2)
      END IF
   ELSE
      pbstr = SysAllocString(WSTR(SPACE(LEN(ansiStr))))
      MultiByteToWideChar(nCodePage, MB_PRECOMPOSED, STRPTR(ansiStr), LEN(ansiStr), pbstr, LEN(ansiStr) * 2)
   END IF
   FUNCTION = pbstr
END FUNCTION


why do you go with AFX_BSTR ( wich is wstring ptr)  and not the following using the CWSTR way

PRIVATE FUNCTION AfxUcode OVERLOAD (BYREF ansiStr AS CONST STRING, BYVAL nCodePage AS LONG = 0) AS CWSTR
   DIM cwstr AS CWSTR
   IF nCodePage = CP_UTF8 THEN
      DIM dwLen AS DWORD = MultiByteToWideChar(CP_UTF8, 0, STRPTR(ansiStr), LEN(ansiStr), NULL, 0)
      IF dwLen THEN
cwstr = WSTR(SPACE(dwLen))
         dwLen = MultiByteToWideChar(CP_UTF8, 0, STRPTR(ansiStr), LEN(ansiStr), @cwstr, dwLen * 2)
      END IF
   ELSE
      cwstr = WSTR(SPACE(LEN(ansiStr)))
      MultiByteToWideChar(nCodePage, MB_PRECOMPOSED, STRPTR(ansiStr), LEN(ansiStr), @cwstr, LEN(ansiStr) * 2)
   END IF
   return cwstr
END FUNCTION


is is because you need it on the memory allocation form to COM  ?

Marc

José Roca

I wrote them before the CWSTR class, when I was trying other ways. Maybe it may need them or not in the future.

José Roca

> why do you go with AFX_BSTR ( wich is wstring ptr)  and not the following using the CWSTR way

Well, a BSTR is not a WSTRING PTR, it has a prefix length. The problem is that the FB compiler has no idea of what a BSTR is. I even have to use AFX_BSTR because in the latest headers they changed the definition to WCHAR.

Because BSTR have a prefix length, they allow to work with embedded nulls, but because with FB we have to treat i as if it was a WSTRING PTR, we can only deal with embedded nulls using pointers.

See below what kind of stunts I have needed to do to get the non COM versions of the open and save file dialogs working.

José Roca

#44
Unicode versions of the open and save file dialogs using the Windows API.


' ========================================================================================
' Creates an Open dialog box that lets the user specify the drive, directory, and the name
' of a file or set of files to be opened.
' - hwndOwner: A handle to the window that owns the dialog box. This parameter can be any
'   valid window handle, or it can be NULL if the dialog box has no owner.
' - wszTitle: A string to be placed in the title bar of the dialog box. If this member is NULL,
'   the system uses the default title (that is, Save As or Open).
' - wszFile: The file name used to initialize the File Name edit control. When the GetOpenFileName
'   or GetSaveFileName function returns successfully, this buffer contains the drive designator,
'   path, file name, and extension of the selected file.
'   If the OFN_ALLOWMULTISELECT flag is set and the user selects multiple files, the buffer
'   contains the current directory followed by the file names of the selected files. For
'   Explorer-style dialog boxes, the directory and file name strings are NULL separated,
'   with an extra NULL character after the last file name. For old-style dialog boxes, the
'   strings are space separated and the function uses short file names for file names with
'   spaces. You can use the FindFirstFile function to convert between long and short file
'   names. If the user selects only one file, the lpstrFile string does not have a separator
'   between the path and file name.
' - wszInitialDir: The initial directory.
' - wszFilter: A buffer containing pairs of "|" separated filter strings. The first string
'   in each pair is a display string that describes the filter (for example, "Text Files"),
'   and the second string specifies the filter pattern (for example, "*.TXT"). To specify
'   multiple filter patterns for a single display string, use a semicolon to separate the
'   patterns (for example, "*.TXT;*.DOC;*.BAK"). A pattern string can be a combination of
'   valid file name characters and the asterisk (*) wildcard character. Do not include spaces
'   in the pattern string.
'   The system does not change the order of the filters. It displays them in the File Types
'   combo box in the order specified in wszFilter. If wszFilter is NULL, the dialog box
'   does not display any filters.
' - wszDefExt: The default extension. GetOpenFileName and GetSaveFileName append this
'   extension to the file name if the user fails to type an extension. This string can be
'   any length, but only the first three characters are appended. The string should not
'   contain a period (.). If this member is NULL and the user fails to type an extension,
'   no extension is appended.
' - pdwFlags: A set of bit flags you can use to initialize the dialog box. When the dialog
'   box returns, it sets these flags to indicate the user's input. For example, to check
'   if the user has checked the read only checkbox:
'   IF (pdwFlags AND %OFN_READONLY) = %OFN_READONLY THEN ...
'   This value can be a combination of the following flags:
'   See complete list and explanations at:
'   https://msdn.microsoft.com/en-us/library/windows/desktop/ms646839(v=vs.85).aspx
' - pdwBufLen: The size of the buffer, in charactersm where the names of the selected
'   files will be returned.
' Return value:
'   An string containing a comma separated list of the selected files.
'   Parse the number of ",". If only one, then the user has selected only a file and the
'   string contains the full path. If more, The first substring contains the path and the
'   others the files.
'   If the user has not selected any file, an empty string is returned.
'   On failure, an empty string is returned and, if not null, the pdwBufLen parameter will
'   be filled by the size of the required buffer in characters.
' ========================================================================================
PRIVATE FUNCTION AfxOpenFileDialog ( _
   BYVAL hwndOwner AS HWND _                    ' // Parent window
, BYREF wszTitle AS WSTRING _                  ' // Caption
, BYREF wszFile AS WSTRING _                   ' // Filename
, BYREF wszInitialDir AS WSTRING _             ' // Start directory
, BYREF wszFilter AS WSTRING _                 ' // Filename filter
, BYREF wszDefExt AS WSTRING _                 ' // Default extension
, BYVAL pdwFlags AS DWORD PTR = NULL _         ' // Flags
, BYVAL pdwBufLen AS DWORD PTR = NULL _        ' // Buffer length
) AS CBSTR

   DIM dwFlags AS DWORD, dwBufLen AS DWORD
   IF pdwFlags THEN dwFlags = *pdwFlags
   IF pdwBufLen THEN dwBufLen = *pdwBuflen

   ' // Filter is a sequence of WSTRINGs with a final (extra) double null terminator
   ' // The "|" characters are replaced with nulls
   DIM wszMarkers AS WSTRING * 4 = "||"
   IF RIGHT(wszFilter, 1) <> "|" THEN wszMarkers += "|"
   DIM cbsFilter AS CBSTR = SysAllocString(wszFilter & wszMarkers)
   ' // Replace markers("|") with nulls
   DIM pchar AS WCHAR PTR = *cbsFilter
   DIM i AS LONG
   FOR i = 0 TO LEN(cbsFilter) - 1
      IF pchar[i] = ASC("|") THEN pchar[i] = 0
   NEXT

   ' // If the initial directory has not been specified, assume the current directory
   IF LEN(wszInitialDir) = 0 THEN wszInitialDir = CURDIR
   ' // The size of the buffer must be at least MAX_PATH characters
   IF dwBufLen = 0 THEN
      IF (dwFlags AND OFN_ALLOWMULTISELECT = OFN_ALLOWMULTISELECT) THEN dwBufLen = 32768  ' // 64 Kb buffer (enough for at least 126 files)
   END IF
   IF dwBufLen < 260 THEN dwBufLen = 260   ' // Make room for at least one path
   ' // Allocate the file name and a marker ("|") to be replaced with a null
   DIM cbsFile AS CBSTR = SysAllocString(wszFile & "|")
   ' // Store the position of the marker
   DIM cbPos AS LONG = LEN(cbsFile) - 1
   ' // Allocate room for the buffer
   IF LEN(cbsFile) < dwBufLen THEN cbsFile += SPACE(dwBufLen - LEN(cbsFile))
   ' // The filename must be null terminated (replace the marker with a null)
   pchar = *cbsFile
   pchar[cbPos] = 0

   ' // Fill the members of the structure
   DIM ofn AS OPENFILENAMEW
   ofn.lStructSize     = SIZEOF(ofn)
   IF AfxWindowsVersion < 5 THEN ofn.lStructSize = 76
   ofn.hwndOwner       = hwndOwner
   ofn.lpstrFilter     = *cbsFilter
   ofn.nFilterIndex    = 1
   ofn.lpstrFile       = *cbsFile
   ofn.nMaxFile        = LEN(cbsFile)
   ofn.lpstrInitialDir = @wszInitialDir
   IF LEN(wszTitle) THEN ofn.lpstrTitle = @wszTitle
   ofn.Flags = dwFlags OR OFN_EXPLORER
   IF LEN(wszDefExt) THEN ofn.lpstrDefExt = @wszDefExt

   ' // Call the open file dialog
   IF GetOpenFilenameW(@ofn) THEN
      pchar = *cbsFile
      FOR i = 0 TO LEN(cbsFile) - 1
         ' // If double null, exit
         IF pchar[i] = 0 AND pchar[i + 1] = 0 THEN EXIT FOR
         ' // Replace null with ","
         IF pchar[i] = 0 THEN pchar[i] = ASC(",")
      NEXT
      ' // Trim trailing spaces
      cbsFile = RTRIM(cbsFile, CHR(32))
   ELSE
      ' // Buffer too small
      IF CommDlgExtendedError = FNERR_BUFFERTOOSMALL THEN
         dwBufLen = ASC(**cbsFile)
      END IF
      cbsFile = ""
   END IF

   ' // Return the retrieved values
   IF pdwFlags THEN *pdwFlags = ofn.Flags
   IF pdwBufLen THEN *pdwBufLen = dwBufLen
   RETURN cbsFile

END FUNCTION
' ========================================================================================



' ========================================================================================
' The parameters are the same that for AfxOpenFileDialog, except the optional pdwBufferLen.
' In the pdwFlags parameter you may add OFN_OVERWRITEPROMPT to be asked if you want to
' overwrite an existing file.
' ========================================================================================
PRIVATE FUNCTION AfxSaveFileDialog ( _
   BYVAL hwndOwner AS HWND _                    ' // Parent window
, BYREF wszTitle AS WSTRING _                  ' // Caption
, BYREF wszFile AS WSTRING _                   ' // Filename
, BYREF wszInitialDir AS WSTRING _             ' // Start directory
, BYREF wszFilter AS WSTRING _                 ' // Filename filter
, BYREF wszDefExt AS WSTRING _                 ' // Default extension
, BYVAL pdwFlags AS DWORD PTR = NULL _         ' // Flags
) AS CBSTR

   DIM dwFlags AS DWORD
   IF pdwFlags THEN dwFlags = *pdwFlags

   ' // Filter is a sequence of WSTRINGs with a final (extra) double null terminator
   ' // The "|" characters are replaced with nulls
   DIM wszMarkers AS WSTRING * 4 = "||"
   IF RIGHT(wszFilter, 1) <> "|" THEN wszMarkers += "|"
   DIM cbsFilter AS CBSTR = SysAllocString(wszFilter & wszMarkers)
   ' // Replace markers("|") with nulls
   DIM pchar AS WCHAR PTR = *cbsFilter
   DIM i AS LONG
   FOR i = 0 TO LEN(cbsFilter) - 1
      IF pchar[i] = ASC("|") THEN pchar[i] = 0
   NEXT

   ' // If the initial directory has not been specified, assume the current directory
   IF LEN(wszInitialDir) = 0 THEN wszInitialDir = CURDIR
   IF LEN(wszFile) > MAX_PATH THEN wszFile = LEFT(wszFile, MAX_PATH)
   DIM cbsFile AS CBSTR = SysAllocString(wszFile & "|")
   ' // Store the position of the marker
   DIM cbPos AS LONG = LEN(cbsFile) - 1
   ' // Allocate room for the buffer
   IF LEN(cbsFile) < MAX_PATH THEN cbsFile += SPACE(MAX_PATH - LEN(cbsFile))
   ' // The filename must be null terminated (replace the marker with a null)
   pchar = *cbsFile
   pchar[cbPos] = 0

   ' // Fill the members of the structure
   DIM ofn AS OPENFILENAMEW
   ofn.lStructSize     = SIZEOF(ofn)
   IF AfxWindowsVersion < 5 THEN ofn.lStructSize = 76
   ofn.lpstrFilter     = *cbsFilter
   ofn.nFilterIndex    = 1
   ofn.lpstrFile       = *cbsFile
   ofn.nMaxFile        = LEN(cbsFile)
   ofn.lpstrInitialDir = @wszInitialDir
   IF LEN(wszTitle) THEN ofn.lpstrTitle = @wszTitle
   ofn.Flags = dwFlags OR OFN_EXPLORER
   IF LEN(wszDefExt) THEN ofn.lpstrDefExt = @wszDefExt

   ' // Call the save filename dialog
   IF GetSaveFilenameW(@ofn) = 0 THEN cbsFile = ""

   ' // Return the retrieved values
   IF pdwFlags THEN *pdwFlags = ofn.Flags
   RETURN cbsFile

END FUNCTION
' ========================================================================================