Announcement

Collapse
No announcement yet.

SHBrowseForFolder problem

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • SHBrowseForFolder problem

    Vista SP1 32-bit
    NSIS 2.41 (ANSI)

    I have 2 problems with this script fragment, and I'm stumped.

    1) The icon index always returns 0. I don't think my code is wrong.
    2) 9 times out of 10 the script crashes on exit. This is AFTER the System plugin is unloaded. The crash is either in ole32.dll or ntdll.dll. Again, I have no idea why.

    Any insight is appreciated!

    code:
    ;=== Program Details
    Name "Folder Test"
    OutFile "foldertest.exe"
    Caption "Folder Test"

    ;=== Runtime Switches
    CRCCheck On
    WindowIcon off
    SilentInstall Silent
    AutoCloseWindow True
    RequestExecutionLevel user
    XPStyle on

    ; Best Compression
    SetCompress Auto
    SetCompressor /SOLID lzma
    SetCompressorDictSize 32
    SetDatablockOptimize On

    Section
    System::Call /NOUNLOAD '*(&t 260)i .r0' ; $0 = address to namebuff
    ; BROWSEINFO struct
    System::Call /NOUNLOAD '*(i n, i n, i r0, t "Folder Dialog Title", i 0x40, i n, i n, i n)i .R0' ; $R0 = address of struct

    System::Call /NOUNLOAD 'shell32::SHBrowseForFolderA(i R0)i .r5'

    System::Call /NOUNLOAD '*$0(&t260 .r3)' ; read namebuff
    MessageBox MB_OK "Selected folder: $3"

    System::Call /NOUNLOAD 'shell32::SHGetPathFromIDListA(i r5, i $0)i'
    System::Call /NOUNLOAD '*$0(&t260 .r3)' ; read namebuff
    MessageBox MB_OK "Selected folder path: $3"

    System::Call /NOUNLOAD '*$R0(i,i,i,t,i,i,i, i .r2)' ; read icon index
    MessageBox MB_OK "Icon index: $2"

    ; cleanup
    System::Call /NOUNLOAD 'ole32::CoTaskMemFree(i r5)v' ; free returned PIDL
    System::Free /NOUNLOAD $0 ; free namebuff
    System::Free $R0 ; free BROWSEINFO

    MessageBox MB_OK "done"
    SectionEnd


  • #2
    I always use SHGetFileInfo to get a icon/iconindex, did not even know that BROWSEINFO had it. But the docs say icon index for folder, not file or drive (it failed on my documents also, so who knows) Do you even need the icon index? or just the HICON?

    You also really should add a HWND to BROWSEINFO...

    I played around with it a bit and the iconindex is 0 for me also every time. (XP SP2)

    code:

    Section
    System::Call /NOUNLOAD '*(&t261)i .r0' ; $0 = address to namebuff
    ; BROWSEINFO struct
    System::Call /NOUNLOAD '*(i $hwndparent,i, i r0, t "Folder Dialog Title", i 0x40|0x4000,i,i,i)i.R0'

    System::Call /NOUNLOAD 'shell32::SHBrowseForFolderA(i R0)i .r5'

    System::Call /NOUNLOAD '*$0(&t260 .r3)' ; read namebuff
    DetailPrint "ret = $5, Selected folder: $3"

    System::Call /NOUNLOAD 'shell32::SHGetPathFromIDListA(i r5, i $0)i'
    System::Call /NOUNLOAD '*$0(&t260 .r3)' ; read namebuff
    DetailPrint "Selected folder path: $3"

    System::Call /NOUNLOAD '*$R0(i,i,i,i,i,i,i,i.r2)' ; read icon index
    DetailPrint "Icon index: $2"

    ; cleanup
    System::Call /NOUNLOAD 'ole32::CoTaskMemFree(i r5)v' ; free returned PIDL
    System::Free /NOUNLOAD $0 ; free namebuff
    System::Free $R0 ; free BROWSEINFO

    DetailPrint "done-----------------"
    /*typedef struct _SHFILEINFO {
    HICON hIcon;
    int iIcon;
    DWORD dwAttributes;
    TCHAR szDisplayName[MAX_PATH];
    TCHAR szTypeName[80];
    } SHFILEINFO;*/
    !define STRUCTSIZE_SHFILEINFOA 352
    #DEFINE SHGFI_ICON 0x000000100
    System::Call '*(i,i,i,&t260,&t80)i.r0'
    System::Call 'shell32::SHGetFileInfoA(t "$3",i,i $0,i 352,i 0x100)i.r9'
    DetailPrint ret=$9,inpath=$3
    System::Call '*$0(i.r1,i.r2)'
    DetailPrint hIco=$1,icoIdx=$2
    System::Free $0
    FindWindow $0 "#32770" "" $HWNDPARENT
    GetDlgItem $0 $0 0x407
    SendMessage $0 ${STM_SETICON} $1 0

    SectionEnd

    note that it is also possible to use a PIDL with SHGetFileInfo (you did not specify the filesystemonly flag, so you could get virtual paths)

    Also, I'm using 2.42 so I don't have to bother with /NOUNLOAD
    IntOp $PostCount $PostCount + 1

    Comment


    • #3
      I don't need the icon index for the folder, but was just curious why it wasn't working.

      The bigger issue is why it's crashing consistently on exit on Vista. I'm assuming that it's not crashing on XP since you've tried it...

      Comment


      • #4
        I just checked with 2.42 and removing /NOUNLOAD, still crashes most of the time.

        Comment


        • #5
          and if you don't call CoTaskMemFree or free the buffers?
          IntOp $PostCount $PostCount + 1

          Comment


          • #6
            Still crashes unfortunately. And I've seen the faulting module as both ole32.dll and ntdll.dll.

            Comment


            • #7
              You have a space between &t and 260. The buffer you allocate is therefore sized 1 bytes and the initial value of that string is "260".
              NSIS FAQ | NSIS Home Page | Donate $
              "I hear and I forget. I see and I remember. I do and I understand." -- Confucius

              Comment


              • #8
                Wow. Thanks kickik, that did it. I can't believe that was the problem. So it was a buffer overrun type of crash then?

                Well, since I have a topic already here instead of starting a new one, can you briefly explain how the &l is used for structure size? The manual doesn't have an example of usage, and I don't see the N for N bytes after it like the others. Thanks again!

                Comment


                • #9
                  Yes, it caused a heap overflow.

                  &l is used to get the structure size. It doesn't really set a struct member but only gets the size.
                  code:
                  System::Call "*${stBITMAP} (_, &l0 .R7) .R9"
                  System::Call "${sysGetObject} (r6, R7, R9)"

                  NSIS FAQ | NSIS Home Page | Donate $
                  "I hear and I forget. I see and I remember. I do and I understand." -- Confucius

                  Comment


                  • #10
                    Ok. So in the above example, the _ character 'skips' all the params and lets you add one to the end of the struct?

                    Comment


                    • #11
                      Indeed.
                      NSIS FAQ | NSIS Home Page | Donate $
                      "I hear and I forget. I see and I remember. I do and I understand." -- Confucius

                      Comment


                      • #12
                        Thanks for all the help!

                        Maybe add those few tips about &l and _ to the System Plugin Documentation when you have an opportunity.

                        Keep up the great work!

                        Comment


                        • #13
                          DOH, not sure how I missed this one, copy&paste without checking is baaad mmmkay
                          IntOp $PostCount $PostCount + 1

                          Comment


                          • #14
                            I need some help with SHBrowseForFolder API call in NSIS. I need 2 things to implement:
                            1.) browse for folders in selected root folder (so other folders outside of defined root folder are not dislpayed at all)
                            2.) preselect an existing folder.

                            My problem is that root folder must be served as pointer to PIDL, but ParseDisplayName API call always fails for me. The second problem, that the preselected folder parameter is ignored.

                            The code:
                            code:

                            Function BrowseFolderByRoot
                            ;Input1: A handle to the owner window for the dialog box on top of the stacks.
                            ;Input2: The location of the root folder from which to start browsing
                            ; as 2nd element of stacks. Only the specified folder and its
                            ; subfolders in the namespace hierarchy appear in the dialog box.
                            ; This member can be NULL; in that case, a default location is used.
                            ;Input3: Initially selected folder as 3rd element of stacks
                            ;Input4: Text above the tree view control in the dialog box as 4th element of stacks.
                            ;Output: Selected path on top of the stacks.
                            ;
                            Exch $0
                            Exch
                            Exch $1
                            Exch 2
                            Exch $2
                            Exch 1
                            Exch 3
                            Exch $3
                            Push $4
                            Push $5
                            Push $6
                            Push $7
                            Push $8
                            Push $9
                            IntOp $4 $1 + 0
                            StrCpy $7 "$2"
                            !define MAX_PATH 260 ;Windows shell is limited to 260 characters(with null terminating char.)


                            System::Call /NoUnload "ole32::CoInitialize(*i.r8) i.r5"
                            StrCmp $5 "error" +3 +4 ;error means error
                            IntCmp $5 0 +3 0 0 ; 0 means success, other values means error
                            messageBox MB_ICONEXCLAMATION "DEBUG: CoInitialize error!"
                            Goto Error

                            System::Call /NoUnload "shell32::SHGetMalloc(*i.r9) i.r5"
                            StrCmp $5 "error" +3 +4 ;error means error
                            IntCmp $5 0 +3 0 0 ; 0 means success, other values means error
                            messageBox MB_ICONEXCLAMATION "DEBUG: SHGetMalloc error!"
                            Goto Error

                            System::Call /NoUnload "shell32::SHGetDesktopFolder(*i.r6) i.r5"
                            StrCmp $5 "error" +3 +4 ;error means error
                            IntCmp $5 0 +3 0 0 ; 0 means success, other values means error
                            messageBox MB_ICONEXCLAMATION "DEBUG: SHGetDesktopFolder error!"
                            Goto Error

                            System::Call /NoUnload "*(&w${MAX_PATH}) i.r2" ;Buffer allocation for unicode path
                            ;Path string must be converted to Unicode
                            ;CodePage=0 (CP_ACP), dwFlags=0, string-to-convert, length=-1(null terminated),
                            System::Call /NoUnload "kernel32::MultiByteToWideChar(i 0, i 0, t r1, i -1, i r2, i ${MAX_PATH}) i.r5"
                            IntCmp $5 0 0 0 +3 ; Larger value than 0 indicates success
                            messageBox MB_ICONEXCLAMATION "DEBUG: MultiByteToWideChar error!"
                            Goto Error

                            ;Convert Unicode path string to PIDL
                            System::Call /NoUnload "shell32::ParseDisplayName(i n, i n, i r2, i.n, *i.r1, i n) i.r5"
                            messageBox MB_ICONEXCLAMATION "DEBUG: 1=$1; 2=$2; 5=$5; 7=$7"
                            IntCmp $1 0 +3 +6 +6 ;0 means error
                            StrCmp $5 "error" +2 +5 ;error means error
                            IntCmp $5 0 +4 0 0 ;0 means success, other values means error
                            messageBox MB_ICONEXCLAMATION "DEBUG: ParseDisplayName error!"
                            StrCpy $1 ""
                            ; Goto Error
                            Nop

                            ;Struct to handle the select folder dialog.
                            IntOp $4 0 + 1 ;0x01=BIF_RETURNONLYFSDIRS; 0x04=BIF_STATUSTEXT
                            System::Call /NoUnload "*(i $0, i r1, t r7, t '$3', i $4, i n, i n, i n) i.r5"

                            ;Display the dialog window
                            System::Call /NoUnload "shell32::SHBrowseForFolder(i r5) i.r4"
                            IntCmp $4 0 0 +2 +2 ;0 means user cancelled, other values means success
                            Goto Canceled
                            ;Read out the selected path
                            System::Call /NoUnload "shell32::SHGetPathFromIDList(i r4, t.r0) i.r6"
                            System::Call /NoUnload "ole32::CoTaskMemFree(i r4)" ;Free up the memory assigned for the output PIDL
                            IntCmp $6 0 0 +3 +3 ;0 means error, other values means success
                            messageBox MB_ICONEXCLAMATION "DEBUG: SHGetPathFromIDList error!"
                            Goto Error
                            Error:
                            StrCpy $0 "error"
                            Goto Finish
                            Canceled:
                            StrCpy $0 ""
                            Finish:
                            System::Free $6
                            System::Free $9
                            System::Call /NoUnload "ole32::CoUninitialize()"
                            System::Free $1
                            System::Free $2
                            System::Free $5
                            System::Free $6
                            System::Free $7
                            Pop $9
                            Pop $8
                            Pop $7
                            Pop $6
                            Pop $5
                            Pop $4
                            Pop $3
                            Pop $2
                            Pop $1
                            Exch $0
                            FunctionEnd

                            The compilable script is in attachement.

                            PS: Sorry, my english is bad.
                            Attached Files

                            Comment


                            • #15
                              Originally Posted by PPeti66x View Post
                              I need some help with SHBrowseForFolder API call in NSIS...
                              What is shell32::ParseDisplayName? You can also just let the system plug-in convert to/from wide strings for you with the 'w' string type.
                              IntOp $PostCount $PostCount + 1

                              Comment

                              Working...
                              X