Browse for Folder: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
mNo edit summary
mNo edit summary
Line 61: Line 61:
StrCpy $2 0
StrCpy $2 0
StrCpy $3 0
StrCpy $3 0
loop: ; BFFM_SETSELECTION is buggy and does scroll to the new item so we find the treeview and do it manually
loop: ; BFFM_SETSELECTION is buggy and does not scroll to the new item so we find the treeview and do it manually
FindWindow $2 "" "" $R1 $2 ; Assuming SysTreeView32 is a grandchild when using BIF_NEWDIALOGSTYLE
FindWindow $2 "" "" $R1 $2 ; Assuming SysTreeView32 is a grandchild when using BIF_NEWDIALOGSTYLE
IntCmp 0 $2 done
IntCmp 0 $2 done

Revision as of 14:05, 15 September 2016

Author: Anders (talk, contrib)


This function displays the folder browser UI so the user can choose a file-system folder. You can specify a root folder or use "" for the default root (deskop). You can also set the initial selection if desired.

Code

!include LogicLib.nsh
!include WinMessages.nsh ; WM_USER
!define TV_FIRST  0x1100
!define /math TVM_GETNEXTITEM ${TV_FIRST} + 10
!define /math TVM_SELECTITEM ${TV_FIRST} + 11
!define /math TVM_ENSUREVISIBLE ${TV_FIRST} + 20
!define TVGN_FIRSTVISIBLE 0x5
!define TVGN_CARET 0x9
!define BFFM_INITIALIZED 1
!if "${NSIS_CHAR_SIZE}" > 1
!define /math BFFM_SETSELECTION ${WM_USER} + 103
!else
!define /math BFFM_SETSELECTION ${WM_USER} + 102
!endif
 
 
Function SHParseDisplayName ; NSIS 2.51+ INPUT:Path OUTPUT:Pidl
Exch $1
Push $2
System::Call 'SHELL32::SHParseDisplayName(w r1, p 0, *p 0r2, i 0, *i 0)i'
${If} $2 P= 0
	System::Call 'SHELL32::ILCreateFromPath(t r1)p.r2' ; SHParseDisplayName is XP+, this works everywhere but is not as clever
${EndIf}
StrCpy $1 $2
Pop $2
Exch $1
FunctionEnd
 
Function BrowseForFolder ; NSIS 2.51+ INPUT:RootPath, HeadingText, InitialPathSelection OUTPUT:Path
System::Store S
Pop $3 ; InitialPathSelection or ""
Pop $2 ; HeadingText
Pop $1 ; RootPath or ""
!macro BrowseForFolder_PathToPidl Path Pidl
StrCpy ${Pidl} ""
${If} "${Path}" != ""
	Push "${Path}"
	Call SHParseDisplayName
	Pop ${Pidl}
${EndIf}
!macroend
!insertmacro BrowseForFolder_PathToPidl $1 $6
!insertmacro BrowseForFolder_PathToPidl $3 $5
System::Get "(p.R1, i.R2, p, p.R3)i R8R8" ; BFFCALLBACK
Pop $R9
System::Call '*(&t261 "")p.r7' ; pszDisplayName buffer
System::Call '*(p $hwndparent, pr6, pr7, t r2, i 0x41, kR9, pr5, i)p.r8' ; BROWSEINFO struct
System::Call 'SHELL32::SHBrowseForFolder(t)(pr8)p.r9' ; Using the (t) hack to force the correct A/W function
BFFCALLBACK_loop:
	StrCmp $R8 "callback1" 0 BFFCALLBACK_done
	${If} $R2 = ${BFFM_INITIALIZED}
	${AndIf} $R3 P<> 0
		SendMessage $R1 ${BFFM_SETSELECTION} 0 $R3
		System::Store S
		StrCpy $2 0
		StrCpy $3 0
		loop: ; BFFM_SETSELECTION is buggy and does not scroll to the new item so we find the treeview and do it manually
			FindWindow $2 "" "" $R1 $2 ; Assuming SysTreeView32 is a grandchild when using BIF_NEWDIALOGSTYLE
			IntCmp 0 $2 done
			FindWindow $3 "SysTreeView32" "" $2
			IntCmp 0 $3 loop
			SendMessage $3 ${TVM_GETNEXTITEM} ${TVGN_CARET} 0 $4
			IntCmp 0 $3 done
			System::Call 'USER32::PostMessage(p$3,i${TVM_ENSUREVISIBLE},p0,p$4)'
		done:
		System::Store L
	${EndIf}
	StrCpy $R8 0 ; Yep, the return value is in the same place as the callback id
	System::Call $R9
	goto BFFCALLBACK_loop
BFFCALLBACK_done:
System::Free $R9
${If} $9 Z<> 0
	System::Call 'SHELL32::SHGetPathFromIDList(p r9, t.s)i'
	System::Call 'OLE32::CoTaskMemFree(p r9)'
${Else}
	Push "" ; Error/cancel, return empty string
${EndIf}
System::Call 'OLE32::CoTaskMemFree(p r5)'
System::Call 'OLE32::CoTaskMemFree(p r6)'
System::Free $7
System::Free $8
System::Store L
FunctionEnd

Example

Section
Push "$Profile"
Push "Hello World"
Push "$AppData"
Call BrowseForFolder
Pop $0
DetailPrint BrowseForFolder=$0
SectionEnd