Browse for Folder: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
(Disabled callbacks on everything except NSIS 3 x86)
(Hacked around bug in 2.51)
Line 1: Line 1:
{{PageAuthor|Anders}}
{{PageAuthor|Anders}}


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 if you are using NSIS 3 32-bit.
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 ==
== Code ==
Line 53: Line 53:
!insertmacro BrowseForFolder_PathToPidl $3 $5
!insertmacro BrowseForFolder_PathToPidl $3 $5
!if "${NSIS_PTR_SIZE}" > 4 ; Callbacks currently not supported on AMD64
!if "${NSIS_PTR_SIZE}" > 4 ; Callbacks currently not supported on AMD64
StrCpy $4 "p0"
StrCpy $R8 ""
StrCpy $R9 0
!else if ! 0b1 ; And 2.51 has buggy callback numbers
StrCpy $4 "p0"
StrCpy $4 "p0"
StrCpy $R8 ""
StrCpy $R8 ""
Line 69: Line 65:
System::Call 'SHELL32::SHBrowseForFolder(t)(pr8)p.r9' ; Using the (t) hack to force the correct A/W function
System::Call 'SHELL32::SHBrowseForFolder(t)(pr8)p.r9' ; Using the (t) hack to force the correct A/W function
BFFCALLBACK_loop:
BFFCALLBACK_loop:
StrCmp $R8 "callback1" 0 BFFCALLBACK_done
StrCpy $R8 $R8 8 ; HACKHACK: Working around 2.x bug where the callback IDs are never released
StrCmp $R8 "callback" 0 BFFCALLBACK_done
${If} $R2 = ${BFFM_INITIALIZED}
${If} $R2 = ${BFFM_INITIALIZED}
${AndIf} $R3 P<> 0
${AndIf} $R3 P<> 0

Revision as of 01:36, 16 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
!define BFFM_VALIDATEFAILEDA 3
!define BFFM_VALIDATEFAILEDW 4
!if "${NSIS_CHAR_SIZE}" > 1
!define BFFM_VALIDATEFAILED ${BFFM_VALIDATEFAILEDW}
!define /math BFFM_SETSELECTION ${WM_USER} + 103
!else
!define BFFM_VALIDATEFAILED ${BFFM_VALIDATEFAILEDA}
!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
!if "${NSIS_PTR_SIZE}" > 4 ; Callbacks currently not supported on AMD64
StrCpy $4 "p0"
StrCpy $R8 ""
StrCpy $R9 0
!else
System::Get "(p.R1, i.R2, p, p.R3)i R8R8" ; BFFCALLBACK
Pop $R9
StrCpy $4 "kR9"
!endif
System::Call '*(&t261 "")p.r7' ; pszDisplayName buffer
System::Call '*(p $hwndparent, pr6, pr7, t r2, i 0x41, $4, 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:
	StrCpy $R8 $R8 8 ; HACKHACK: Working around 2.x bug where the callback IDs are never released
	StrCmp $R8 "callback" 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
	${IfThen} $R2 = ${BFFM_VALIDATEFAILED} ${|} StrCpy $R8 1 ${|}
	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