Announcement

Collapse
No announcement yet.

SHBROWSEFORFOLDER CODEPTR question

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

  • SHBROWSEFORFOLDER CODEPTR question

    I have this code from the forum. It works, but I would very much like to remove
    the %wm_user declarations:

    %bffm_setstatustext=%wm_user+100
    %bffm_setselection=%wm_user+102

    I just cant figure out whats going on with the line
    tbi.lpfncallback = CODEPTR(fbrowseproc) ' wassis all about?

    I want to loose the Callback function? fbrowseproc alltogether
    but when I do it no longer goes to my startdir.

    How do I do this pls?

    Code:
    TYPE browseinfo
         hwndowner AS LONG
         pidlroot AS LONG
         pszdisplayname AS ASCIIZ PTR
         lpsztitle AS ASCIIZ PTR
         ulflags AS LONG
         lpfncallback AS LONG
         lparam AS LONG
         iimage AS LONG
    END TYPE
    
    
    'Why dont I need these when i #INCLUDE     "WIN32API.INC"???
    'DECLARE FUNCTION SHBROWSEFORFOLDER   LIB "SHELL32.DLL" ALIAS "SHBrowseForFolder"   (lpbi AS BrowseInfo) AS LONG
    'DECLARE FUNCTION SHGETPATHFROMIDLIST LIB "SHELL32.DLL" ALIAS "SHGetPathFromIDList" (BYVAL pidList AS LONG, BYVAL lpBuffer AS LONG) AS LONG
    
    %bffm_setstatustext=%wm_user+100
    %bffm_setselection=%wm_user+102 
    
    '-----------------------------------------------------------------------------------------------------
    FUNCTION FolderDialog(hwnd AS LONG, title AS STRING, Dir AS ASCIIZ*%max_path ) AS LONG
        LOCAL zbuffer AS ASCIIZ*%max_path 
        LOCAL lpidlist AS LONG
        LOCAL zstartdir AS ASCIIZ*%max_path 
        LOCAL tbi AS browseinfo
        tbi.hwndowner    = hwnd
        tbi.lpsztitle    = STRPTR(title)
        zstartdir        = Dir
        tbi.ulflags      = &h0001 OR &h0002 OR &h0004
        tbi.pidlroot     = 0
        tbi.lpfncallback = CODEPTR(fbrowseproc) ' wassis all about?
        tbi.lparam       = VARPTR(zstartdir)
        lpidlist         = SHBROWSEFORFOLDER(tbi)
        IF lpidlist THEN
            zBuffer = SPACE$(256)
            CALL SHGETPATHFROMIDLIST (lpidlist&, Dir) ' Return the path
           'Dir = LEFT$(Buffer, INSTR(Buffer, CHR$(0)) - 1)
            FUNCTION = lpidlist
        END IF
    MSGBOX zstartdir
    END FUNCTION
    
    '-----------------------------------------------------------------------------------------------------
    FUNCTION fbrowseproc(BYVAL hwnd&, BYVAL msg&, BYVAL a&, BYVAL startdir&) AS LONG ' i wanna loose this alltogether
         STATIC zdir AS ASCIIZ*%max_path
          SELECT CASE msg&
           CASE 1
             SENDMESSAGE hwnd&, %bffm_setselection, %true, startdir&
           CASE 2
            IF SHGETPATHFROMIDLIST(a&,BYVAL VARPTR(zdir)) THEN
               SENDMESSAGE hwnd&, %bffm_setstatustext, 0, BYVAL VARPTR(zdir)
            END IF
          END SELECT
          FUNCTION=0
    END FUNCTION    
    
    '************************************************************************
    ------------------
    Kind Regards
    Mike



    [This message has been edited by Mike Trader (edited July 05, 2001).]

  • #2
    The callback is needed to verify that the dialog should highlight the specified initial folder... exactly as you noted. Without the start folder confirmation (Sending a %bffm_setselection message), the dialog will start with the default set to "My Computer" (or whatever it haappens to be called on the particular installation).

    This is "briefly" documented in WIN32.HLP.

    ------------------
    Lance
    PowerBASIC Support
    mailto:[email protected][email protected]</A>
    Lance
    mailto:[email protected]

    Comment


    • #3
      Thx Lance,

      I found Win32.hlp at: http://www.cs.virginia.edu/~lcc-win32/

      I read it, but i just dont get why its necessary to send a message to the main hDlg window?

      The Win32.hlp says:
      The browse dialog box calls this function to notify it about events.

      notify IT - I assume the IT is the SHBROWSEFORFOLDER Dialog box.

      So why are we sending a message to the HDlg window? There is nothing in that Callback function to process anything?
      What is accomplished by doing that?


      ------------------
      Kind Regards
      Mike

      Comment


      • #4
        The callback in your code is a simple callback rather than a DlgProc/WndProc callback, per se.

        That is, it is not the callback that processes every single SHBrowseForFolder dialog event. Rather, it is a user-defined callback that can receive/process a limited number of notifications from the SHBrowseForFolder dialog - events that your app may need to operate upon for some reason.

        For example, in one of my apps, I use the callback to verify whether the selected folder name is valid in the scope of my application, and if not, I send a %BFFM_ENABLEOK message to disable the OK button. See my very recent post for a snapshot of my callback which (tries to) prevent non-local folder names from being selected.

        A couple of tips for you:

        1. All references to 256 bytes should be replaced with the equate %MAX_PATH (which is 260).

        2. In the calling code, you need to free the memory used by the ID List after retrieving the folder name:
        Code:
        DECLARE SUB CoTaskMemFree LIB "ole32.dll" ALIAS "CoTaskMemFree" (BYVAL hMem AS LONG)
        ...
        IF lpidlist& THEN        
          ' zBuffer = SPACE$(%MAX_PATH) ' ASCIIZ strings do not need to be pre-initialized!
          CALL SHGETPATHFROMIDLIST (lpidlist&, zBuffer)
          CoTaskMemFree lpidlist&
          FUNCTION = lpidlist
        END IF
        (Note: in your calling code, your call to SHGetPathFromIDList passes a variable called "Dir", but this should be "zBuffer")

        3. In your callback, you are passing an ASCIIZ string to ShGetPathFromIDList, so you can pass the second parameter directly, rather than the BYVAL VARPTR(zDir) method you are using:
        Code:
        IF SHGETPATHFROMIDLIST(a&, zdir) THEN
        4. Since this callback uses the standard format of a WndProc/DlgProc (hWnd,wMsg,wParam,lParam), you can substitute a DDT CALLBACK FUNCTION and use the internal CBxxx system variables. This is purely optional, but makes the code slightly easier to write:
        Code:
        CALLBACK FUNCTION fbrowseproc
            DIM zdir AS ASCIIZ * %max_path
            IF CBMSG = %BFFM_INITIALIZED THEN
                DIALOG SEND CBHNDL, %BFFM_SETSELECTION, %TRUE, CBLPARAM
            ELSEIF CBMSG = %BFFM_SELCHANGED THEN
                CALL SHGetPathFromIDList(BYVAL CBWPARAM, zDir)
            END IF
        END FUNCTION
        I hope this helps!


        ------------------
        Lance
        PowerBASIC Support
        mailto:[email protected][email protected]</A>
        Lance
        mailto:[email protected]

        Comment


        • #5
          Mike I combined some fragments.
          Code:
             #Compile Exe
             #Register None
             #Dim All
             #Include "WIN32API.INC"
          
             %ID_FRAME1    = 101
             %ID_DIRECTORY = 102
             %ID_SELECTDIR = 103
          
             Declare Sub CoTaskMemFree Lib "ole32.dll" Alias "CoTaskMemFree" (pv As Long)
             
             %BIF_RETURNONLYFSDIRS   = 1
             %BIF_STATUSTEXT         = 4
             %BFFM_INITIALIZED       = 1
             %BFFM_SELCHANGED        = 2
             %BFFM_SETSTATUSTEXT     = %WM_USER + 100
             %BFFM_ENABLEOK          = %WM_USER + 101
             %BFFM_SETSELECTION      = %WM_USER + 102
                                              
             CallBack Function BrowseForFolderProc
                Dim TmpAsciiz As Asciiz * %MAX_PATH, StatusText As String, CurDrv As Asciiz * 3
                %maxNetResources = 1000
                Dim i As Long, hEnum As Long, d As Long, j As Long, k As Long
                Dim NetResources(%maxNetResources -1) As NETRESOURCE
          
                Select Case CbMsg
                   Case %BFFM_INITIALIZED
                      TmpAsciiz = CurDir$
                      SendMessage CbHndl, %BFFM_SETSELECTION, 1, VarPtr(TmpAsciiz)
                   Case %BFFM_SELCHANGED
                      SHGetPathFromIDList ByVal CbWparam, TmpAsciiz
                      StatusText = TmpAsciiz
                      
                      If Asc(TmpAsciiz, 2) <> 58 Then i = 0 Else i = Asc(TmpAsciiz, 1)
                      If i < 65 Or i > 80 Then i = 0 Else If i >= 97 Or i > 112 Then i = i - 32
                      
                      If i Then
                         CurDrv = Chr$(i) + ":"
                         Select Case GetDriveType(CurDrv)
                            Case %DRIVE_FIXED
                            Case %DRIVE_REMOTE
                               If IsFalse(WNetOpenEnum(%RESOURCE_CONNECTED, %RESOURCETYPE_ANY, 0&, ByVal 0&, hEnum)) Then
                                  If hEnum Then
                                     d = %maxNetResources: j = Len(NETRESOURCE) * d
                                     If IsFalse(WNetEnumResource(hEnum, d, NetResources(0), j)) Then
                                        For k = 0 To d - 1
                                           If UCase$(Left$(NetResources(k)[email protected], 2)) = CurDrv Then _
                                              StatusText = CurDrv + " = " + NetResources(k)[email protected] + " (not allowed)": i = 0
                                        Next
                                     End If
                                     WNetCloseEnum hEnum
                                  End If
                               End If
                            Case Else
                               i = 0: StatusText = "Illegal drive"
                          End Select
                      End If
                      If i Then If (GetAttr(TmpAsciiz) And %SUBDIR) <> %SUBDIR Then i = 0
                      Dialog Send CbHndl, %BFFM_ENABLEOK, 0, Abs(IsTrue(i))
                      SendMessage CbHndl, %BFFM_SETSTATUSTEXT, 0, StrPtr(StatusText)
                End Select
             End Function
          
             CallBack Function DlgProc
                Select Case CbMsg
                   Case %WM_INITDIALOG
                      Control Add Frame,   CbHndl, %ID_FRAME1, " Folder ", 5, 5, 260, 27
                      Control Add TextBox, CbHndl, %ID_DIRECTORY, "", 10, 15, 200, 12
                      Control Add Button,  CbHndl, %ID_SELECTDIR, "Browse", 215, 15, 45, 12
                   Case %WM_COMMAND
                      If CbCtl = %ID_SELECTDIR Then
                         Local pidl As Long, sBrowseInfo As BROWSEINFO, TmpAsciiz As Asciiz * %MAX_PATH, DisplayText As Asciiz * 100
                         DisplayText = "Select a folder on local hard disk"
                         SHGetSpecialFolderLocation CbHndl, 17, ByVal VarPtr(sBrowseInfo.pIDLRoot)
                         sBrowseInfo.hWndOwner = CbHndl
                         sBrowseInfo.lpfnCallback  = CodePtr(BrowseForFolderProc)
                         sBrowseInfo.pszDisplayName = VarPtr(TmpAsciiz)
                         sBrowseInfo.lpszTitle = VarPtr(DisplayText)
                         sBrowseInfo.ulFlags = %BIF_RETURNONLYFSDIRS Or %BIF_STATUSTEXT
                         pidl = SHBrowseForFolder(sBrowseInfo)
                         If pidl Then
                            If SHGetPathFromIDList(ByVal pidl, TmpAsciiz) Then Control Set Text CbHndl, %ID_DIRECTORY, TmpAsciiz
                            CoTaskMemFree ByVal pidl
                         End If
                         CoTaskMemFree ByVal sBrowseInfo.pIDLRoot
                                   
                      End If
                End Select
             End Function
          
             Function PbMain
                Dim hDlg As Long
                Dialog New 0, "Test", , , 270, 70, %WS_CAPTION Or %WS_SYSMENU Or %WS_MINIMIZEBOX To hDlg
                Dialog Show Modal hDlg Call DlgProc
             End Function
          BTW, one question, not SHBrowseForFolder relative.
          How to detect ia printer port connected to another PC ?
          Should be somewhere around WNet .., but lazy to search.
          Does somebody know ?


          ------------------
          E-MAIL: [email protected]

          Comment


          • #6
            Wow Semen & Lance,
            I have so much to learn!
            Thx for those pointers. I have streamlined my code as much as possible
            This is about as simple As I can get it I think.
            I use the function return to detect a CANCEL by the user
            and I pass the selected folder back using the Initial path variable.

            I like this cos I can write on line of code to call the function
            and check the result for a cancel:
            IF FolderDialog(0,"Select a folder", Path) THEN

            So here is what I got:

            Code:
            #COMPILE EXE "FindFldr.exe"
            #INCLUDE     "WIN32API.INC" ' Win API definitions
            
            '************************************************************************
            ' Find Folder Declarations
            '************************************************************************
            TYPE browseinfo
                 hwndowner AS LONG
                 pidlroot AS LONG
                 pszdisplayname AS ASCIIZ PTR
                 lpsztitle AS ASCIIZ PTR
                 ulflags AS LONG
                 lpfncallback AS LONG
                 lparam AS LONG
                 iimage AS LONG
            END TYPE
            
            %BFFM_SETSELECTION = %wm_user+102 ' must be this value (102)
            
            DECLARE SUB CoTaskMemFree LIB "ole32.dll" ALIAS "CoTaskMemFree" (BYVAL hMem AS LONG)
            
            '************************************************************************
            ' Find Folder
            '************************************************************************
            CALLBACK FUNCTION fbrowseproc
                DIM zdir AS ASCIIZ*%max_path
                IF CBMSG = 1 THEN DIALOG SEND CBHNDL, %BFFM_SETSELECTION, %TRUE, CBLPARAM ' %BFFM_INITIALIZED     
                IF CBMSG = 2 THEN CALL SHGETPATHFROMIDLIST(BYVAL CBWPARAM, zDir) ' %BFFM_SELCHANGED
            END FUNCTION    
            
            FUNCTION FolderDialog(hwnd AS LONG, title AS STRING, Dir AS ASCIIZ*%max_path ) AS LONG
                LOCAL zbuffer AS ASCIIZ*%max_path 
                LOCAL lpidlist AS LONG
                LOCAL zstartdir AS ASCIIZ*%max_path 
                LOCAL tbi AS browseinfo
                tbi.hwndowner    = hwnd
                tbi.lpsztitle    = STRPTR(title)
                zstartdir        = Dir
                tbi.ulflags      = &h0001 OR &h0002 OR &h0004
                tbi.pidlroot     = 0
                tbi.lpfncallback = CODEPTR(fbrowseproc) ' Select Starting folder
                tbi.lparam       = VARPTR(zstartdir)
                lpidlist         = SHBROWSEFORFOLDER(tbi)
                IF lpidlist THEN
                    CALL SHGETPATHFROMIDLIST (lpidlist&, Dir) ' Return the path
                    CALL CoTaskMemFree(lpidlist&)
                    FUNCTION = 1 ' User choose a Folder (0 if he hits Cancel)
                END IF
            END FUNCTION
            
            '************************************************************************
            FUNCTION PBMAIN()AS LONG
            LOCAL Path AS ASCIIZ*%max_path 
            
                Path   = "C:\PBDLL60\BIN" ' Starting Directory
                IF FolderDialog(0,"Select a folder", Path) THEN 
                    MSGBOX Path
                END IF
            
            END FUNCTION
            '************************************************************************

            ------------------
            Kind Regards
            Mike

            [This message has been edited by Mike Trader (edited July 06, 2001).]

            Comment

            Working...
            X