[Solved - OS fault] File Requesters open in previous path

Windows specific forum
User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

[Solved - OS fault] File Requesters open in previous path

Post by kenmo »

Spin off this thread: viewtopic.php?f=13&t=73886

This is a minor "bug" behavior I have noticed for a while but never investigated.
The File Requesters open in the last selected directory, even if you give a specific directory argument.

It's either PureBasic or Windows, caching the directory in some way? Can it be overridden?

Code: Select all

CompilerIf (#PB_Compiler_Debugger)
  MessageRequester("Instructions",
      "1. Compile this to an EXE (Desktop/test.exe or similar)" + #LF$ +
      "2. Run it" + #LF$ + 
      "3. When the first requester opens, choose a file in a different folder" + #LF$ +
      "4. The second requester opens in that folder (bug)" + #LF$ +
      "5. Run the program again, and it opens in that changed folder (bug)" + #LF$ + #LF$ +
      "Some sort of Windows caching issue ??" )
CompilerElse

  ;CurrentDir.s = GetCurrentDirectory()
  ;MessageRequester("Current Dir", CurrentDir)
  
  ProgramDir.s = GetPathPart(ProgramFilename())
  MessageRequester("Program Dir", ProgramDir)
  
  OpenFileRequester("Title", ProgramDir, "All Files|*.*", 0)
  OpenFileRequester("Title", ProgramDir, "All Files|*.*", 0)

CompilerEndIf
Last edited by kenmo on Tue Nov 05, 2019 8:45 pm, edited 1 time in total.
User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

Re: File Requesters open in previous path

Post by kenmo »

This seems to be why. Can it be overridden?
https://docs.microsoft.com/en-us/window ... nfilenamea
lpstrInitialDir

Type: LPCTSTR

The initial directory. The algorithm for selecting the initial directory varies on different platforms.

Windows 7:

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.
Otherwise, if lpstrFile contains a path, that path is the initial directory.
Otherwise, if lpstrInitialDir is not NULL, it specifies the initial directory.
If lpstrInitialDir is NULL and the current directory contains any files of the specified filter types, the initial directory is the current directory.
Otherwise, the initial directory is the personal files directory of the current user.
Otherwise, the initial directory is the Desktop folder.

Windows 2000/XP/Vista:

If lpstrFile contains a path, that path is the initial directory.
Otherwise, lpstrInitialDir specifies the initial directory.
Otherwise, if the application has used an Open or Save As dialog box in the past, the path most recently used is selected as the initial directory. However, if an application is not run for a long time, its saved selected path is discarded.
If lpstrInitialDir is NULL and the current directory contains any files of the specified filter types, the initial directory is the current directory.
Otherwise, the initial directory is the personal files directory of the current user.
Otherwise, the initial directory is the Desktop folder.
I guess it's been a Windows complaint for 8+ years :)
https://social.msdn.microsoft.com/Forum ... w-to-avoid
User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

Re: File Requesters open in previous path

Post by kenmo »

Sorry to reply to my own question, but I figured out a workaround that seems to work on Windows 10.
Just 2 procedures + 1 call, before you open any requesters.

Windows 7+ remembers the first DefaultDir you call. If you call that DefaultDir again, Windows ignores it and opens in the last selected path.

Apparently this was an attempt to solve lazy programs which don't track the user's selected path, when the user expects it to. But it broke other programs' intended behavior.

So a workaround is... first open DefaultDir to a temporary folder you'll likely never call again. Then future DefaultDir will work as expected.
Preferably, open DefaultDir as hidden as possible, in this workaround it closes it as soon as possible.

Code: Select all

;-
;- -----------------------------
;- WORKAROUND START
;-

CompilerIf (#PB_Compiler_OS = #PB_OS_Windows)
Procedure _PrepareFileRequester(*OFN.OPENFILENAME)
  OpenFileRequester(PeekS(*OFN\lpstrTitle), PeekS(*OFN\lpstrInitialDir), "", 0)
EndProcedure

Procedure PrepareFileRequester()
  Static Prepared.i = #False
  If (Not Prepared)
    Prepared = #True
    If (OSVersion() >= #PB_OS_Windows_7)
      Protected Title.s = " "
      While (FindWindow_(#Null, @Title))
        Title + " "
      Wend
      Protected TempDir.s = GetTemporaryDirectory() + "." + Str(Date())
      CreateDirectory(TempDir)
      Protected OFN.OPENFILENAME
      OFN\lpstrTitle      = @Title
      OFN\lpstrInitialDir = @TempDir
      Protected *Thread = CreateThread(@_PrepareFileRequester(), @OFN)
      Protected *Window
      Repeat : Delay(0) : *Window = FindWindow_(#Null, @Title) : Until (*Window)
      SendMessage_(*Window, #WM_CLOSE, 0, 0)
      Repeat : Delay(0) : Until (Not IsThread(*Thread))
      DeleteDirectory(TempDir, "")
    EndIf
  EndIf
EndProcedure

CompilerElse
Macro PrepareFileRequester()
  ;
EndMacro
CompilerEndIf

;-
;- WORKAROUND END
;- -----------------------------


;-
;-

CompilerIf (#PB_Compiler_Debugger)
  MessageRequester("Instructions",
      "1. Compile this to an EXE (Desktop/test.exe or similar)" + #LF$ +
      "2. Run it" + #LF$ +
      "3. When the first requester opens, choose a file in a different folder" + #LF$ +
      "4. The second requester opens in that folder (bug)" + #LF$ +
      "5. Run the program again, and it opens in that changed folder (bug)" + #LF$ + #LF$ +
      "Some sort of Windows caching issue ??" )
CompilerElse
 
  ProgramDir.s = GetPathPart(ProgramFilename())
  MessageRequester("Program Dir", ProgramDir)
  
  PrepareFileRequester() ;- ADD THIS ONE LINE
  
  OpenFileRequester("Title", ProgramDir, "All Files|*.*", 0)
  SaveFileRequester("Title", ProgramDir, "All Files|*.*", 0)

CompilerEndIf
;-
breeze4me
Enthusiast
Enthusiast
Posts: 527
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: File Requesters open in previous path

Post by breeze4me »

User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

Re: File Requesters open in previous path

Post by kenmo »

Not for me (tested on Win 7 just now).
It uses the OpenFileName_() API, which has the issue, exactly what is discussed in that MSDN link.
https://social.msdn.microsoft.com/Forum ... w-to-avoid

Maybe you misunderstand the (subtle) issue.
Take Justin's code and replace the bottom with this:

Code: Select all

;- #TEST
; compile an EXE (with a unique name) and run it
; the first dialog opens in Desktop (correct)
; the second dialog ignores Desktop and opens in whatever folder you last selected
GetOpenFileName(0, "", GetUserDirectory(#PB_Directory_Desktop), "All files (*.*)|*.*|MP3 (*.mp3)|*.mp3", 1, "", "", #Null, @iFilter, @fileTitle$, #OFN_FILEMUSTEXIST)
GetOpenFileName(0, "", GetUserDirectory(#PB_Directory_Desktop), "All files (*.*)|*.*|MP3 (*.mp3)|*.mp3", 1, "", "", #Null, @iFilter, @fileTitle$, #OFN_FILEMUSTEXIST)

This answer from that link is interesting, maybe I'll experiment with this, and if it works, suggest it to the PB team as a fix.
You should be able to put the full path in the lpstrFile field of the OPENFILENAME struct (and leave lpstrInitialDir empty). This is generally what is used in the "Save As" scenario for instance, where the dialog starts in the same folder as the item you are "save as"ing exists. You'll probably need to supply a filename too though (which will appear in the filename box).
EDIT 2019-11-05 This seems to work (specify a lpstrFile and it will always open in that folder, the OS won't ignore your specification) BUT it seems you MUST specify a file name (even ".") which is a small annoyance, you can't open with an empty name field.
Last edited by kenmo on Tue Nov 05, 2019 8:48 pm, edited 1 time in total.
breeze4me
Enthusiast
Enthusiast
Posts: 527
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: File Requesters open in previous path

Post by breeze4me »

OK, you are right. The compiled exe doesn't work.
Little John
Addict
Addict
Posts: 4527
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: [Solved - OS fault] File Requesters open in previous pat

Post by Little John »

kenmo, thank you for the detailed research, and for the workaround!
Much appreciated.
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: [Solved - OS fault] File Requesters open in previous pat

Post by IdeasVacuum »

Hi kenmo

I have seen this issue before and assumed it was a PB glitch. Very presumptuous of MS for sure.

Good workaround, thanks for sharing.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

Re: [Solved - OS fault] File Requesters open in previous pat

Post by kenmo »

About the issue:

Basically, Windows is trying to be clever to cover up for old, user-unfriendly programs.
If you open a requester in the same directory multiple times, Windows thinks "oh, you don't *really* want to open in that directory, you must want the last folder the user browsed to!"
Of course this is not always what we want! We can implement that ourselves.

About my workaround:

If it's not clear, what it does is open a one-time Requester in a temporary directory, and immediately close it (hopefully without seeing it).
As long as you don't open a Requester in that path in the rest of the program, Windows won't do this behavior where it assumes you want the user's last selected folder.

About threading:

The code I posted above opens a Requester from a child thread, and closes it from the main thread.
In real testing, since that post, it seems to work more reliably if the Requester is opened from the main thread (and closed from a child thread).
I will post my updated code later today.
Little John
Addict
Addict
Posts: 4527
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: [Solved - OS fault] File Requesters open in previous pat

Post by Little John »

Hi kenmo, your code works fine here (on Windows 10).

However, when using your workaround in a program, some care must be taken:
If the program has an open window when PrepareFileRequester() is called, then the program hangs (because FindWindow_() does not work as expected anymore).

Code: Select all

ProgramDir.s = GetPathPart(ProgramFilename())
MessageRequester("Program Dir", ProgramDir)

OpenWindow(0, 10, 10, 200, 200, "Test")

PrepareFileRequester()   ;- Does NOT work! --> Call this BEFORE any call of OpenWindow().

OpenFileRequester("Title", ProgramDir, "All Files|*.*", 0)
SaveFileRequester("Title", ProgramDir, "All Files|*.*", 0)
JHPJHP
Addict
Addict
Posts: 2129
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: [Solved - OS fault] File Requesters open in previous pat

Post by JHPJHP »

Hi All,

I cannot remember if the following worked in previous versions of Windows, but for Windows 10 I have not had a problem.

Add a Backslash

Code: Select all

CompilerIf #PB_OS_Windows
  PathAddBackslash_(ProgramDir)
CompilerEndIf

Code: Select all

CompilerIf (#PB_Compiler_Debugger)
  MessageRequester("Instructions",
      "1. Compile this to an EXE (Desktop/test.exe or similar)" + #LF$ +
      "2. Run it" + #LF$ + 
      "3. When the first requester opens, choose a file in a different folder" + #LF$ +
      "4. The second requester opens in that folder (bug)" + #LF$ +
      "5. Run the program again, and it opens in that changed folder (bug)" + #LF$ + #LF$ +
      "Some sort of Windows caching issue ??" )
CompilerElse

  ;CurrentDir.s = GetCurrentDirectory()
  ;MessageRequester("Current Dir", CurrentDir)
  
  ProgramDir.s = GetPathPart(ProgramFilename())
  
  CompilerIf #PB_OS_Windows
    PathAddBackslash_(ProgramDir)
  CompilerEndIf
  MessageRequester("Program Dir", ProgramDir)
  
  OpenFileRequester("Title", ProgramDir, "All Files|*.*", 0)
  OpenFileRequester("Title", ProgramDir, "All Files|*.*", 0)

CompilerEndIf
Little John
Addict
Addict
Posts: 4527
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: [Solved - OS fault] File Requesters open in previous pat

Post by Little John »

Hi JHPJHP, unfortunately adding

Code: Select all

PathAddBackslash_(ProgramDir)
does not solve the problem here (on Windows 10 Pro x64, version 1909).
JHPJHP
Addict
Addict
Posts: 2129
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: [Solved - OS fault] File Requesters open in previous pat

Post by JHPJHP »

Hi Little John,

That's the problem with Windows, I always feel like I am programming in a bubble.

Microsoft Window 10 Pro :: 10.0.18363 Build 18363 :: x64
Image
User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

Re: [Solved - OS fault] File Requesters open in previous pat

Post by kenmo »

Huh, I wonder why it doesn't happen for you... maybe Microsoft finally removed this "feature" in a version of Windows 10?

Try building it to something other than "test.exe" since Windows caches this (and other things) by executable name... build it as something unique like test1234NeverRanThisBefore.exe and see how it behaves.

Thanks for the demonstration video!
JHPJHP
Addict
Addict
Posts: 2129
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: [Solved - OS fault] File Requesters open in previous pat

Post by JHPJHP »

Hi kenmo,

I looked at some of my recent projects to figure out why it's been working, and found that inadvertently I've been doing a variation of the following.
- force a double backslash on the default directory after the first call to OpenFileRequester

The OpenFileRequester will keep opening until Cancel is pressed.
- please let me know if this breaks the Windows bubble

Code: Select all

CompilerIf (#PB_Compiler_Debugger)
  MessageRequester("Instructions",
      "1. Compile this to an EXE (Desktop/test.exe or similar)" + #LF$ +
      "2. Run it" + #LF$ + 
      "3. When the first requester opens, choose a file in a different folder" + #LF$ +
      "4. The second requester opens in that folder (bug)" + #LF$ +
      "5. Run the program again, and it opens in that changed folder (bug)" + #LF$ + #LF$ +
      "Some sort of Windows caching issue ??" )
CompilerElse

  ProgramDir.s = GetPathPart(ProgramFilename())
  MessageRequester("Program Dir", ProgramDir)

  test$ = OpenFileRequester("Title", ProgramDir, "All Files|*.*", 0)
  CompilerIf #PB_OS_Windows
    PathAddBackslash_(ProgramDir) : ProgramDir + "\"
  CompilerEndIf

  While test$ <> ""
    MessageRequester("OpenFileRequester", test$)
    test$ = OpenFileRequester("Title", ProgramDir, "All Files|*.*", 0)
  Wend

CompilerEndIf
Post Reply