• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2421
  • Last Modified:

ShBrowseForFolder with Make New Folder button

Could someone please post an example of how to call ShBrowseForFolder which displays the Make New Folder button, and which uses a callback function to set the initial directory.

Thanks
0
JohnStevenson
Asked:
JohnStevenson
  • 2
  • 2
1 Solution
 
Slick812Commented:
are you asking how to add a "Make New Folder button"  to a ShBrowseForFolder  Dialog box?
0
 
JohnStevensonAuthor Commented:
No, I know how to do this, by adding the BIF_NEWDIALOGSTYLE flag to the ulFlags parameterof the BrowseInfo structure.

My problem is that when I do this, I get a working "Make New Folder" button but I loose the initial directory setting of the folder dialog. Here's the code:

function BrowseCallbackProc(hwnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer; stdcall;
var
  Path: array[0..MAX_PATH] of Char;
  ptPos: TPoint;
  Rect : TRect;

begin

  case uMsg of
    BFFM_INITIALIZED:
      begin
        {Position folder window}
        GetWindowRect(hWnd, Rect);
        ptPos := GetFormPosition(frmMain, Rect.Right - Rect.Left,
          Rect.Bottom - Rect.Top, 1);
        MoveWindow(hWnd, ptPos.X, ptPos.Y, Rect.Right - Rect.Left,
          Rect.Bottom - Rect.Top, True);
        SendMessage(hwnd, BFFM_SETSELECTION, 1, lpData);
        SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, lpData);
      end;
    BFFM_SELCHANGED:
      begin
        {Set the status text to the currently selected path.}
        if SHGetPathFromIDList(Pointer(lParam), Path) then
          SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, Integer(@Path));
      end;
  end;

  Result := 0;

end; { BrowseCallbackProc }


function SelectDirectory(const Caption, InitialDir: string; const Root: WideString;
  ShowStatus: Boolean; out Directory: string): Boolean;
var BrowseInfo: TBrowseInfo;
    Buffer: PChar;
    RootItemIDList,
    ItemIDList: PItemIDList;
    ShellMalloc: IMalloc;
    IDesktopFolder: IShellFolder;
    Eaten, Flags: LongWord;
    Windows: Pointer;
    Path: String;

begin

  Result := False;

  Directory := '';
  Path := InitialDir;

  if (Length(Path) > 0) and (Path[Length(Path)] = '\') then
    Delete(Path, Length(Path), 1);

  FillChar(BrowseInfo, SizeOf(BrowseInfo), 0);

  if (ShGetMalloc(ShellMalloc) = S_OK) and (ShellMalloc <> nil) then
  begin

    Buffer := ShellMalloc.Alloc(MAX_PATH);

    try
      SHGetDesktopFolder(IDesktopFolder);
      IDesktopFolder.ParseDisplayName(Application.Handle, nil, PWideChar(Root),
        Eaten, RootItemIDList, Flags);

      with BrowseInfo do
      begin
        hwndOwner := Application.Handle;
        pidlRoot := RootItemIDList;
        pszDisplayName := Buffer;
        lpszTitle := PChar(Caption);
        ulFlags := BIF_RETURNONLYFSDIRS or BIF_NEWDIALOGSTYLE;

        if ShowStatus then
          ulFlags := ulFlags or BIF_STATUSTEXT;

        lParam := Integer(PChar(Path));
        lpfn := BrowseCallbackProc;

      end;
 
      Result := ItemIDList <> nil;

      if Result then
      begin
        ShGetPathFromIDList(ItemIDList, Buffer);
        ShellMalloc.Free(ItemIDList);
        Directory := Buffer;
      end;

    finally
      ShellMalloc.Free(Buffer);
    end;

  end;

end; { SelectDirectory }
0
 
Slick812Commented:
OK, here is some code that I tried, and it seems to be able to do the browse dialog with the required stuff, Although, when I did it I could NOT get a Status Text to show when I used the BIF_NEWDIALOGSTYLE, no matter what I did, so I added a static Label and just set the text for that, I do Not have time now to fully test this, but it worked on my machine with windows XP

OH, the  MAX_PATH  for a maximum text length went out with win 95 first edition, I do not think that is a valid setting for win XP, I usually use a 2 KB buffer, but as far as I have seen there is NO LIMIT for the text path for XP in shell ops


implementation

{$R *.DFM}

var
hStatic1: Integer;



function BrowseCallbackProc(hWnd,iMsg, lParam, pData: Integer): Integer; stdcall;
//function BrowseCallbackProc(hwnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer; stdcall;
var
  Path: array[0..2047] of Char;
  ptPos: TPoint;
  Rect : TRect;

begin
  case iMsg of
    BFFM_INITIALIZED:
      begin
        {Position folder window}
        GetWindowRect(hWnd, Rect);
        //ptPos := GetFormPosition(frmMain, Rect.Right - Rect.Left,
        //  Rect.Bottom - Rect.Top, 1);
        ptPos.x := Form1.Left+20;
        ptPos.y := Form1.Top+20;
        MoveWindow(hWnd, ptPos.X, ptPos.Y, Rect.Right - Rect.Left,
          Rect.Bottom - Rect.Top, False);
        hStatic1 := CreateWindow('Static', PChar(OleStrToString(PWideChar(pData))){'Number of Messages'},
         WS_VISIBLE or WS_CHILD,6,26,(Rect.Right - Rect.Left)+52,32,hWnd,0,hInstance,nil);
         SendMessage(hStatic1,WM_SETFONT,GetStockObject(ANSI_VAR_FONT),0);
        if pData > 0 then
        begin
        SendMessage(hwnd, WM_USER + 103{BFFM_SETSELECTION}, 1, pData);
        //SendMessage(hwnd, WM_USER + 104{BFFM_SETSTATUSTEXT}, 0, pData);
        end;
//define BFFM_SETSELECTIONA      (WM_USER + 102)
//define BFFM_SETSELECTIONW      (WM_USER + 103)
//define BFFM_SETSTATUSTEXTW     (WM_USER + 104)
      end;
    BFFM_SELCHANGED:
      begin
        {Set the status text to the currently selected path.}
        //if SHGetPathFromIDList(PItemIDList(LParam), ListFolder) then
        if SHGetPathFromIDList(Pointer(lParam), Path) then
          //SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, Integer(@Path));
          SetWindowText(hStatic1, Path);
      end;
  end;

  Result := 0;

end; { BrowseCallbackProc }

//function SelectDirectory(const Caption, InitialDir: string; const Root: WideString;
//  ShowStatus: Boolean; out Directory: string): Boolean;
function SelectDirectory(const Caption, InitialDir: string; const Root: String;
  ShowStatus: Boolean; out Directory: string): Boolean;
var BrowseInfo: TBrowseInfo;
    //Buffer: PChar;
    CharBuf: array[0..2047] of Char;
    RootItemIDList,
    pBrowseIDL: PItemIDList;
    ShMemAlloc: IMalloc;
    IDesktopFolder: IShellFolder;
    Eaten, Flags: LongWord;
    //Windows: Pointer;
    Path: String;

begin
  Result := False;

  Directory := '';
  Path := InitialDir;

  if (Length(Path) > 0) and (Path[Length(Path)] = '\') then
    Delete(Path, Length(Path), 1);

  FillChar(BrowseInfo, SizeOf(BrowseInfo), 0);

  if (ShGetMalloc(ShMemAlloc) = S_OK) and (ShMemAlloc <> nil) then
  begin

    //Buffer := ShellMalloc.Alloc(2048);

    //try
      SHGetDesktopFolder(IDesktopFolder);
    //IDesktopFolder.ParseDisplayName(Application.Handle, nil, PWideChar(Root),
    //    Eaten, RootItemIDList, Flags);
      IDesktopFolder.ParseDisplayName(Application.Handle, nil, PWideChar(WideString(Root)),
        Eaten, RootItemIDList, Flags);

      with BrowseInfo do
      begin
        hwndOwner := Application.Handle;
        pidlRoot := RootItemIDList;
        pszDisplayName := @CharBuf;
        lpszTitle := PChar(Caption);
        ulFlags := BIF_RETURNONLYFSDIRS or $40{or BIF_NEWDIALOGSTYLE};
 //define BIF_NEWDIALOGSTYLE     0x0040
        //if ShowStatus then
        //  ulFlags := ulFlags or BIF_STATUSTEXT;

        //lParam := Integer(PChar(Path));
        lParam := Integer(PWideChar(WideString(Path)));
        lpfn := @BrowseCallbackProc;

      end;
      pBrowseIDL := SHBrowseForFolder(BrowseInfo);
      Result := pBrowseIDL <> nil;

      if Result then
      begin
        SHGetPathFromIDList(pBrowseIDL, @CharBuf);
        ShMemAlloc.Free(pBrowseIDL);
        Directory := CharBuf;
      end;

    //finally
      //ShellMalloc.Free(Buffer);
    //end;

  end;

end;

procedure TForm1.sbut_Browse4FolderClick(Sender: TObject);
var
Dir1: String;
begin
if SelectDirectory('A Browse Dialog WHAT', 'C:\Stuff\Games', 'C:\', True, Dir1) then
  Showmessage(Dir1);
end;
0
 
JohnStevensonAuthor Commented:
Thanks Slick812.

I got it working using your sample. Incidentally, using BFFM_SETEXPANDED (WM_USER + 106) in the callback and passing a Pidl rather than a pointer to a wide string, seems to work better than using BFFM_SETSELECTION. I had problems with this when my BrowseInfo.pidlRoot was something other than the Desktop (or null).
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 2
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now