- Marcar como novo
- Marcador
- Subscrever
- Silenciar
- Subscrever fonte RSS
- Destacar
- Imprimir
- Denunciar conteúdo inapropriado
In the app I'm building, I'm using the ever-familiar function InitializeOpen to launch Windows' Open dialog:
[bash]integer(4) function InitializeOpen() use *globals !to pass FileSpec to s.OpenInputFile() !Declare structure used to pass and receive attributes: ! Declare filter specification. This is a concatenation of pairs of null-terminated ! strings. The first string in each pair is the file type name, the second is a ! semicolon-separated list of file types for the given name. The list ends with ! a trailing null-terminated empty string. !Set filter to ASCII text file: character*(*),parameter :: FilterSpec = "APP Input File (*.txt)"C//"*.txt;*.TXT"C//""C !Declare string variable to return the file specification. Initialize with an initial !FileSpec, if any; otherwise null string: OFN%lStructSize = SizeOf(OFN) OFN%hWndOwner = NULL ! For non-console applications, set this to the ! hWnd of the Owner window, e.g., ghWndMain. OFN%hInstance = NULL ! For Win32 applications, you can set this ! to the appropriate hInstance OFN%lpStrFilter = loc(FilterSpec) OFN%lpStrCustomFilter = NULL OFN%nMaxCustFilter = 0 OFN%nFilterIndex = 1 !Specifies initial filter value (see URL) OFN%lpStrFile = loc(FileSpec) !FileSpec = Full Path FileName OFN%nMaxFile = SizeOf(FileSpec) !OFN%LPStrFileTitle = loc(szTitleName//".txt"C) !from OpenScribe2 OFN%nMaxFileTitle = 0 !OpenScribe 2 sets this to 25 OFN%lpStrInitialDir = NULL !Use Windows default directory !!OFN%lpStrInitialDir = 'H:' !Use my H-Drive OFN%lpStrTitle = loc(""C) !OpenScribe2 sets this to null OFN%Flags = OFN_PathMustExist !OpenScribe2 sets this to null !OFN%NFileOffset = NULL !from OpenScribe2 !OFN%NFileExtension =NUL !from OpenScribe2 OFN%lpStrDefExt = loc("txt"C) !OFN%LCustData = NULL !from OpenScribe2 OFN%lpFNHook = NULL OFN%lpTemplateName = NULL InitializeOpen = 1 end function InitializeOpen[/bash]
As a default, when a file is opened, the app. points to C:\\My Documents.
After initially browsing to a more ideal directory, I'd like to track browsing information so that, once a file is selected, the app remembers this directory and:
A) returns there when the app is used the next time;
B) points there first when a file is saved (e.g., SaveAs)
Two lines in the function are noteworthy:
OFN%lpStrInitialDir = NULL !Use Windows default directory
OFN%lpStrInitialDir = 'H:\' !Use my H-Drive
The first one may have something to do w/ forcing the default to C:\\My Documents.
The second one seemed like one I could at least set to force it to look in my H-drive, yet when enabled, it triggers a nasty Windows error (which is why I have it commented it out).
It could be that I must use an INI file in conjunction w/ f.InitialzeOpen but I'm not sure how to configure.
Finally, I did search this forum and did not see this question posted by anyone.
Any thoughts/experience are appreciated.
- Marcas:
- Intel® Fortran Compiler
Link copiado
- Marcar como novo
- Marcador
- Subscrever
- Silenciar
- Subscrever fonte RSS
- Destacar
- Imprimir
- Denunciar conteúdo inapropriado
Once you have obtained the file you can extract the directory name and store it withinan application "saved" or global variable and this can be used as the initial setting for OFN%lpStrInitialDir next time round. If however you wish to save the name for use next time you run the application you could use the registry (rather than an INI file)to save settings for your program. (There are good examples of using the registry in Norman Lawerence's book Compaq Visual Fortran).
If one only needs to select a directory location (and not any particular file) it gets more problematic. The SHBrowseForFolder function can be used but unfortunately I don't think it is possible to set the initial directory using this. If any one knows how to then please let me know.
I hope this helps.
- Marcar como novo
- Marcador
- Subscrever
- Silenciar
- Subscrever fonte RSS
- Destacar
- Imprimir
- Denunciar conteúdo inapropriado
- Marcar como novo
- Marcador
- Subscrever
- Silenciar
- Subscrever fonte RSS
- Destacar
- Imprimir
- Denunciar conteúdo inapropriado
Once a path has been selected, it can then be pre-pended onto filenames to be opened, and saved in an INI file or in the registry for use with subsequent program invocations. If all file opens include the complete file path name, there will be no ambiguity about the "default" path (usually set to the location of the program's executable, which is also easily discoverable in Windows). Here is a wrapper function for selecting a path:
[bash]! Shows a folder chooser shell window. The path to the chosen ! folder is placed in folderPath, which is a character array of ! the specified length. Returns true if the user chose a folder, ! or false if the operation was cancelled. The title caption ! is specified as a string table resource ID in the optional ! argument title. ! LOGICAL FUNCTION CmnDlgChooseFolder (hwndParent, folderPath, title) USE ExtraWinTy USE ifcom, only: COMInitialize, COMUnInitialize IMPLICIT NONE INTEGER(HANDLE), INTENT(IN) :: hwndParent CHARACTER(LEN=*), INTENT(INOUT) :: folderPath INTEGER, INTENT(IN), OPTIONAL :: title ! stringtable id value TYPE(T_BROWSEINFO) :: bi CHARACTER(LEN=MAX_PATH) :: buffer INTEGER :: pidl ! pointer to id list INTEGER :: status, rval2 CHARACTER(LEN=200) :: titleBuffer INTEGER :: titleId TYPE(T_STRRET) :: rstring INTEGER :: IShellFolder_desktop CmnDlgChooseFolder = .FALSE. IF (PRESENT(title)) THEN titleId = title ELSE titleId = IDS_DEFAULT_CHOOSEFOLDER_TITLE END IF titleBuffer = STGet(titleId, 200) CALL COMInitialize (status) buffer = folderPath ! TYPE T_BROWSEINFO ! SEQUENCE ! INTEGER :: hwndOwner ! INTEGER :: pidlRoot ! INTEGER :: pszDisplayName ! INTEGER :: lpszTitle ! INTEGER :: ulFlags ! INTEGER :: lpfn ! INTEGER :: lParam ! INTEGER :: iImage ! END TYPE T_BROWSEINFO bi%hwndOwner = hwndParent bi%pidlRoot = NULL bi%pszDisplayName = LOC(buffer) bi%lpszTitle = LOC(titleBuffer) bi%ulFlags = BIF_RETURNONLYFSDIRS bi%lpfn = NULL bi%lParam = 0 bi%iImage = 0 ! SHBrowseForFolder returns an item identifier list. pidl = SHBrowseForFolder (bi) IF (pidl /= 0) THEN SELECT CASE (windows_version) ! Win95 and Win98 CASE (VER_PLATFORM_WIN32_WINDOWS) ! We need to ask the shell to decode the item identifier ! list into a parseable name. First, retrieve an IShellFolder ! COM interface handle to the desktop folder. rval2 = SHGetDesktopFolder(LOC(IShellFolder_desktop)) IF (rval2 == NOERROR) THEN ! Use the GetDisplayNameOf method to convert the item ! identifier list into a string. The SHGDN_FORPARSING flag ! indicates we want the parseable path, not the general- ! purpose display label. rstring%uType = STRRET_CSTR rval2 = IShellFolder_GetDisplayNameOf (IShellFolder_desktop,& pidl, & SHGDN_FORPARSING, & LOC(rstring) ) IF (rval2 == NOERROR) THEN folderPath = rstring%cStr CmnDlgChooseFolder = .TRUE. END IF END IF ! WinNT, Win2K, WinXP CASE (VER_PLATFORM_WIN32_NT) CmnDlgChooseFolder = SHGetPathFromIDList (pidl, folderPath) END SELECT CALL CoTaskMemFree (pidl) END IF CALL COMUnInitialize () END FUNCTION CmnDlgChooseFolder [/bash]
- Marcar como novo
- Marcador
- Subscrever
- Silenciar
- Subscrever fonte RSS
- Destacar
- Imprimir
- Denunciar conteúdo inapropriado
Thanks Paul,
What is in the ExtraTy module? Does it form part of standard modules or is it user defined?
- Marcar como novo
- Marcador
- Subscrever
- Silenciar
- Subscrever fonte RSS
- Destacar
- Imprimir
- Denunciar conteúdo inapropriado
Sorry for the omission. ExtraWinTy contains Win32 data type definitions which were omitted from the original set supplied with DVF, and this list used to be very long. As DVF -> CVF -> IVF has evolved, the missing types have mostly been filled in, so this module has shrunk. I haven't checked whether these items are now supplied with IVF; at any rate, here they are:
[bash]MODULE ExtraWinTy
USE ifwinty
TYPE T_BROWSEINFO
SEQUENCE
INTEGER(HANDLE) :: hwndOwner
INTEGER :: pidlRoot
INTEGER :: pszDisplayName
INTEGER :: lpszTitle
INTEGER :: ulFlags
INTEGER :: lpfn
INTEGER(fLPARAM) :: lParam
INTEGER :: iImage
END TYPE T_BROWSEINFO
!typedef struct _STRRET
!{
! UINT uType; // One of the STRRET_* values
! union
! {
! LPWSTR pOleStr; // OLESTR that will be freed
! LPSTR pStr; // ANSI string that will be freed (needed?)
! UINT uOffset; // Offset into SHITEMID
! char cStr[MAX_PATH]; // Buffer to fill in (ANSI)
! } DUMMYUNIONNAME;
!} STRRET, *LPSTRRET;
TYPE T_STRRET
SEQUENCE
INTEGER :: uType
CHARACTER(LEN=MAX_PATH) :: cStr
END TYPE T_STRRET
END MODULE ExtraWinTy
[/bash]
- Marcar como novo
- Marcador
- Subscrever
- Silenciar
- Subscrever fonte RSS
- Destacar
- Imprimir
- Denunciar conteúdo inapropriado
Thanks again for that, however I don't seem to be able to resolve some of the functions and constants used in the routine using IVF 11.1.056. Namely:
C:\Win\FEM2000\Windows\w32_GetPath.f90(104): error #6404: This name does not have a type, and must have an explicit type. [BIF_RETURNONLYFSDIRS]
C:\Win\FEM2000\Windows\w32_GetPath.f90(110): error #6404: This name does not have a type, and must have an explicit type. [SHBROWSEFORFOLDER]
C:\Win\FEM2000\Windows\w32_GetPath.f90(122): error #6404: This name does not have a type, and must have an explicit type. [SHGETDESKTOPFOLDER]
C:\Win\FEM2000\Windows\w32_GetPath.f90(129): error #6404: This name does not have a type, and must have an explicit type. [STRRET_CSTR]
C:\Win\FEM2000\Windows\w32_GetPath.f90(132): error #6404: This name does not have a type, and must have an explicit type. [SHGDN_FORPARSING]
C:\Win\FEM2000\Windows\w32_GetPath.f90(130): error #6404: This name does not have a type, and must have an explicit type. [ISHELLFOLDER_GETDISPLAYNAMEOF]
C:\Win\FEM2000\Windows\w32_GetPath.f90(143): error #6404: This name does not have a type, and must have an explicit type. [SHGETPATHFROMIDLIST]
where are these defined in your code?
- Marcar como novo
- Marcador
- Subscrever
- Silenciar
- Subscrever fonte RSS
- Destacar
- Imprimir
- Denunciar conteúdo inapropriado
Thanks for all your replies. It seems since DannyCat's first reply, we got way off "in the weeds". I should have weighed back in sooner.
Let's step back to DannyCat's suggestion:
Once you have obtained the file you can extract the directory name and store it withinan application "saved" or global variable and this can be used as the initial setting for OFN%lpStrInitialDir next time round.
This shows we're on the same track as this was precisely the line I pointed to. But please note what I said:
The second one seemed like one I could at least set to force it to look in my H-drive, yet when enabled, it triggers a nasty Windows error (which is why I have it commented it out).
Before we attempt to do DannyCat's suggestion, it seems like the following line (or something close to it) should take me to my H-drive:
OFN%lpStrInitialDir = 'H:'
Why does it trigger the app-stopping error? (It compiles fine.) Is the syntax wrong? Again, I have found no guidance on this (using a customized direct to a particular directory in the line noted in this function), seeming to suggest the the world - or most of it - is happy with the default line that takes you to MyDocuments (i.e., OFN%lpStrInitialDir = NULL). Pardon my cynicism.
Assuming this could work, of course, the rest of DannyCat's logic makes sense (exception noted below). Theoretically, I could - once I get to the file I'm seeking - capture its directory by parsing the full file spec.
As I understand the rest of DCat's suggestion ("... store it withinan application "saved" or global variable and this can be used as the initial setting for OFN%lpStrInitialDir next time round."), I predict a problem. Yes, the directory can be stored off into a global variable but its contents would only be available while the app is running. When the app is loaded next time, that cell is empty. Maybe I'm missing something.
To be clear, I'm definitely not interested in "remembering" the file name - only the working directory.
IF I could get the offending line I cited above to work within f.InitializeOpen(), I believe I could extract the full-path directory name and write it to an INI file, which I could read when the app is launched each time. This would seem loads easier than the complicated code Paul Curtis kindly provided.
Again, I appreciate all the offerings here. I'm looking for the simplest way to "get there".
- Marcar como novo
- Marcador
- Subscrever
- Silenciar
- Subscrever fonte RSS
- Destacar
- Imprimir
- Denunciar conteúdo inapropriado
- Marcar como novo
- Marcador
- Subscrever
- Silenciar
- Subscrever fonte RSS
- Destacar
- Imprimir
- Denunciar conteúdo inapropriado
Are you trying to keep track of just the directory after selecting a file, or after just selecting a folder? If it is the former then common dialog procedure that is used in f.InitializeOpen example can be used. Once the file has been selected the full path is returned in string assignede.g. OFN%lpstrFile = loc(filename)and the offset to the filename itself is returned in OFN%nFileOffset. Therefore directory name will be in filename(1:OFN%nFileOffset). You can then save this in the application or registry or INI file, - where ever you wish!
Be aware that strings need to be NULL terminated.
If you are just selecting the directory then you can't use file open/save common dialogs to do this, you need to use the shell routines kindly provided by Paul.One of thereasons Paul joined this discussion was to answer my question regarding setting the initial directiory in the SHBrowseForFolder function.
You don't seem sure what you want to do and consequently we have difficultly helping you with your issues.
PS Thanks again to Paul for the code, I can make good use of this and ditch my 'orrible C code version at last.
- Marcar como novo
- Marcador
- Subscrever
- Silenciar
- Subscrever fonte RSS
- Destacar
- Imprimir
- Denunciar conteúdo inapropriado
All your strings must be C-strings and therefore 'null' terminated.
Thus ='H:' is wrong,
whereas = 'H:'c is correct
(='H:'//char(0) is also correct.).
Your error is caused by your not terminating the directory string correctly.
I repeat that, in my experience, susequent calls to the common dialog GETOPENFILENAME open the windowat the directory selected by the previous call, so it 'remembers' where it was (perhaps by using the registry), so long as the user selects a file and presses the 'OK' button. This is confirmed by a statement on the Microsoft website that 'When the user selects at least one file and clicks the OK button, the process' current working directory is changed to the directory contain the file(s) being opened'
- Marcar como novo
- Marcador
- Subscrever
- Silenciar
- Subscrever fonte RSS
- Destacar
- Imprimir
- Denunciar conteúdo inapropriado
Thanks for additional replies, one & all. I appreciate the code you provided, Paul. Not quite sure what to do w/ it but it seems like one hell of piece of work.
To DCat, as I hope I made clear, let me explain again. I run the app for the "first time" and, when I invoke f.InitializeOpen, it's OK if it takes me to MyDocuments. I browse from within that dialog and find the file I'm seeking (in, say, some directory on an entirely different drive). The app is closed. When I later re-launch the app and invoke theOpenFile dialog (via f.InitializeOpen), I want it to take me to the very subdirectory that holds the file I opened last time! Nothing more complicated than that. The other info you provided:
Once the file has been selected the full path is returned in string assignede.g. OFN%lpstrFile = loc(filename)and the offset to the filename itself is returned in OFN%nFileOffset. Therefore directory name will be in filename(1:OFN%nFileOffset).
was very useful.
The first part of Anthony's reply:
All your strings must be C-strings and therefore 'null' terminated.
Thus ='H:' is wrong,
whereas = 'H:'c is correct
(='H:'//char(0) is also correct.).
Your error is caused by your not terminating the directory string correctly.
was very interesting and - at first blush - seemed like it hit at the core of my initial question. I tried using this exact strnig:
OFN%lpStrInitialDir = 'H:'c
presumably to force it - at least initially -to go to the H-drive (root directory) when the OpenDialog is launched. But at compilation, I get this strange message:
Warning: The backslash character and following character is not a valid escape sequence
The backslash character & the following character ('); nothing wrong with the (') preceding the H.
I would like to know your thoughts on this.
Now, the last part of Anthony's reply (reiterating his earler coment) was the most important:
I repeat that, in my experience, susequent calls to the common dialog GETOPENFILENAME open the windowat the directory selected by the previous call, so it 'remembers' where it was (perhaps by using the registry), so long as the user selects a file and presses the 'OK' button.
Firstly, to his earler question (I presume you use OFN as an argument to GETOPENFILENAME?), YES.
Here's the best part. Just now, though never before, I ran f.InitializeOpen w/ the default
OFN%lpStrInitialDir = NULL
and guess what? It behaved exactly as you predicted (and as I wished): it returns to where it was before.
So, I guess all's well that ends well.
- Marcar como novo
- Marcador
- Subscrever
- Silenciar
- Subscrever fonte RSS
- Destacar
- Imprimir
- Denunciar conteúdo inapropriado
OOPs! My error. I forgot that when the compiler can see the backslash in a string such as 'H:'cterminated wtih the 'c characters (which identifies it as a C-string, C-strings are constructed and stored by the compiler, which adds the null character to the end of the string and stores it), it will treat the combinationof backslash+next characteras an escape sequence. All you need to do is double up the back slash thus
OFN%lpStrInitialDir = 'H:\'c
Alternatively, the following should work
OFN%lpStrInitialDir = 'H:'//char(0)
since, without the terminating 'C the compiler does not view the RHS as a C-string, and hence does not take the sight of a backslash as the start of an escape sequence pair of characters, and does not automatically add a terminating null character to the end of the character string.
Always remember when specifying the length of a character string, to allow an extra character for the addition of the null character to the end, if you are specifying a C-string as an argument. Similarly, when creating a buffer to receive a C-string, allow for the null character that will be used to terminate the string.
Windows API functions invariable require and return C-strings, strings terminated with at least one (sometimes two) null characters

- Subscrever fonte RSS
- Marcar tópico como novo
- Marcar tópico como lido
- Flutuar este Tópico para o utilizador atual
- Marcador
- Subscrever
- Página amigável para impressora