Link to home
Start Free TrialLog in
Avatar of JohnStevenson
JohnStevenson

asked on

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
Avatar of Member_2_248744
Member_2_248744
Flag of United States of America image

are you asking how to add a "Make New Folder button"  to a ShBrowseForFolder  Dialog box?
Avatar of JohnStevenson
JohnStevenson

ASKER

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 }
ASKER CERTIFIED SOLUTION
Avatar of Member_2_248744
Member_2_248744
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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).