Hi Charles,
Speaking of FileDialogs, could you include the ability to select multiple files and have an array that contains the DIR and the files that have been selected?
I found this example that, given my still lack of knowledge of O2, I can't translate.
Could this be a nice implementation?
ofn.Flags = OFN_EXPLORER | OFN_ALLOWMULTISELECT;
if (GetOpenFileName(&ofn)==TRUE)
{
printf("files selected\n");
char* ptr = ofn.lpstrFile;
ptr[ofn.nFileOffset-1] = 0;
printf("Directory path: %s\n", ptr);
ptr += ofn.nFileOffset;
while (*ptr)
{
fCount++;
printf("File: %i %s\n", fCount, ptr);
ptr += (lstrlen(ptr)+1);
}
printf("\n");
printf("selected %i files\n", fCount);
}
else
{
printf("no files selected\n");
}
break;
Hi Nicola,
You can select only 1 block of files on each dialog call, but not multiple blocks:
uses FileDialogs
uses console
OPENFILENAME ofn
char FileBuf[2048]
int FileBufLen=2048
'
ofn.lStructSize = sizeof(OPENFILENAME)
'ofn.hwndOwner = hWnd
ofn.hInstance = GetModuleHandle
'ofn.lpstrFilter = filter
ofn.lpstrCustomFilter = null
ofn.nMaxCustFilter = 0
'ofn.nFilterIndex = 1
ofn.lpstrFile = FileBuf 'coupling to char buffer
ofn.nMaxFile = FileBufLen
ofn.lpstrFileTitle = null
ofn.nMaxFileTitle = 0
'ofn.lpstrInitialDir = idir
ofn.lpstrTitle = "Multi-Select"
ofn.Flags = OFN_EXPLORER | OFN_ALLOWMULTISELECT
ofn.nFileOffset = 0
ofn.nFileExtension = 0
ofn.lpstrDefExt = null
ofn.lCustData = 0
ofn.lpfnHook = 0
ofn.lpTemplateName = null
if GetOpenFileName ofn
print "files selected" cr
char*pt
@pt=@FileBuf
int j
int fcount
j=ofn.nFileOffset
print "Directory path: "+left(pt,j)+cr
@pt+=j
while asc(pt)
fCount++
print "File: " fCount+tab+pt+cr
@pt+=len(pt)+1
wend
print cr
print "selected " fcount " files"+cr
else
print "no files selected"+cr
endif
wait
Great Charles. :)
I tried putting this in the FileDialogs. I got this...
Unfortunately, I don't know how to get an array to return from a function.
'no case sensitivity
#ifndef HAS_COREWIN
uses corewin
#endif
% OFN_READONLY 0x00000001
% OFN_OVERWRITEPROMPT 0x00000002
% OFN_HIDEREADONLY 0x00000004
% OFN_NOCHANGEDIR 0x00000008
% OFN_SHOWHELP 0x00000010
% OFN_ENABLEHOOK 0x00000020
% OFN_ENABLETEMPLATE 0x00000040
% OFN_ENABLETEMPLATEHANDLE 0x00000080
% OFN_NOVALIDATE 0x00000100
% OFN_ALLOWMULTISELECT 0x00000200
% OFN_EXTENSIONDIFFERENT 0x00000400
% OFN_PATHMUSTEXIST 0x00000800
% OFN_FILEMUSTEXIST 0x00001000
% OFN_CREATEPROMPT 0x00002000
% OFN_SHAREAWARE 0x00004000
% OFN_NOREADONLYRETURN 0x00008000
% OFN_NOTESTFILECREATE 0x00010000
% OFN_NONETWORKBUTTON 0x00020000
% OFN_NOLONGNAMES 0x00040000 '// force no long names for 4.x modules
% OFN_EXPLORER 0x00080000 '// new look commdlg
% OFN_NODEREFERENCELINKS 0x00100000
% OFN_LONGNAMES 0x00200000 '// force long names for 3.x modules
% OFN_ENABLEINCLUDENOTIFY 0x00400000 '// send include message to callback
% OFN_ENABLESIZING 0x00800000
% OFN_DONTADDTORECENT 0x02000000
% OFN_FORCESHOWHIDDEN 0x10000000 '// Show All files including System and hidden files
type OPENFILENAMEA
DWORD lStructSize
SYS hwndOwner
SYS hInstance
CHAR* lpstrFilter
CHAR* lpstrCustomFilter
DWORD nMaxCustFilter
DWORD nFilterIndex
CHAR* lpstrFile
DWORD nMaxFile
CHAR* lpstrFileTitle
DWORD nMaxFileTitle
CHAR* lpstrInitialDir
CHAR* lpstrTitle
DWORD Flags
WORD nFileOffset
WORD nFileExtension
CHAR* lpstrDefExt
LONG lCustData
SYS lpfnHook
CHAR* lpTemplateName
end type
typedef OPENFILENAMEA OPENFILENAME
Declare GetModuleHandle lib "kernel32.dll" alias "GetModuleHandleA" (optional char*n) as sys
Declare GetOpenFileName Lib "comdlg32.dll" Alias "GetOpenFileNameA" (OPENFILENAME*opfn) As sys
Declare GetSaveFileName Lib "comdlg32.dll" Alias "GetSaveFileNameA" (OPENFILENAME*opfn) As sys
Declare CommDlgExtendedError Lib "comdlg32.dll" () as dword
[font=Verdana, Arial, Helvetica, sans-serif][/font]
[font=Verdana, Arial, Helvetica, sans-serif]'FileDialog( $ iDir , $ filter ,$ title , % parent , flag )[/font]
Function FileDialog(string iDir,filter,Title,Name, sys Hwnd, Flags, a) As String
'===============================================================================
String Selex
OPENFILENAME ofn
int FileNameLen=2048
char FileName[2048]
if Name then FileName=Name
int retval
ofn.lStructSize = sizeof(OPENFILENAME)
ofn.hwndOwner = hWnd
ofn.hInstance = GetModuleHandle
ofn.lpstrFilter = filter
ofn.lpstrCustomFilter = null
ofn.nMaxCustFilter = 0
'ofn.nFilterIndex = 1
ofn.lpstrFile = FileName 'coupling to char buffer
ofn.nMaxFile = FileNameLen
ofn.lpstrFileTitle = null
ofn.nMaxFileTitle = 0
ofn.lpstrInitialDir = idir
ofn.lpstrTitle = title
ofn.Flags = flags
ofn.nFileOffset = 0
ofn.nFileExtension = 0
ofn.lpstrDefExt = null
ofn.lCustData = 0
ofn.lpfnHook = 0
ofn.lpTemplateName = null
sys retval
'
if a then
retval=GetSaveFileName(ofn)
else
retval=GetOpenFileName(ofn)
end if
'
'http://msdn.microsoft.com/en-us/library/windows/desktop/ms646916(v=vs.85).aspx
if retval
char*pt
@pt=@Filename
int j
int fcount
j=ofn.nFileOffset
Selex = left(pt,j)+";"
@pt+=j
while asc(pt)
fCount++
Selex=Selex+pt+";"
@pt+=len(pt)+1
wend
return fCount+";"+Selex
else
return ""
end if
End Function
Function GetFileName(string name, sys a, string filt="") as string
'=================================================================
'
'dir="OxygenBasic\demos\GUI"
'
sys hwnd
string filter,dir,title
'
if filt then
filter=filt
else
string sep=chr(0)
filter=
"all files"+sep+"*.*"+sep+
"text"+sep+"*.txt"+sep+
"basic"+sep+"*.bas;*.o2bas"+sep+
"include"+sep+"*.inc"+sep+
"header"+sep+"*.h"+sep+
sep
end if
'
if a then
title="Save File as"
else
title="Load File"
end if
'
sys flags = OFN_EXPLORER or OFN_OVERWRITEPROMPT or OFN_HIDEREADONLY
'
return FileDialog(dir,filter,title,name,hwnd,flags,a)
'
end function
'! SHBrowseForFolder Lib "shell32" (BrowseInfo *lpbi) as sys
'! SHGetPathFromIDList Lib "shell32" (sys pidList, sys lpBuffer) As sys
function BrowseCallbackProc(sys hwnd, int uMsg, sys wParam, lParam) as int, callback
====================================================================================
'Used with the BrowseForFolder function to select a default folder
% BFFM_ENABLEOK 0x465
% BFFM_SETSELECTION 0x466
% BFFM_SETSTATUSTEXT 0x464
% BFFM_INITIALIZED 1
% BFFM_SELCHANGED 2
% BFFM_VALIDATEFAILED 3
'
if uMsg = BFFM_INITIALIZED
SendMessage(hwnd, BFFM_SETSELECTION, uMsg, lParam )
endif
return 0 ' the function should always return 0
end function
type BrowseInfo
sys hWndOwner
sys pIDLRoot
sys pszDisplayName
sys lpszTitle
int ulFlags
sys lpfnCallback
sys lParam
int iImage
end type
'
function BrowseForDir(sys hWnd, string strTitle="", strInitDir="\" ) as string
===============================================================================
'% BIF_RETURNONLYFSDIRS = 1
'% BIF_EDITBOX = &H10
% MAX_PATH = 260
'
BrowseInfo tbi
tbi.hWndOwner = hwnd
tbi.lpszTitle = strptr(strTitle)
'tbi.ulFlags = BIF_RETURNONLYFSDIRS or BIF_EDITBOX
tbi.lpfnCallback = @BrowseCallbackProc
tbi.lParam = strptr(strInitDir)
sys h = SHBrowseForFolder(@tbi)
if h
char s[MAX_PATH]
SHGetPathFromIDList(h, s)
return s
endif
end function
'USAGE:
'sys hWnd=0
'print strDir = BrowseForDir(hWnd,"Select a Folder","c:\users")
The program to try I used this:
uses filedialogs_1
string sep=chr(0)
string filter="pdf"+sep+"*.pdf"
string tipOFN, f
sys tipo
int i
sub printout()
[font=Verdana, Arial, Helvetica, sans-serif] f=FileDialog("C:\",filter,tipofn,"", 0,tipo,0)[/font]
print f
end sub
tipOFN="OFN_EXPLORER or OFN_ALLOWMULTISELECT"
tipo=OFN_EXPLORER or OFN_ALLOWMULTISELECT
printout
tipOFN="OFN_EXPLORER"
tipo=OFN_EXPLORER
printout
ExitProcess(0)
tipOFN="OFN_EXPLORER or OFN_OVERWRITEPROMPT or OFN_HIDEREADONLY or OFN_ALLOWMULTISELECT"
tipo=OFN_EXPLORER or OFN_OVERWRITEPROMPT or OFN_HIDEREADONLY or OFN_ALLOWMULTISELECT
printout
tipOFN="OFN_OVERWRITEPROMPT or OFN_HIDEREADONLY or OFN_ALLOWMULTISELECT"
tipo=OFN_OVERWRITEPROMPT or OFN_HIDEREADONLY or OFN_ALLOWMULTISELECT
printout
tipOFN="OFN_OVERWRITEPROMPT or OFN_ALLOWMULTISELECT"
tipo=OFN_OVERWRITEPROMPT or OFN_ALLOWMULTISELECT
printout
tipOFN="OFN_EXPLORER or OFN_ALLOWMULTISELECT"
tipo=OFN_EXPLORER or OFN_ALLOWMULTISELECT
printout
tipOFN="OFN_ALLOWMULTISELECT or OFN_LONGNAMES"
tipo=OFN_ALLOWMULTISELECT or OFN_LONGNAMES
printout
Hi Nicola,
Because it is customized, I would recommend exposing the full OPENFILENAME structure in your application, and process the result directly. Then you can set up a string array to capture the filenames instead of printing them.
But to return an array of strings, from any procedure, deploy the string array param like this:
procedure foo(string *s[])
...
end procedure
dim string f[100]
foo f[]
Thanks Charles.
I'm probably saying a lot of nonsense, but wouldn't it be possible to do the opposite? That is, return the pointer to an array worked in the procedure:
procedure foo()
dim s[100]
...
return *s[]
end procedure
dim string f
*f = foo[]
It is possible but there's trouble from the (string) garbage collector so you have to use bstrings and dispose of them yourself.
Also, when working in a multi-threaded application, it is not safe to pass string arrays naively.
Do you want to go down this technical rabbit hole? :)
Of course not...
It's better not to end up like a rabbit. ???
That's why my idea of making a string easily workable with a split return, using a separator character that can't be used for a filename.
The string contains the number of selected files in the first place, then the Directory and then the names of the files.
fun with GetOpenFileName
// GetOpenFileName with the OFN_ALLOWMULTISELECT flag
#define OFN_READONLY 0x00000001
#define OFN_OVERWRITEPROMPT 0x00000002
#define OFN_HIDEREADONLY 0x00000004
#define OFN_NOCHANGEDIR 0x00000008
#define OFN_SHOWHELP 0x00000010
#define OFN_ENABLEHOOK 0x00000020
#define OFN_ENABLETEMPLATE 0x00000040
#define OFN_ENABLETEMPLATEHANDLE 0x00000080
#define OFN_NOVALIDATE 0x00000100
#define OFN_ALLOWMULTISELECT 0x00000200
#define OFN_EXTENSIONDIFFERENT 0x00000400
#define OFN_PATHMUSTEXIST 0x00000800
#define OFN_FILEMUSTEXIST 0x00001000
#define OFN_CREATEPROMPT 0x00002000
#define OFN_SHAREAWARE 0x00004000
#define OFN_NOREADONLYRETURN 0x00008000
#define OFN_NOTESTFILECREATE 0x00010000
#define OFN_NONETWORKBUTTON 0x00020000
#define OFN_NOLONGNAMES 0x00040000
#define OFN_EXPLORER 0x00080000
#define OFN_NODEREFERENCELINKS 0x00100000
#define OFN_LONGNAMES 0x00200000
#define OFN_ENABLEINCLUDENOTIFY 0x00400000
#define OFN_ENABLESIZING 0x00800000
#define OFN_DONTADDTORECENT 0x02000000
#define OFN_FORCESHOWHIDDEN 0x10000000
#define HWND_DESKTOP 0x00000000
#define FNERR_BUFFERTOOSMALL 0x00003003
#define NulChar chr(0)
typedef struct tagOFNA { // structure used by GetOpenFileName()
dword lStructSize;
sys hwndOwner;
sys hInstance;
sys lpstrFilter;
sys lpstrCustomFilter;
dword nMaxCustFilter;
dword nFilterIndex;
sys lpstrFile;
dword nMaxFile;
sys lpstrFileTitle;
dword nMaxFileTitle;
sys lpstrInitialDir;
sys lpstrTitle;
dword Flags;
word nFileOffset;
word nFileExtension;
sys lpstrDefExt;
sys lCustData;
sys lpfnHook;
sys lpTemplateName;
} OPENFILENAMEA, *LPOPENFILENAMEA;
! GetModuleHandle lib "kernel32.dll" alias "GetModuleHandleA"(optional char*n) as sys
! GetOpenFileName lib "comdlg32.dll" alias "GetOpenFileNameA"(OPENFILENAMEA*opfn) as sys
! CommDlgExtendedError lib "comdlg32.dll" alias "CommDlgExtendedError"() as dword
'_____________________________________________________________________________
function FileDialog(sys hWin, string title, dir, filter) as string
string filename = nuls 1048576 'one meg for multi select filenames, try 5 to see error message.
OPENFILENAMEA ofn
ofn.lStructSize = sizeof(OPENFILENAMEA)
ofn.hwndOwner = hWin
ofn.hInstance = GetModuleHandle(NulChar)
ofn.lpstrFilter = strptr filter
ofn.lpstrCustomFilter = null
ofn.nMaxCustFilter = 0
ofn.nFilterIndex = 1
ofn.lpstrFile = strptr filename
ofn.nMaxFile = len(filename)
ofn.lpstrFileTitle = null
ofn.nMaxFileTitle = 0
ofn.lpstrInitialDir = strptr dir
ofn.lpstrTitle = strptr title
ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_FORCESHOWHIDDEN
if GetOpenFileName(ofn) then 'zero mean success
long charpos = instr(filename, NulChar & NulChar ) 'find ending double nul character
if charpos then filename = left(filename, charpos) 'remove ending zero characters
long index
for index = 1 to len(filename)
if asc(filename, index) = NulChar then asc(filename, index) = 13 'replace characters zero with CR
next
return filename 'job done, first line is folder, other are filename
else 'failed
long LastError = CommDlgExtendedError()
if LastError = 0 then
print "Cancelled by user !"
elseif LastError = FNERR_BUFFERTOOSMALL then
'If the buffer is too small, the function returns FALSE and the CommDlgExtendedError function returns FNERR_BUFFERTOOSMALL.
'In this case, the first two bytes of the lpstrFile buffer contain the required size, in bytes or characters.
dim NeededLenght as word at strptr filename
print "CommDlgExtendedError: FNERR_BUFFERTOOSMALL mean ofn.lpstrFile buffer is too small ! Needed minimum " & str(NeededLenght) & " bytes." '12291
else
print "CommDlgExtendedError: error #" & str(LastError)
endif
endif
end function
'_____________________________________________________________________________
'code start...
sys hWin = HWND_DESKTOP
string title = "Test File Opening Dialog"
string dir = "."
string filter = "all (*.*)" & NulChar & "*.*" & NulChar &
"text (*.txt)" & NulChar & "*.txt" & NulChar &
"basic (*.bas)" & NulChar & "*.bas;*.o2bas" & NulChar &
"include (*.inc)" & NulChar & "*.inc" & NulChar &
"header (*.h)" & NulChar & "*.h" & NulChar & NulChar
print FileDialog(hWin, title, dir, filter)
'_____________________________________________________________________________
'
Your code appears to be a Windows API-based file dialog function, likely written in a language resembling BASIC or a similar dialect. It's using the OPENFILENAME structure and related functions to create a file open dialog. Here's a breakdown and analysis of key parts of your code:
Constant Definitions: The #define statements define constants typically used with the OPENFILENAME structure, providing various options for the file dialog (like OFN_READONLY, OFN_FILEMUSTEXIST, etc.).
Structure Definition: The tagOFNA struct defines the OPENFILENAMEA structure, which is used by the GetOpenFileName function. This structure holds information the function needs to initialize the dialog.
Function Declarations: The ! prefixed lines declare external functions from Windows DLLs (kernel32.dll and comdlg32.dll). These are likely function prototypes for calling these DLL functions.
FileDialog Function: This is the main function that creates and shows the file dialog. It initializes an OPENFILENAMEA structure and calls GetOpenFileName. If successful, it processes the returned file name(s), replacing NUL characters with carriage returns (CR).
Error Handling: The code checks the return value of GetOpenFileName and uses CommDlgExtendedError to get additional error information if needed.
Buffer Size: The filename string is initially allocated 1 MB, which is quite large but necessary for multi-select scenarios. However, this could be overkill for single file selection and could be optimized.
Test Code: The last part of the script sets up parameters for the FileDialog function and calls it.
Potential Issues and Improvements:
Buffer Size: While 1 MB should be more than sufficient, consider if such a large buffer is always necessary, or if it could be adjusted based on expected usage.
Error Handling: The error handling is basic. It might be beneficial to provide more detailed error messages or handling, especially for other error codes that CommDlgExtendedError might return.
Code Style: The mix of lowercase and uppercase, along with the use of 'sys' and 'string' types, suggests a particular coding style or language requirement. Ensure consistency throughout the code.
Documentation: Adding comments explaining each part of the code, especially around the flags used in the OPENFILENAMEA structure, would make the code more readable and maintainable.
Return Value of GetOpenFileName: The comment zero mean success seems incorrect. Usually, a non-zero value indicates success in Windows API calls. Double-check this.
Compatibility: Ensure that this code is compatible with the versions of Windows you intend to support, especially with regard to the 32-bit and 64-bit differences and any potential Unicode issues.
Overall, the code seems well-structured for its intended purpose. The use of the Windows API is appropriate for a file dialog in a Windows environment, and the function appears to cover the essential features needed for file selection.
Theo
Don't get me wrong ..but why all this long explanation
it is stuff/ things we already know and documentation
we can find on many places..
I build this FileDialog 10 years ago in my include file
Aurel,
I do think that Theo really like AI. ~:-)
Theo,
Here is what Bill say about the GetOpenFileName() (https://learn.microsoft.com/en-us/windows/win32/api/commdlg/nf-commdlg-getopenfilenamea)'s return value.
-If the user specifies a file name and clicks the OK button, the return value is nonzero.
-If the user cancels or closes the Open dialog box or an error occurs, the return value is zero.