具有特定驱动器的 NSIS SelectFolderDialog
NSIS SelectFolderDialog with specific drives
在 NSIS SelectFolderDialog 中,我能否以某种方式仅显示 HDD 树?我需要限制用户选择任何不在 HDD 上的目录。
要完全控制显示哪些项目,您必须编写一个自定义 NSIS 插件,该插件调用 SHBrowseForFolder
并提供 IFolderFilterSite.
的实现
另一种方法是在用户选择了您无法接受的内容时禁用“确定”按钮:
!include LogicLib.nsh
!ifndef DRIVE_FIXED
!define DRIVE_FIXED 3
!endif
; This function decides which items are acceptable
Function MyFolderValidator ; Input: =PIDL, =Path Output:[=10=]=0 if invalid
System::Call 'KERNEL32::GetVolumePathName(tr2, t.r4, i ${NSIS_MAX_STRLEN})i.r5'
${If} <> 0
StrCpy
${ElseIf} == "error" ; GetVolumePathName is Win2000+
StrCpy 3 ; GetDriveType only accepts root paths (This will not work for UNC)
${EndIf}
System::Call 'KERNEL32::GetDriveType(tr2)i.r3'
${If} = ${DRIVE_FIXED}
StrCpy [=10=] 1 ; Allow this path
${Else}
StrCpy [=10=] 0 ; Don't allow this path
${EndIf}
FunctionEnd
!include WinMessages.nsh
!define /math BFFM_ENABLEOK ${WM_USER} + 101
!define BFFM_SELCHANGED 2
!define BFFM_VALIDATEFAILEDA 3
!define BFFM_VALIDATEFAILEDW 4
!if "${NSIS_CHAR_SIZE}" > 1
!define BFFM_VALIDATEFAILED ${BFFM_VALIDATEFAILEDW}
!else
!define BFFM_VALIDATEFAILED ${BFFM_VALIDATEFAILEDA}
!endif
!if "${NSIS_PTR_SIZE}" <= 4
Function BrowseForValidatedFolder ; NSIS 2.51+
System::Store S
Pop ; HeadingText
Pop ; ValidatorFunc
System::Get "(p.R1, i.R2, p.R3, p)i R8R8" ; BFFCALLBACK
Pop $R9 ; The system plug-in callback function
StrCpy "kR9" ; SHBrowseForFolder callback parameter
System::Call '*(&t261 "")p.r7' ; pszDisplayName buffer
System::Call '*(p $hwndparent, p0, pr7, t r2, i 0x51, , p0, i)p.r8' ; BROWSEINFO struct
!if "${NSIS_CHAR_SIZE}" > 1
System::Call 'SHELL32::SHBrowseForFolderW(pr8)p.r9'
!else
System::Call 'SHELL32::SHBrowseForFolderA(pr8)p.r9'
!endif
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_SELCHANGED}
${AndIf} $R3 P<> 0
System::Store S
StrCpy [=10=]
StrCpy $R3
System::Call 'SHELL32::SHGetPathFromIDList(p $R3, t.r2)'
Call [=10=]
SendMessage $R1 ${BFFM_ENABLEOK} 0 [=10=]
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 ${|} ; Keep the dialog open
System::Call $R9
goto BFFCALLBACK_loop
BFFCALLBACK_done:
System::Free $R9 ; BFFCALLBACK
System::Free ; pszDisplayName
System::Free ; BROWSEINFO
${If} 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::Store L
FunctionEnd
!endif
!macro BrowseForValidatedFolder HeadingText ValidatorFuncName VarResult
GetFunctionAddress ${VarResult} ${ValidatorFuncName}
Push ${VarResult}
Push "${HeadingText}"
Call BrowseForValidatedFolder
Pop ${VarResult}
!macroend
Section
!insertmacro BrowseForValidatedFolder "Choose a location for blah blah" MyFolderValidator [=10=]
${If} [=10=] != ""
MessageBox mb_ok "Result: [=10=]"
${Else}
MessageBox mb_iconstop "User cancelled"
${EndIf}
SectionEnd
在 NSIS SelectFolderDialog 中,我能否以某种方式仅显示 HDD 树?我需要限制用户选择任何不在 HDD 上的目录。
要完全控制显示哪些项目,您必须编写一个自定义 NSIS 插件,该插件调用 SHBrowseForFolder
并提供 IFolderFilterSite.
另一种方法是在用户选择了您无法接受的内容时禁用“确定”按钮:
!include LogicLib.nsh
!ifndef DRIVE_FIXED
!define DRIVE_FIXED 3
!endif
; This function decides which items are acceptable
Function MyFolderValidator ; Input: =PIDL, =Path Output:[=10=]=0 if invalid
System::Call 'KERNEL32::GetVolumePathName(tr2, t.r4, i ${NSIS_MAX_STRLEN})i.r5'
${If} <> 0
StrCpy
${ElseIf} == "error" ; GetVolumePathName is Win2000+
StrCpy 3 ; GetDriveType only accepts root paths (This will not work for UNC)
${EndIf}
System::Call 'KERNEL32::GetDriveType(tr2)i.r3'
${If} = ${DRIVE_FIXED}
StrCpy [=10=] 1 ; Allow this path
${Else}
StrCpy [=10=] 0 ; Don't allow this path
${EndIf}
FunctionEnd
!include WinMessages.nsh
!define /math BFFM_ENABLEOK ${WM_USER} + 101
!define BFFM_SELCHANGED 2
!define BFFM_VALIDATEFAILEDA 3
!define BFFM_VALIDATEFAILEDW 4
!if "${NSIS_CHAR_SIZE}" > 1
!define BFFM_VALIDATEFAILED ${BFFM_VALIDATEFAILEDW}
!else
!define BFFM_VALIDATEFAILED ${BFFM_VALIDATEFAILEDA}
!endif
!if "${NSIS_PTR_SIZE}" <= 4
Function BrowseForValidatedFolder ; NSIS 2.51+
System::Store S
Pop ; HeadingText
Pop ; ValidatorFunc
System::Get "(p.R1, i.R2, p.R3, p)i R8R8" ; BFFCALLBACK
Pop $R9 ; The system plug-in callback function
StrCpy "kR9" ; SHBrowseForFolder callback parameter
System::Call '*(&t261 "")p.r7' ; pszDisplayName buffer
System::Call '*(p $hwndparent, p0, pr7, t r2, i 0x51, , p0, i)p.r8' ; BROWSEINFO struct
!if "${NSIS_CHAR_SIZE}" > 1
System::Call 'SHELL32::SHBrowseForFolderW(pr8)p.r9'
!else
System::Call 'SHELL32::SHBrowseForFolderA(pr8)p.r9'
!endif
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_SELCHANGED}
${AndIf} $R3 P<> 0
System::Store S
StrCpy [=10=]
StrCpy $R3
System::Call 'SHELL32::SHGetPathFromIDList(p $R3, t.r2)'
Call [=10=]
SendMessage $R1 ${BFFM_ENABLEOK} 0 [=10=]
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 ${|} ; Keep the dialog open
System::Call $R9
goto BFFCALLBACK_loop
BFFCALLBACK_done:
System::Free $R9 ; BFFCALLBACK
System::Free ; pszDisplayName
System::Free ; BROWSEINFO
${If} 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::Store L
FunctionEnd
!endif
!macro BrowseForValidatedFolder HeadingText ValidatorFuncName VarResult
GetFunctionAddress ${VarResult} ${ValidatorFuncName}
Push ${VarResult}
Push "${HeadingText}"
Call BrowseForValidatedFolder
Pop ${VarResult}
!macroend
Section
!insertmacro BrowseForValidatedFolder "Choose a location for blah blah" MyFolderValidator [=10=]
${If} [=10=] != ""
MessageBox mb_ok "Result: [=10=]"
${Else}
MessageBox mb_iconstop "User cancelled"
${EndIf}
SectionEnd