Select folder dialog

Share your advanced PureBasic knowledge/code with the community.
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Select folder dialog

Post by tatanas »

Hi !

I was looking for a OpenFolderRequester function (same style as OpenFileRequester) because the PathRequester interface is not user friendly to deal with network share.
I found IFileOpenDialog interface (WinApi) then my search led me to a Russian Purebasic forum.

Here is the result : http://purebasic.info/phpBB3ex/viewtopi ... 242#p49242 --> Link is DEAD

Here is the code :

Code: Select all

IncludeFile "IFileDialogDef.pbi"

; Procedure.s OpenFolderRequester(Title.s, DefaultFolder.s, Array AddPlace.s(1), hWndWindow = #Null) ; le tableau AddPlace sert à ajouter des emplacements dans la partie Favoris de la boite de dialogue
Procedure.s OpenFolderRequester(Title.s = "", DefaultFolder.s = "", hWndWindow = #Null)
	Define.s FolderName
	
	PathRemoveBackslash_(DefaultFolder)
	hResult = CoInitializeEx_(#Null, #COINIT_APARTMENTTHREADED | #COINIT_DISABLE_OLE1DDE)
	
	If hResult = #S_OK Or hResult = #S_FALSE
		If CoCreateInstance_(?CLSID_IFileOpenDialog, #Null, #CLSCTX_ALL, ?IID_IFileOpenDialog, @FileOpenDialog.IFileOpenDialog) = #S_OK
			ShellItem.IShellItem = CreateItemFromParsingName(DefaultFolder)
			FileOpenDialog\SetTitle(@Title)
			FileOpenDialog\SetDefaultFolder(ShellItem)
			
			; permet d'ajouter des emplacements dans la partie "Favoris" en haut à gauche de la boite de dialogue
; 			For rtnCount = 0 To ArraySize(AddPlace()) - 1
; 				ShellItem = CreateItemFromParsingName(AddPlace(rtnCount))
; 				FileOpenDialog\AddPlace(ShellItem, #FDAP_BOTTOM)
; 			Next
			
			FileOpenDialog\SetFileNameLabel(@"Nom du dossier :")
; 			FileOpenDialog\SetFileName(@DefaultFolder)
			FileOpenDialog\SetOkButtonLabel(@"Sélectionner")
			FileOpenDialog\GetOptions(@pfos)
			FileOpenDialog\SetOptions(pfos | #FOS_PICKFOLDERS | #FOS_FORCEFILESYSTEM | #FOS_PATHMUSTEXIST | #FOS_DONTADDTORECENT)
			FileOpenDialog\SetClientGuid(CreateGUID())
			
			If FileOpenDialog\Show(hWndWindow) = #S_OK
				FileOpenDialog\GetResult(@ShellItem)
				ShellItem\GetDisplayName(#SIGDN_FILESYSPATH, @ppszName)
				FolderName = PeekS(ppszName)
				CoTaskMemFree_(ppszName)
			EndIf
			FileOpenDialog\ClearClientData()
			FileOpenDialog\Release()
		EndIf
		CoUninitialize_()
	EndIf
	PathAddBackslash_(FolderName)
	ProcedureReturn FolderName
EndProcedure

;----example
;Define.s Dim AddPlace(2), FolderName
;AddPlace(0) = GetHomeDirectory()
;AddPlace(1) = GetTemporaryDirectory()
;FolderName = OpenFolderRequester("Open Folder Requester", GetHomeDirectory(), AddPlace())
;If FolderName : MessageRequester("Open Folder Requester", FolderName, #PB_MessageRequester_Info) : EndIf
The IFileDialogDef.pbi file :

Code: Select all

#CLSCTX_INPROC_SERVER = $1
#CLSCTX_INPROC_HANDLER = $2
#CLSCTX_LOCAL_SERVER = $4
#CLSCTX_INPROC_SERVER16 = $8
#CLSCTX_REMOTE_SERVER = $10
#CLSCTX_ALL = #CLSCTX_INPROC_SERVER | #CLSCTX_LOCAL_SERVER | #CLSCTX_REMOTE_SERVER
#CLSCTX_INPROC_HANDLER16 = $20
#CLSCTX_RESERVED1 = $40
#CLSCTX_RESERVED2 = $80
#CLSCTX_RESERVED3 = $100
#CLSCTX_RESERVED4 = $200
#CLSCTX_NO_CODE_DOWNLOAD = $400
#CLSCTX_RESERVED5 = $800
#CLSCTX_NO_CUSTOM_MARSHAL = $1000
#CLSCTX_ENABLE_CODE_DOWNLOAD = $2000
#CLSCTX_NO_FAILURE_LOG = $4000
#CLSCTX_DISABLE_AAA = $8000
#CLSCTX_ENABLE_AAA = $10000
#CLSCTX_FROM_DEFAULT_CONTEXT = $20000
#CLSCTX_ACTIVATE_X86_SERVER = $40000
#CLSCTX_ACTIVATE_32_BIT_SERVER = #CLSCTX_ACTIVATE_X86_SERVER
#CLSCTX_ACTIVATE_64_BIT_SERVER = $80000
#CLSCTX_ENABLE_CLOAKING = $100000
#CLSCTX_APPCONTAINER = $400000
#CLSCTX_ACTIVATE_AAA_AS_IU = $800000
#CLSCTX_RESERVED6 = $1000000
#CLSCTX_ACTIVATE_ARM32_SERVER = $2000000
#CLSCTX_PS_DLL = $80000000

#FOS_OVERWRITEPROMPT = $2
#FOS_STRICTFILETYPES = $4
#FOS_NOCHANGEDIR = $8
#FOS_PICKFOLDERS = $20
#FOS_FORCEFILESYSTEM = $40
#FOS_ALLNONSTORAGEITEMS = $80
#FOS_NOVALIDATE = $100
#FOS_ALLOWMULTISELECT = $200
#FOS_PATHMUSTEXIST = $800
#FOS_FILEMUSTEXIST = $1000
#FOS_CREATEPROMPT = $2000
#FOS_SHAREAWARE = $4000
#FOS_NOREADONLYRETURN = $8000
#FOS_NOTESTFILECREATE = $10000
#FOS_HIDEMRUPLACES = $20000
#FOS_HIDEPINNEDPLACES = $40000
#FOS_NODEREFERENCELINKS = $100000
#FOS_OKBUTTONNEEDSINTERACTION = $200000
#FOS_DONTADDTORECENT = $2000000
#FOS_FORCESHOWHIDDEN = $10000000
#FOS_DEFAULTNOMINIMODE = $20000000
#FOS_FORCEPREVIEWPANEON = $40000000
#FOS_SUPPORTSTREAMABLEITEMS = $FFFFFFFF80000000

#FDAP_BOTTOM = $0
#FDAP_TOP = $1

#SIGDN_NORMALDISPLAY = $0
#SIGDN_PARENTRELATIVEPARSING = $80018001
#SIGDN_DESKTOPABSOLUTEPARSING = $80028000
#SIGDN_PARENTRELATIVEEDITING = $80031001
#SIGDN_DESKTOPABSOLUTEEDITING = $8004c000
#SIGDN_FILESYSPATH = $80058000
#SIGDN_URL = $80068000
#SIGDN_PARENTRELATIVEFORADDRESSBAR = $8007c001
#SIGDN_PARENTRELATIVE = $80080001
#SIGDN_PARENTRELATIVEFORUI = $80094001

Interface IFileDialog Extends IModalWindow
  SetFileTypes(cFileTypes, *rgFilterSpec)
  SetFileTypeIndex(iFileType)
  GetFileTypeIndex(*piFileType)
  Advise(*pfde, *pdwCookie)
  Unadvise(dwCookie)
  SetOptions(fos)
  GetOptions(*pfos)
  SetDefaultFolder(*psi)
  SetFolder(*psi)
  GetFolder(*ppsi)
  GetCurrentSelection(*ppsi)
  SetFileName(*pszName)
  GetFileName(*pszName)
  SetTitle(*pszTitle)
  SetOkButtonLabel(*pszText)
  SetFileNameLabel(*pszLabel)
  GetResult(*ppsi)
  AddPlace(*psi, fdap)
  SetDefaultExtension(*pszDefaultExtension)
  Close(hr)
  SetClientGuid(guid)
  ClearClientData()
  SetFilter(*pFilter)
EndInterface

Interface IFileDialog2 Extends IFileDialog
  SetCancelButtonLabel(*pszLabel)
  SetNavigationRoot(*psi)
EndInterface

Interface IFileOpenDialog Extends IFileDialog
  GetResults(*ppenum)
  GetSelectedItems(*ppsai)
EndInterface

Interface IFileSaveDialog Extends IFileDialog
  ApplyProperties(*psi, *pStore, hwnd, *pSink)
  GetProperties(*ppStore)
  SetCollectedProperties(*pList, fAppendDefault)
  SetProperties(*pStore)
  SetSaveAsItem(*psi)
EndInterface

Interface IFileDialogCustomize Extends IUnknown
  EnableOpenDropDown(dwIDCtl)
  AddMenu(dwIDCtl, *pszLabel)
  AddPushButton(dwIDCtl, *pszLabel)
  AddComboBox(dwIDCtl)
  AddRadioButtonList(dwIDCtl)
  AddCheckButton(dwIDCtl, *pszLabel, bChecked)
  AddEditBox(dwIDCtl, *pszText)
  AddSeparator(dwIDCtl)
  AddText(dwIDCtl, *pszText)
  SetControlLabel(dwIDCtl, *pszLabel)
  GetControlState(dwIDCtl, *pdwState)
  SetControlState(dwIDCtl, dwState)
  GetEditBoxText(dwIDCtl, *ppszText)
  SetEditBoxText(dwIDCtl, *pszText)
  GetCheckButtonState(dwIDCtl, *pbChecked)
  SetCheckButtonState(dwIDCtl, bChecked)
  AddControlItem(dwIDCtl, dwIDItem, *pszLabel)
  RemoveControlItem(dwIDCtl, dwIDItem)
  RemoveAllControlItems(dwIDCtl)
  GetControlItemState(dwIDCtl, dwIDItem, *pdwState)
  SetControlItemState(dwIDCtl, dwIDItem, dwState)
  GetSelectedControlItem(dwIDCtl, *pdwIDItem)
  SetSelectedControlItem(dwIDCtl, dwIDItem)
  StartVisualGroup(dwIDCtl, *pszLabel)
  EndVisualGroup()
  MakeProminent(dwIDCtl)
  SetControlItemText(dwIDCtl, dwIDItem, *pszLabel)
EndInterface

Interface IFileDialogControlEvents Extends IUnknown
  OnItemSelected(*pfdc, dwIDCtl, dwIDItem)
  OnButtonClicked(*pfdc, dwIDCtl)
  OnCheckButtonToggled(*pfdc, dwIDCtl, bChecked)
  OnControlActivating(*pfdc, dwIDCtl)
EndInterface

Interface IFileDialogEvents Extends IUnknown
  OnFileOk(*pfd)
  OnFolderChanging(*pfd, *psiFolder)
  OnFolderChange(*pfd)
  OnSelectionChange(*pfd)
  OnShareViolation(*pfd, *psi, *pResponse)
  OnTypeChange(*pfd)
  OnOverwrite(*pfd, *psi, *pResponse)
EndInterface

Prototype protoSHCreateItemFromParsingName(*pszPath, *pbc, riid, *ppv)

Procedure CreateItemFromParsingName(FolderName.s)
  Protected SHCreateItemFromParsingName.protoSHCreateItemFromParsingName

  hResult = OleInitialize_(#Null)

  If hResult = #S_OK Or hResult = #S_FALSE
    shell32 = OpenLibrary(#PB_Any, "shell32.dll")

    If IsLibrary(shell32)
      SHCreateItemFromParsingName = GetFunction(shell32, "SHCreateItemFromParsingName")

      If SHCreateItemFromParsingName : SHCreateItemFromParsingName(@FolderName, #Null, ?IID_IShellItem, @ShellItem.IShellItem) : EndIf

      CloseLibrary(shell32)
    EndIf
    OleUninitialize_()
  EndIf
  ProcedureReturn ShellItem
EndProcedure

Procedure CreateGUID()
  guid.GUID : CoCreateGuid_(@guid) : ProcedureReturn guid
EndProcedure

DataSection
  CLSID_IFileOpenDialog:
    Data.l $DC1C5A9C
    Data.w $E88A, $4DDE
    Data.b $A5, $A1, $60, $F8, $2A, $20, $AE, $F7
  CLSID_FileSaveDialog:
    Data.l $C0B4E2F3
    Data.w $BA21, $4773
    Data.b $8D, $BA, $33, $5E, $C9, $46, $EB, $8B
  IID_IFileDialog:
    Data.l $42F85136
    Data.w $DB7E, $439C
    Data.b $85, $F1, $E4, $07, $5D, $13, $5F, $C8
  IID_IFileDialog2:
    Data.l $61744FC7
    Data.w $85B5, $4791
    Data.b $A9, $B0, $27, $22, $76, $30, $9B, $13
  IID_IFileOpenDialog:
    Data.l $D57C7288
    Data.w $D4AD, $4768
    Data.b $BE, $02, $9D, $96, $95, $32, $D9, $60
  IID_IFileSaveDialog:
    Data.l $84bCCD23
    Data.w $5FDE, $4CDB
    Data.b $AE, $A4, $AF, $64, $B8, $3D, $78, $AB
  IID_IFileDialogCustomize:
    Data.l $E6FDD21A
    Data.w $163F, $4975
    Data.b $9C, $8C, $A6, $9F, $1B, $A3, $70, $34
  IID_IFileDialogControlEvents:
    Data.l $36116642
    Data.w $D713, $4B97
    Data.b $9B, $83, $74, $84, $A9, $D0, $04, $33
  IID_IFileDialogEvents:
    Data.l $973510DB
    Data.w $7D7F, $452B
    Data.b $89, $75, $74, $A8, $58, $28, $D3, $54
  IID_IShellItem:
    Data.l $43826D1E
    Data.w $E718, $42EE
    Data.b $BC, $55, $A1, $E2, $61, $C3, $7B, $FE
EndDataSection
Last edited by tatanas on Fri Jun 03, 2022 12:49 pm, edited 2 times in total.
Windows 10 Pro x64
PureBasic 6.20 x64
Denis
Enthusiast
Enthusiast
Posts: 778
Joined: Fri Apr 25, 2003 5:10 pm
Location: Doubs - France

Re: Select folder dialog

Post by Denis »

Hi tatanas

Thanks for the link, the code runs well under Win 10 x64.
Corresponds to my need :D
A+
Denis
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Select folder dialog

Post by tatanas »

You're welcome :)
I knew this code would interest someone.
Windows 10 Pro x64
PureBasic 6.20 x64
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Select folder dialog

Post by Kwai chang caine »

Very usefull code, a selecter like in the majority of others big softwares :D
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Select folder dialog

Post by Kwai chang caine »

I knew a day, i need this splendid code 8)
After 3 years, someone know if it's possible to ask to PathRequester to expand the tree in the left side until the defauult path for example "C:\Users\Public\" ?
For the moment the explorer at right go to the folder "C:\Users\Public\"
But the tree at left is collapsed :|
ImageThe happiness is a road...
Not a destination
Quin
Addict
Addict
Posts: 1124
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: Select folder dialog

Post by Quin »

When trying to use this code on Windows 11 ARM (21H2), the dialog doesn't show at all, and always returns 0. Has anyone else seen this?
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Select folder dialog

Post by tatanas »

Quin,
Try using this call (without the array variable):

Code: Select all

OpenFolderRequester("Open Folder Requester", GetHomeDirectory())
Kwain chang caine,
It seems we have to use the NameSpaceTreeControl interface for interacting with the tree control.
Unfortunatly for now it doesnt work. Here is the interface declaration :

Code: Select all

#CLSCTX_INPROC_SERVER = $1
#CLSCTX_INPROC_HANDLER = $2
#CLSCTX_LOCAL_SERVER = $4
#CLSCTX_INPROC_SERVER16 = $8
#CLSCTX_REMOTE_SERVER = $10
#CLSCTX_ALL = #CLSCTX_INPROC_SERVER | #CLSCTX_LOCAL_SERVER | #CLSCTX_REMOTE_SERVER
#CLSCTX_INPROC_HANDLER16 = $20
#CLSCTX_RESERVED1 = $40
#CLSCTX_RESERVED2 = $80
#CLSCTX_RESERVED3 = $100
#CLSCTX_RESERVED4 = $200
#CLSCTX_NO_CODE_DOWNLOAD = $400
#CLSCTX_RESERVED5 = $800
#CLSCTX_NO_CUSTOM_MARSHAL = $1000
#CLSCTX_ENABLE_CODE_DOWNLOAD = $2000
#CLSCTX_NO_FAILURE_LOG = $4000
#CLSCTX_DISABLE_AAA = $8000
#CLSCTX_ENABLE_AAA = $10000
#CLSCTX_FROM_DEFAULT_CONTEXT = $20000
#CLSCTX_ACTIVATE_X86_SERVER = $40000
#CLSCTX_ACTIVATE_32_BIT_SERVER = #CLSCTX_ACTIVATE_X86_SERVER
#CLSCTX_ACTIVATE_64_BIT_SERVER = $80000
#CLSCTX_ENABLE_CLOAKING = $100000
#CLSCTX_APPCONTAINER = $400000
#CLSCTX_ACTIVATE_AAA_AS_IU = $800000
#CLSCTX_RESERVED6 = $1000000
#CLSCTX_ACTIVATE_ARM32_SERVER = $2000000
#CLSCTX_PS_DLL = $80000000

#FOS_OVERWRITEPROMPT = $2
#FOS_STRICTFILETYPES = $4
#FOS_NOCHANGEDIR = $8
#FOS_PICKFOLDERS = $20
#FOS_FORCEFILESYSTEM = $40
#FOS_ALLNONSTORAGEITEMS = $80
#FOS_NOVALIDATE = $100
#FOS_ALLOWMULTISELECT = $200
#FOS_PATHMUSTEXIST = $800
#FOS_FILEMUSTEXIST = $1000
#FOS_CREATEPROMPT = $2000
#FOS_SHAREAWARE = $4000
#FOS_NOREADONLYRETURN = $8000
#FOS_NOTESTFILECREATE = $10000
#FOS_HIDEMRUPLACES = $20000
#FOS_HIDEPINNEDPLACES = $40000
#FOS_NODEREFERENCELINKS = $100000
#FOS_OKBUTTONNEEDSINTERACTION = $200000
#FOS_DONTADDTORECENT = $2000000
#FOS_FORCESHOWHIDDEN = $10000000
#FOS_DEFAULTNOMINIMODE = $20000000
#FOS_FORCEPREVIEWPANEON = $40000000
#FOS_SUPPORTSTREAMABLEITEMS = $FFFFFFFF80000000

#FDAP_BOTTOM = $0
#FDAP_TOP = $1

#SIGDN_NORMALDISPLAY = $0
#SIGDN_PARENTRELATIVEPARSING = $80018001
#SIGDN_DESKTOPABSOLUTEPARSING = $80028000
#SIGDN_PARENTRELATIVEEDITING = $80031001
#SIGDN_DESKTOPABSOLUTEEDITING = $8004c000
#SIGDN_FILESYSPATH = $80058000
#SIGDN_URL = $80068000
#SIGDN_PARENTRELATIVEFORADDRESSBAR = $8007c001
#SIGDN_PARENTRELATIVE = $80080001
#SIGDN_PARENTRELATIVEFORUI = $80094001


#NSTCIS_NONE = $0
#NSTCIS_SELECTED = $1
#NSTCIS_EXPANDED = $2
#NSTCIS_BOLD = $4
#NSTCIS_DISABLED = $8
#NSTCIS_SELECTEDNOEXPAND = $10

#NSTCS_HASEXPANDOS = $1
#NSTCS_HASLINES = $2
#NSTCS_SINGLECLICKEXPAND = $4
#NSTCS_FULLROWSELECT = $8
#NSTCS_SPRINGEXPAND = $10
#NSTCS_HORIZONTALSCROLL = $20
#NSTCS_ROOTHASEXPANDO = $40
#NSTCS_SHOWSELECTIONALWAYS = $80
#NSTCS_NOINFOTIP = $200
#NSTCS_EVENHEIGHT = $400
#NSTCS_NOREPLACEOPEN = $800
#NSTCS_DISABLEDRAGDROP = $1000
#NSTCS_NOORDERSTREAM = $2000
#NSTCS_RICHTOOLTIP = $4000
#NSTCS_BORDER = $8000
#NSTCS_NOEDITLABELS = $10000
#NSTCS_TABSTOP = $20000
#NSTCS_FAVORITESMODE = $80000
#NSTCS_AUTOHSCROLL = $100000
#NSTCS_FADEINOUTEXPANDOS = $200000
#NSTCS_EMPTYTEXT = $400000
#NSTCS_CHECKBOXES = $800000
#NSTCS_PARTIALCHECKBOXES = $1000000
#NSTCS_EXCLUSIONCHECKBOXES = $2000000
#NSTCS_DIMMEDCHECKBOXES = $4000000
#NSTCS_NOINDENTCHECKS = $8000000
#NSTCS_ALLOWJUNCTIONS = $10000000
#NSTCS_SHOWTABSBUTTON = $20000000
#NSTCS_SHOWDELETEBUTTON = $40000000
#NSTCS_SHOWREFRESHBUTTON = $80000000

#NSTCS2_DEFAULT = $0
#NSTCS2_INTERRUPTNOTIFICATIONS = $1
#NSTCS2_SHOWNULLSPACEMENU = $2
#NSTCS2_DISPLAYPADDING = $4
#NSTCS2_DISPLAYPINNEDONLY = $8
#NTSCS2_NOSINGLETONAUTOEXPAND = $10
#NTSCS2_NEVERINSERTNONENUMERATED = $20


Interface INameSpaceTreeControl Extends IUnknown
	AppendRoot(*psiRoot, grfEnumFlags, grfRootStyle, *pif)
	CollapseAll()
	EnsureItemVisible(*psi)
	GetItemCustomState(*psi, *piStateNumber)
	GetItemRect(*psi, *prect)
	GetItemState(*psi, nstcisMask, *pnstcisFlags)
	GetNextItem(*psi, nstcgi, *ppsiNext)
	GetRootItems(*ppsiaRootItems)
	GetSelectedItems(*psiaItems)
	HitTest(*ppt, *ppsiOut)
	Initialize(hwndParent, *prc, nsctsFlags)
	InsertRoot(iIndex, *psiRoot, grfEnumFlags, grfRootStyle, *pif)
	RemoveAllRoots()
	RemoveRoot(*psiRoot)
	SetItemCustomState(*psi, iStateNumber)
	SetItemState(*psi, nstcisMask, nstcisFlags)
	SetTheme(*pszTheme)
	TreeAdvise(*punk, *pdwCookie)
	TreeUnadvise(dwCookie)
EndInterface

Interface INameSpaceTreeControl2 Extends INameSpaceTreeControl
	GetControlStyle(nstcsMask, *pnstcsStyle)
	SetControlStyle(nstcsMask, nstcsStyle)
	GetControlStyle2(nstcsMask, *pnstcsStyle)
	SetControlStyle2(nstcsMask, nstcsStyle)
EndInterface

Interface IFileDialog Extends IModalWindow
  SetFileTypes(cFileTypes, *rgFilterSpec)
  SetFileTypeIndex(iFileType)
  GetFileTypeIndex(*piFileType)
  Advise(*pfde, *pdwCookie)
  Unadvise(dwCookie)
  SetOptions(fos)
  GetOptions(*pfos)
  SetDefaultFolder(*psi)
  SetFolder(*psi)
  GetFolder(*ppsi)
  GetCurrentSelection(*ppsi)
  SetFileName(*pszName)
  GetFileName(*pszName)
  SetTitle(*pszTitle)
  SetOkButtonLabel(*pszText)
  SetFileNameLabel(*pszLabel)
  GetResult(*ppsi)
  AddPlace(*psi, fdap)
  SetDefaultExtension(*pszDefaultExtension)
  Close(hr)
  SetClientGuid(guid)
  ClearClientData()
  SetFilter(*pFilter)
EndInterface

Interface IFileDialog2 Extends IFileDialog
  SetCancelButtonLabel(*pszLabel)
  SetNavigationRoot(*psi)
EndInterface

Interface IFileOpenDialog Extends IFileDialog
  GetResults(*ppenum)
  GetSelectedItems(*ppsai)
EndInterface

Interface IFileSaveDialog Extends IFileDialog
  ApplyProperties(*psi, *pStore, hwnd, *pSink)
  GetProperties(*ppStore)
  SetCollectedProperties(*pList, fAppendDefault)
  SetProperties(*pStore)
  SetSaveAsItem(*psi)
EndInterface

Interface IFileDialogCustomize Extends IUnknown
  EnableOpenDropDown(dwIDCtl)
  AddMenu(dwIDCtl, *pszLabel)
  AddPushButton(dwIDCtl, *pszLabel)
  AddComboBox(dwIDCtl)
  AddRadioButtonList(dwIDCtl)
  AddCheckButton(dwIDCtl, *pszLabel, bChecked)
  AddEditBox(dwIDCtl, *pszText)
  AddSeparator(dwIDCtl)
  AddText(dwIDCtl, *pszText)
  SetControlLabel(dwIDCtl, *pszLabel)
  GetControlState(dwIDCtl, *pdwState)
  SetControlState(dwIDCtl, dwState)
  GetEditBoxText(dwIDCtl, *ppszText)
  SetEditBoxText(dwIDCtl, *pszText)
  GetCheckButtonState(dwIDCtl, *pbChecked)
  SetCheckButtonState(dwIDCtl, bChecked)
  AddControlItem(dwIDCtl, dwIDItem, *pszLabel)
  RemoveControlItem(dwIDCtl, dwIDItem)
  RemoveAllControlItems(dwIDCtl)
  GetControlItemState(dwIDCtl, dwIDItem, *pdwState)
  SetControlItemState(dwIDCtl, dwIDItem, dwState)
  GetSelectedControlItem(dwIDCtl, *pdwIDItem)
  SetSelectedControlItem(dwIDCtl, dwIDItem)
  StartVisualGroup(dwIDCtl, *pszLabel)
  EndVisualGroup()
  MakeProminent(dwIDCtl)
  SetControlItemText(dwIDCtl, dwIDItem, *pszLabel)
EndInterface

Interface IFileDialogControlEvents Extends IUnknown
  OnItemSelected(*pfdc, dwIDCtl, dwIDItem)
  OnButtonClicked(*pfdc, dwIDCtl)
  OnCheckButtonToggled(*pfdc, dwIDCtl, bChecked)
  OnControlActivating(*pfdc, dwIDCtl)
EndInterface

Interface IFileDialogEvents Extends IUnknown
  OnFileOk(*pfd)
  OnFolderChanging(*pfd, *psiFolder)
  OnFolderChange(*pfd)
  OnSelectionChange(*pfd)
  OnShareViolation(*pfd, *psi, *pResponse)
  OnTypeChange(*pfd)
  OnOverwrite(*pfd, *psi, *pResponse)
EndInterface

Prototype protoSHCreateItemFromParsingName(*pszPath, *pbc, riid, *ppv)

Procedure CreateItemFromParsingName(FolderName.s)
  Protected SHCreateItemFromParsingName.protoSHCreateItemFromParsingName

  hResult = OleInitialize_(#Null)

  If hResult = #S_OK Or hResult = #S_FALSE
    shell32 = OpenLibrary(#PB_Any, "shell32.dll")

    If IsLibrary(shell32)
      SHCreateItemFromParsingName = GetFunction(shell32, "SHCreateItemFromParsingName")

      If SHCreateItemFromParsingName : SHCreateItemFromParsingName(@FolderName, #Null, ?IID_IShellItem, @ShellItem.IShellItem) : EndIf

      CloseLibrary(shell32)
    EndIf
    OleUninitialize_()
  EndIf
  ProcedureReturn ShellItem
EndProcedure

Procedure CreateGUID()
  guid.GUID : CoCreateGuid_(@guid) : ProcedureReturn guid
EndProcedure

DataSection
  CLSID_NameSpaceTreeControl: ; "{AE054212-3535-4430-83ED-D501AA6680E6}"
    Data.l $AE054212
    Data.w $3535, $4430
    Data.b $83, $ED, $D5, $01, $AA, $66, $80, $E6
  CLSID_IFileOpenDialog:
    Data.l $DC1C5A9C
    Data.w $E88A, $4DDE
    Data.b $A5, $A1, $60, $F8, $2A, $20, $AE, $F7
  CLSID_FileSaveDialog:
    Data.l $C0B4E2F3
    Data.w $BA21, $4773
    Data.b $8D, $BA, $33, $5E, $C9, $46, $EB, $8B
  IID_INameSpaceTreeControl: ; "{028212A3-B627-47e9-8856-C14265554E4F}"
    Data.l $028212A3
    Data.w $B627, $47e9
    Data.b $88, $56, $C1, $42, $65, $55, $4E, $4F
  IID_INameSpaceTreeControl2: ; "7cc7aed8-290e-49bc-8945-c1401cc9306c"
    Data.l $7CC7AED8
    Data.w $290E, $49BC
    Data.b $89, $45, $C1, $40, $1C, $C9, $30, $6C
  IID_IFileDialog: ; "{42f85136-db7e-439c-85f1-e4075d135fc8}"
    Data.l $42F85136
    Data.w $DB7E, $439C
    Data.b $85, $F1, $E4, $07, $5D, $13, $5F, $C8
  IID_IFileDialog2:
    Data.l $61744FC7
    Data.w $85B5, $4791
    Data.b $A9, $B0, $27, $22, $76, $30, $9B, $13
  IID_IFileOpenDialog:
    Data.l $D57C7288
    Data.w $D4AD, $4768
    Data.b $BE, $02, $9D, $96, $95, $32, $D9, $60
  IID_IFileSaveDialog:
    Data.l $84bCCD23
    Data.w $5FDE, $4CDB
    Data.b $AE, $A4, $AF, $64, $B8, $3D, $78, $AB
  IID_IFileDialogCustomize:
    Data.l $E6FDD21A
    Data.w $163F, $4975
    Data.b $9C, $8C, $A6, $9F, $1B, $A3, $70, $34
  IID_IFileDialogControlEvents:
    Data.l $36116642
    Data.w $D713, $4B97
    Data.b $9B, $83, $74, $84, $A9, $D0, $04, $33
  IID_IFileDialogEvents:
    Data.l $973510DB
    Data.w $7D7F, $452B
    Data.b $89, $75, $74, $A8, $58, $28, $D3, $54
  IID_IShellItem:
    Data.l $43826D1E
    Data.w $E718, $42EE
    Data.b $BC, $55, $A1, $E2, $61, $C3, $7B, $FE
EndDataSection
And an example from Microsoft : https://github.com/microsoft/Windows-cl ... Sample.cpp
Last edited by tatanas on Wed May 15, 2024 3:41 pm, edited 1 time in total.
Windows 10 Pro x64
PureBasic 6.20 x64
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Select folder dialog

Post by tatanas »

Actually, we don't need the NameSpaceTreeControl interface to interact with the treeview.

Not very clean but it works :

Code: Select all

IncludeFile "IFileDialogDef.pbi"



; https://www.purebasic.fr/english/viewtopic.php?t=20397
; https://www.autohotkey.com/boards/viewtopic.php?t=108274

Global hTreeView, ProcID
Global path.s = "C:\Windows"
Global title.s = "Open Folder Requester"

Procedure EnumCallback(hWnd, Value)
	ClassName$ = Space(64)
	GetClassName_(hWnd, @ClassName$, 64)
	If ClassName$ = "SysTreeView32"
		hTreeView = hWnd
		ProcedureReturn #False
	EndIf
	ProcedureReturn #True
EndProcedure

Procedure GetItemID(Gadget, Item)
	Protected hItem, hItem2
	hItem = SendMessage_(Gadget, #TVM_GETNEXTITEM, #TVGN_ROOT, 0)
	For i = 0 To Item - 1
		hItem2 = SendMessage_(Gadget, #TVM_GETNEXTITEM, #TVGN_CHILD, hItem)
		Repeat
			If hItem2 = #Null : hItem2 = SendMessage_(Gadget, #TVM_GETNEXTITEM, #TVGN_NEXT, hItem) : EndIf
			If hItem2 = #Null : hItem = SendMessage_(Gadget, #TVM_GETNEXTITEM, #TVGN_PARENT, hItem) : EndIf
		Until hItem2 <> #Null
		hItem = hItem2
	Next
	ProcedureReturn hItem
EndProcedure

Procedure ExpandTreeView(*value)

	count = CountString(path, "\") + 1
	NewList pathpart.s()
	For i = 1 To count
		AddElement(pathpart())
		pathpart() = StringField(path, i, "\")
	Next
	
	Repeat
		hWin = FindWindow_(0, title)
		Delay(500)
	Until hWin

	GetWindowThreadProcessId_(hWin, @ProcID)
	EnumChildWindows_(hWin, @EnumCallback(), 0)
	
	Buffer = AllocateMemory(100)
	hProc = OpenProcess_(#PROCESS_VM_OPERATION | #PROCESS_VM_READ | #PROCESS_VM_WRITE, 0, ProcID)
	Result = VirtualAllocEx_(hProc, 0, SizeOf(TV_ITEM), #MEM_RESERVE | #MEM_COMMIT, #PAGE_READWRITE)
	
	Define TVItem.TV_ITEM
	TVItem\mask = #TVIF_TEXT | #TVIF_HANDLE
	TVItem\pszText = Result + 400
	TVItem\cchTextMax = 100
	
	If hTreeView
		For i = 0 To SendMessage_(hTreeView, #TVM_GETCOUNT, 0, 0) - 1
	
			TVItem\hItem = GetItemID(hTreeView, i)
			
			WriteProcessMemory_(hProc, Result, @TVItem, SizeOf(TV_ITEM), 0)
			SendMessage_(hTreeView, #TVM_GETITEM, 0, Result)
			ReadProcessMemory_(hProc, Result + 400, Buffer, MemorySize(Buffer), 0)
	; 		Debug PeekS(Buffer)
	
			ForEach pathpart()
				If FindString(PeekS(Buffer), pathpart())
					SendMessage_(hTreeView, #TVM_EXPAND, #TVE_EXPAND, TVItem\hItem)
					DeleteElement(pathpart(), 1)
					Break
				EndIf
			Next
		Next
	EndIf
	
	VirtualFreeEx_(hProc, Result, 0, #MEM_RELEASE)
	CloseHandle_(hProc)

EndProcedure


; Procedure.s OpenFolderRequester(Title.s, DefaultFolder.s, Array AddPlace.s(1), hWndWindow = #Null) ; le tableau AddPlace sert à ajouter des emplacements dans la partie Favoris de la boite de dialogue
Procedure.s OpenFolderRequester(Title.s = "", DefaultFolder.s = "", hWndWindow = #Null)
	Protected FolderName.s
	
	PathRemoveBackslash_(DefaultFolder)
	hResult = CoInitializeEx_(#Null, #COINIT_APARTMENTTHREADED | #COINIT_DISABLE_OLE1DDE)

	If hResult = #S_OK Or hResult = #S_FALSE
		If CoCreateInstance_(?CLSID_IFileOpenDialog, #Null, #CLSCTX_ALL, ?IID_IFileOpenDialog, @FileOpenDialog.IFileOpenDialog) = #S_OK
			ShellItem.IShellItem = CreateItemFromParsingName(DefaultFolder)
			FileOpenDialog\SetTitle(@Title)
			FileOpenDialog\SetDefaultFolder(ShellItem)
			
			; permet d'ajouter des emplacements dans la partie "Favoris" en haut à gauche de la boite de dialogue
; 			For rtnCount = 0 To ArraySize(AddPlace()) - 1
; 				ShellItem = CreateItemFromParsingName(AddPlace(rtnCount))
; 				FileOpenDialog\AddPlace(ShellItem, #FDAP_BOTTOM)
; 			Next
		
			FileOpenDialog\SetFileNameLabel(@"Nom du dossier :")
; 			FileOpenDialog\SetFileName(@DefaultFolder)
			FileOpenDialog\SetOkButtonLabel(@"Sélectionner")
			FileOpenDialog\GetOptions(@pfos)
			FileOpenDialog\SetOptions(pfos | #FOS_PICKFOLDERS | #FOS_FORCEFILESYSTEM | #FOS_PATHMUSTEXIST | #FOS_DONTADDTORECENT)
			FileOpenDialog\SetClientGuid(CreateGUID())
		
			If FileOpenDialog\Show(hWndWindow) = #S_OK
				FileOpenDialog\GetResult(@ShellItem)
				ShellItem\GetDisplayName(#SIGDN_FILESYSPATH, @ppszName)
				FolderName = PeekS(ppszName)
				CoTaskMemFree_(ppszName)
			EndIf
			FileOpenDialog\ClearClientData()
			FileOpenDialog\Release()
		EndIf
		CoUninitialize_()
	EndIf
	PathAddBackslash_(FolderName)

	ProcedureReturn FolderName
EndProcedure


;----example

CreateThread(@ExpandTreeView(), 0)

Define FolderName.s
FolderName = OpenFolderRequester(title, path)

If FolderName
	MessageRequester("Open Folder Requester", FolderName, #PB_MessageRequester_Info)
EndIf
Windows 10 Pro x64
PureBasic 6.20 x64
Wolfram
Enthusiast
Enthusiast
Posts: 604
Joined: Thu May 30, 2013 4:39 pm

Re: Select folder dialog

Post by Wolfram »

How can I use the SetCancelButtonLabel() Methode?
macOS Catalina 10.15.7
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Select folder dialog

Post by tatanas »

Sorry it seems there is no SetCancelButtonLabel() Method, or I can't find it.
https://learn.microsoft.com/en-us/windo ... uttonlabel
Windows 10 Pro x64
PureBasic 6.20 x64
Post Reply