@José Roca
Hi José
I am having a problem with AfxOpenFileDialog. If I use an empty sting for wszInitialDir my application crashes with a memory violation. If I use NULL I get a type mismatch.
I see that lpstrInitialDir is platform dependent according to the OPENFILENAME structure but even when I use CurDir for wszInitialDir the directory opened is not the current directory.
I am on Windows 10 and I have a horrible feeling that both your code and mine are OK but Windows 10 is misbehaving.
AfxOpenFileDialog
-
- Posts: 4292
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
-
- Posts: 4292
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: AfxOpenFileDialog
Oh dear, I have just been playing with PowerBASIC's 'Display Openfile' and that appears to be behaving as expected.
-
- Posts: 564
- Joined: Sep 27, 2016 18:20
- Location: Valencia, Spain
Re: AfxOpenFileDialog
It is a problem with FB 64 bit when you pass "" and the code tries to modify the content of the variable.
Replace AfxOpenFileDialog and AfxSaveFileDialog in AfxWin.inc with:
Replace AfxOpenFileDialog and AfxSaveFileDialog in AfxWin.inc with:
Code: Select all
' ========================================================================================
' 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.
' Usage example:
' DIM wszFile AS WSTRING * 260 = "*.*"
' DIM wszInitialDir AS STRING * 260 = CURDIR
' DIM wszFilter AS WSTRING * 260 = "BAS files (*.BAS)|*.BAS|" & "All Files (*.*)|*.*|"
' DIM dwFlags AS DWORD = OFN_EXPLORER OR OFN_FILEMUSTEXIST OR OFN_HIDEREADONLY OR OFN_ALLOWMULTISELECT
' DIM cws AS CWSTR = AfxOpenFileDialog(hwnd, "", wszFile, wszInitialDir, wszFilter, "BAS", @dwFlags, NULL)
' AfxMsg cws
' ========================================================================================
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 CWSTR
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 cwsFilter AS CWSTR = wszFilter & wszMarkers
DIM dwFilterStrSize AS DWORD = LEN(cwsFilter)
' // Replace markers("|") with nulls
DIM pchar AS WCHAR PTR = *cwsFilter
FOR i AS LONG = 0 TO LEN(cwsFilter) - 1
IF pchar[i] = ASC("|") THEN pchar[i] = 0
NEXT
' // If the initial directory has not been specified, assume the current directory
' // FreeBasic 64 bit fails if we pass an empty string and we try to modify wszInitialDir
DIM _wszInitialDir AS WSTRING * MAX_PATH = wszInitialDir
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
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 cwsFile AS CWSTR = wszFile & "|"
' // Store the position of the marker
DIM cbPos AS LONG = LEN(cwsFile) - 1
' // Allocate room for the buffer
IF LEN(cwsFile) < dwBufLen THEN cwsFile += SPACE(dwBufLen - LEN(cwsFile))
DIM dwFileStrSize AS DWORD = LEN(cwsFile)
' // The filename must be null terminated (replace the marker with a null)
pchar = *cwsFile
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 = *cwsFilter
ofn.nFilterIndex = 1
ofn.lpstrFile = *cwsFile
ofn.nMaxFile = dwFileStrSize
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 = *cwsFile
FOR i AS LONG = 0 TO dwFileStrSize - 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
cwsFile = RTRIM(cwsFile, CHR(32))
IF RIGHT(**cwsFile, 1) = "," THEN cwsFile = LEFT(**cwsFile, LEN(cwsFile) - 1)
ELSE
' // Buffer too small
IF CommDlgExtendedError = FNERR_BUFFERTOOSMALL THEN
dwBufLen = ASC(**cwsFile)
END IF
cwsFile = ""
END IF
' // Return the retrieved values
IF pdwFlags THEN *pdwFlags = ofn.Flags
IF pdwBufLen THEN *pdwBufLen = dwBufLen
RETURN cwsFile
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.
' Usage example:
' DIM wszFile AS WSTRING * 260 = "*.*"
' DIM wszInitialDir AS STRING * 260 = CURDIR
' DIM wszFilter AS WSTRING * 260 = "BAS files (*.BAS)|*.BAS|" & "All Files (*.*)|*.*|"
' DIM dwFlags AS DWORD = OFN_EXPLORER OR OFN_FILEMUSTEXIST OR OFN_HIDEREADONLY OR OFN_OVERWRITEPROMPT
' DIM cws AS CWSTR = AfxSaveFileDialog(hwnd, "", wszFile, wszInitialDir, wszFilter, "BAS", @dwFlags)
' AfxMsg cws
' ========================================================================================
PRIVATE FUNCTION AfxSaveFileDialog ( _
BYVAL hwndOwner AS HWND _ ' // Parent window
, BYREF wszTitle AS WSTRING _ ' // Caption
, BYREF wszFileName 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 CWSTR
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 cwsFilter AS CWSTR = wszFilter & wszMarkers
DIM dwFilterStrSize AS DWORD = LEN(cwsFilter)
' // Replace markers("|") with nulls
DIM pchar AS WCHAR PTR = *cwsFilter
DIM i AS LONG
FOR i = 0 TO LEN(cwsFilter) - 1
IF pchar[i] = ASC("|") THEN pchar[i] = 0
NEXT
' // If the initial directory has not been specified, assume the current directory
' // FreeBasic 64 bit fails if we pass an empty string and we try to modify wszInitialDir
DIM _wszInitialDir AS WSTRING * MAX_PATH = wszInitialDir
IF LEN(_wszInitialDir) = 0 THEN _wszInitialDir = CURDIR
DIM wszFile AS WSTRING * MAX_PATH = wszFileName
DIM cwsFile AS CWSTR = wszFile & "|"
' // Store the position of the marker
DIM cbPos AS LONG = LEN(cwsFile) - 1
' // Allocate room for the buffer
IF LEN(cwsFile) < MAX_PATH THEN cwsFile += SPACE(MAX_PATH - LEN(cwsFile))
DIM dwFileStrSize AS DWORD = LEN(cwsFile)
' // The filename must be null terminated (replace the marker with a null)
pchar = *cwsFile
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 = *cwsFilter
ofn.nFilterIndex = 1
ofn.lpstrFile = *cwsFile
ofn.nMaxFile = dwFileStrSize
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 cwsFile = ""
' // Return the retrieved values
IF pdwFlags THEN *pdwFlags = ofn.Flags
RETURN cwsFile
END FUNCTION
' ========================================================================================
-
- Posts: 4292
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: AfxOpenFileDialog
Thanks José. That took a bit of doing - I have UAC at full throttle and Afx is in "C:\Program Files (x86)". Anyway I got there.
I am using FB 32. I am not crashing on an empty initial directory now.
I have an application in two folders, Bob and Alice. I am simulating Bob and Alice exchanging encrypted data. The first time I use the Open File Dialog in either folder the opening directory is the application folder in each case. If I then navigate to a target file not in either folder and select it I then get another Open File Dialog requesting a task key and that opens in the application folder; where the task keys are. This behavior is exactly as I want.
However, if I introduce another member of the 'syndicate' called Jack, say, and create a folder for him then the first time I use the Open File Dialog in that folder the opening directory is of the target directory I used when in either Bob or Alice and not Jack's folder.
Windows 7 changed the way that the initial directory is chosen. To my mind it was an exceptionally stupid change and they should have left well alone - it was working fine with Windows 2000/XP/Vista. It did not break pre Windows 7 code but that code no longer behaves as expected.
What I will do is delete Bob's, Alice's and Jack's folders and start again to see if I can fathom out what is going on.
I am using FB 32. I am not crashing on an empty initial directory now.
I have an application in two folders, Bob and Alice. I am simulating Bob and Alice exchanging encrypted data. The first time I use the Open File Dialog in either folder the opening directory is the application folder in each case. If I then navigate to a target file not in either folder and select it I then get another Open File Dialog requesting a task key and that opens in the application folder; where the task keys are. This behavior is exactly as I want.
However, if I introduce another member of the 'syndicate' called Jack, say, and create a folder for him then the first time I use the Open File Dialog in that folder the opening directory is of the target directory I used when in either Bob or Alice and not Jack's folder.
Windows 7 changed the way that the initial directory is chosen. To my mind it was an exceptionally stupid change and they should have left well alone - it was working fine with Windows 2000/XP/Vista. It did not break pre Windows 7 code but that code no longer behaves as expected.
What I will do is delete Bob's, Alice's and Jack's folders and start again to see if I can fathom out what is going on.
-
- Posts: 564
- Joined: Sep 27, 2016 18:20
- Location: Valencia, Spain
Re: AfxOpenFileDialog
> Thanks José. That took a bit of doing - I have UAC at full throttle and Afx is in "C:\Program Files (x86)". Anyway I got there.
If I were you, I would choose a different folder.
AfxOpenFileDialog uses CURDIR if you pass an empty string.
The crash with FB 64 bit was my fault, but as it works with 32 bit and it worked with both 32/64 in Windows 7, I wasn't aware of it. This month I have started to use Windows 10. I will have to use CONST AS WSTRING from now to avoid these bugs.
If I were you, I would choose a different folder.
AfxOpenFileDialog uses CURDIR if you pass an empty string.
The crash with FB 64 bit was my fault, but as it works with 32 bit and it worked with both 32/64 in Windows 7, I wasn't aware of it. This month I have started to use Windows 10. I will have to use CONST AS WSTRING from now to avoid these bugs.
-
- Posts: 4292
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: AfxOpenFileDialog
I know. I tried an empty string as an experiment and mentioned it because it crashed.Jose Roca wrote:AfxOpenFileDialog uses CURDIR if you pass an empty string.
I am actually using KeyPath = AfxGetExePath, the application's folder.
This is the problem I have (from OPENFILENAME structure at MSDN)
1. If lpstrInitialDir has the same value as was passed the first time the application used an Open or Save As dialog box, the path most recently selected by the user is used as the initial directory. [1]
Pre Windows 7 we had:
1. If lpstrFile contains a path, that path is the initial directory.
2. Otherwise, lpstrInitialDir specifies the initial directory.
What I want is the pre Windows 7 algorithm.
I created three new folders for Alice, Bob and Jack. I got Alice to choose a target file well from the three folders. The task key request saw the target file folder open and not the application folder in accordance with [1]. Bob and Jack first use of Open go to the same target file folder.
I am sorry Microsoft but that is not an application memory but a global memory. Why would anyone want to use a global MRU?
PowerBASIC's 'DISPLAY OPENFILE' is proprietary and embedded in the compiler; which has not changed for a few years. Perhaps it is locked into the 'old way' of doing things.
I am tempted to write a PowerBASIC dll < Yer gotta laugh>
-
- Posts: 4292
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: AfxOpenFileDialog
I have just run this in PowerBASIC:
The first Open was alicex. I selected a file miles away.
The second Open was 'unphased' with where I had been and opened in alicex.
The third Open was given an empty string for the initial folder and it opened in the application's directory.
PowerBASIC is using old technology.
I have just read a post, going back a few years, where one guy described the Windows 7 algorithm as 'sagacity' - my very sentiment. One poor guy was getting hammered by his customers and it had nothing to do with him.
Code: Select all
#COMPILE EXE "test.exe"
#DIM ALL
#INCLUDE "win32api.inc"
%OfdStyleOpen = %OFN_EXPLORER OR %OFN_FILEMUSTEXIST OR %-OFN_HIDEREADONLY
FUNCTION PBMAIN () AS LONG
Local szFileName As AsciiZ * %Max_Path
Display OpenFile 0, , , "Test", "F:\alicex", Chr$( "All files (*.*)", 0, "*.*", 0), "", "", %OfdStyleOpen To szFileName
msgbox szfilename
Display OpenFile 0, , , "test", "F:\alicex", Chr$( "All files (*.*)", 0, "*.*", 0), "", "", %OfdStyleOpen To szFileName
msgbox szfilename
Display OpenFile 0, , , "Test", "", Chr$( "All files (*.*)", 0, "*.*", 0), "", "", %OfdStyleOpen To szFileName
msgbox szfilename
End Function
The second Open was 'unphased' with where I had been and opened in alicex.
The third Open was given an empty string for the initial folder and it opened in the application's directory.
PowerBASIC is using old technology.
I have just read a post, going back a few years, where one guy described the Windows 7 algorithm as 'sagacity' - my very sentiment. One poor guy was getting hammered by his customers and it had nothing to do with him.