SelectFolderEx() - new dialog on Win Vista+

Post your working scripts, libraries and tools
just me
Posts: 6551
Joined: 02 Oct 2013, 08:51
Location: Germany

SelectFolderEx() - new dialog on Win Vista+

13 Jun 2016, 04:32

Related: viewtopic.php?f=5&t=18764

The function is using the Common Item Dialog to select folders on Win Vista+ providing a almost identical UI for selecting files and folders (at least on WIn 10 here):

Code: Select all

; ==================================================================================================================================
; Shows a dialog to select a folder.
; Depending on the OS version the function will use either the built-in FileSelectFolder command (XP and previous)
; or the Common Item Dialog (Vista and later).
; Parameter:
;     StartingFolder -  the full path of a folder which will be preselected.
;     Prompt         -  a text used as window title (Common Item Dialog) or as text displayed withing the dialog.
;     ----------------  Common Item Dialog only:
;     OwnerHwnd      -  HWND of the Gui which owns the dialog. If you pass a valid HWND the dialog will become modal.
;     BtnLabel       -  a text to be used as caption for the apply button.
;  Return values:
;     On success the function returns the full path of selected folder; otherwise it returns an empty string.
; MSDN:
;     Common Item Dialog -> msdn.microsoft.com/en-us/library/bb776913%28v=vs.85%29.aspx
;     IFileDialog        -> msdn.microsoft.com/en-us/library/bb775966%28v=vs.85%29.aspx
;     IShellItem         -> msdn.microsoft.com/en-us/library/bb761140%28v=vs.85%29.aspx
; ==================================================================================================================================
SelectFolderEx(StartingFolder := "", Prompt := "", OwnerHwnd := 0, OkBtnLabel := "") {
   Static OsVersion := DllCall("GetVersion", "UChar")
        , IID_IShellItem := 0
        , InitIID := VarSetCapacity(IID_IShellItem, 16, 0)
                  & DllCall("Ole32.dll\IIDFromString", "WStr", "{43826d1e-e718-42ee-bc55-a1e261c37bfe}", "Ptr", &IID_IShellItem)
        , Show := A_PtrSize * 3
        , SetOptions := A_PtrSize * 9
        , SetFolder := A_PtrSize * 12
        , SetTitle := A_PtrSize * 17
        , SetOkButtonLabel := A_PtrSize * 18
        , GetResult := A_PtrSize * 20
   SelectedFolder := ""
   If (OsVersion < 6) { ; IFileDialog requires Win Vista+, so revert to FileSelectFolder
      FileSelectFolder, SelectedFolder, *%StartingFolder%, 3, %Prompt%
      Return SelectedFolder
   }
   OwnerHwnd := DllCall("IsWindow", "Ptr", OwnerHwnd, "UInt") ? OwnerHwnd : 0
   If !(FileDialog := ComObjCreate("{DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7}", "{42f85136-db7e-439c-85f1-e4075d135fc8}"))
      Return ""
   VTBL := NumGet(FileDialog + 0, "UPtr")
   ; FOS_CREATEPROMPT | FOS_NOCHANGEDIR | FOS_PICKFOLDERS
   DllCall(NumGet(VTBL + SetOptions, "UPtr"), "Ptr", FileDialog, "UInt", 0x00002028, "UInt")
   If (StartingFolder <> "")
      If !DllCall("Shell32.dll\SHCreateItemFromParsingName", "WStr", StartingFolder, "Ptr", 0, "Ptr", &IID_IShellItem, "PtrP", FolderItem)
         DllCall(NumGet(VTBL + SetFolder, "UPtr"), "Ptr", FileDialog, "Ptr", FolderItem, "UInt")
   If (Prompt <> "")
      DllCall(NumGet(VTBL + SetTitle, "UPtr"), "Ptr", FileDialog, "WStr", Prompt, "UInt")
   If (OkBtnLabel <> "")
      DllCall(NumGet(VTBL + SetOkButtonLabel, "UPtr"), "Ptr", FileDialog, "WStr", OkBtnLabel, "UInt")
   If !DllCall(NumGet(VTBL + Show, "UPtr"), "Ptr", FileDialog, "Ptr", OwnerHwnd, "UInt") {
      If !DllCall(NumGet(VTBL + GetResult, "UPtr"), "Ptr", FileDialog, "PtrP", ShellItem, "UInt") {
         GetDisplayName := NumGet(NumGet(ShellItem + 0, "UPtr"), A_PtrSize * 5, "UPtr")
         If !DllCall(GetDisplayName, "Ptr", ShellItem, "UInt", 0x80028000, "PtrP", StrPtr) ; SIGDN_DESKTOPABSOLUTEPARSING
            SelectedFolder := StrGet(StrPtr, "UTF-16"), DllCall("Ole32.dll\CoTaskMemFree", "Ptr", StrPtr)
         ObjRelease(ShellItem)
   }  }
   If (FolderItem)
      ObjRelease(FolderItem)
   ObjRelease(FileDialog)
   Return SelectedFolder
}
Edit:
License: The Unlicense (for details see unlicense.org)
User avatar
Xtra
Posts: 1511
Joined: 02 Oct 2015, 12:15

Re: SelectFolderEx() - new dialog on Win Vista+

13 Jun 2016, 06:34

Looks good thanks!
User avatar
jballi
Posts: 586
Joined: 29 Sep 2013, 17:34

Re: SelectFolderEx() - new dialog on Win Vista+

20 Jul 2016, 14:35

Looks useful. Thanks.
raylan
Posts: 12
Joined: 29 Dec 2015, 06:30

Re: SelectFolderEx() - new dialog on Win Vista+

04 Oct 2016, 11:32

That's really something
Thumbs up!
User avatar
DataLife
Posts: 330
Joined: 29 Sep 2013, 19:52

Re: SelectFolderEx() - new dialog on Win Vista+

06 Oct 2016, 14:55

I have replaced all FileSelectFolder with SelectFolderEx in my program SavePictureAs.

Do I have your permission to release SavePictureAs with SelectFolderEx with proper credit given?
Check out my scripts. (MyIpChanger) (ClipBoard Manager) (SavePictureAs)
All my scripts are tested on Windows 10, AutoHotkey 32 bit Ansi unless otherwise stated.
just me
Posts: 6551
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: SelectFolderEx() - new dialog on Win Vista+

06 Oct 2016, 15:12

Added license. You have, even without credit!
User avatar
DataLife
Posts: 330
Joined: 29 Sep 2013, 19:52

Re: SelectFolderEx() - new dialog on Win Vista+

06 Oct 2016, 16:58

just me wrote:Added license. You have, even without credit!
thanks
Check out my scripts. (MyIpChanger) (ClipBoard Manager) (SavePictureAs)
All my scripts are tested on Windows 10, AutoHotkey 32 bit Ansi unless otherwise stated.
rommmcek
Posts: 730
Joined: 15 Aug 2014, 15:18

Re: SelectFolderEx() - new dialog on Win Vista+

24 Nov 2019, 13:14

Awesome function!

Here is my contribution, so that after click on the TreeView (on the left hand side of the dialog), Select Folder button can be applied immediately:

Code: Select all

If !DllCall(NumGet(VTBL + GetResult, "UPtr"), "Ptr", FileDialog, "PtrP", ShellItem, "UInt") {
	GetDisplayName := NumGet(NumGet(ShellItem + 0, "UPtr"), A_PtrSize * 5, "UPtr")
       	 If !DllCall(GetDisplayName, "Ptr", ShellItem, "UInt", 0x80028000, "PtrP", StrPtr) ; SIGDN_DESKTOPABSOLUTEPARSING
            SelectedFolder := StrGet(StrPtr, "UTF-16"), DllCall("Ole32.dll\CoTaskMemFree", "Ptr", StrPtr)
        !SelectedFolder? DllCall(NumGet(VTBL + SetTitle, "UPtr"), "Ptr", FileDialog, "WStr", SelectedFolder, "UInt"): "" ; added
        ;InStr(FileExist("C:\Users\RRR\My Ahk-s"), "D", 1)? "": SelectedFolder:= "" ; not needed, Dialog will propmpt by itself if folder doesn't exist
        ObjRelease(ShellItem)
}
P.s: DllCall in the added ternary, is bluntly copy-pasted from the line above in this function. (First I used ControlGetText, then I noticed the script already refers to value in that control)

EDIT3: Just had an issue if #Warn is present in the script: "Warning: This variable has not been assigned a value.", so I had to add Local ShellItem:= StrPtr:= FolderItem:= "" to the starting declaration of the function to prevent it.

Return to “Scripts and Functions”

Who is online

Users browsing this forum: No registered users and 89 guests