70. How do I open SHBrowseForFolder at a pre-determined location ?
SHBrowseForFolder is a very useful function, but it would be even more useful
if you could open it at a given location, such as the last folder the user
accessed. This makes your app much more user-friendly if the user is
repetitively working on files in the same location. There's precious few things
more irritating than an app that can't remember where it was a scant few minutes
ago...
The secret here is to make use of the function's callback interface. The
BROWSEINFO struct looks like this:
typedef struct _browseinfo
{ HWND hwndOwner;
LPCITEMIDLIST pidlRoot;
LPTSTR pszDisplayName;
LPCTSTR lpszTitle;
UINT ulFlags;
BFFCALLBACK lpfn;
LPARAM lParam;
int iImage;
} BROWSEINFO
Note that BFFCALLBACK member ? You can set
that up to point at a function of your own devising. The browse window calls
this function once its up and running, and you can then send a message to the
browse window to tell it to go to the pre-defined place you stored in the
structure earlier (boy, this is counter-intuitive, right ?).
So, let's say we have a button which
launches the browse dialog. We start by getting a pointer to the
Shell allocators IMalloc interface, in order to reserve some memory to store the
PIDL we'll need later. We then set up the BROWSEINFO struct, then finally call
SHBrowseForFolder. This code is storing the folder path in an edit control,
referenced by the ClassWizard-created member variable m_ctrl_FolderSrcEdit
void
CMyDlg::OnSrcFolderBrowseBtnClick()
{
LPMALLOC pMalloc;
CString csInitial;
BROWSEINFO bi;
LPSTR lpBuffer;
LPITEMIDLIST pidlBrowse; //PIDL selected by user
CString csSrcFolder;
// We need to get a handle to
the Shell allocator's IMalloc interface
if (!SUCCEEDED
(SHGetMalloc (&pMalloc)))
{
AfxMessageBox
("Unable to link to Shell
IMalloc interface");
return;
}
// Allocate a buffer to
receive browse information.
lpBuffer = (LPSTR) (pMalloc->Alloc (_MAX_PATH));
if (lpBuffer
== NULL) return;
// Fill in the BROWSEINFO
structure.
bi.hwndOwner = GetSafeHwnd ();
bi.pidlRoot = NULL;
bi.pszDisplayName = lpBuffer;
bi.lpszTitle = "Select
source folder for files";
bi.ulFlags = 0;
// Add your own function here
to retrieve a stored starting folder :
csInitial = GetInitialFolder ();
if (csInitial.GetLength()
> 0)
{
// Set the
callback Fn pointer to my function:
bi.lpfn =
SetSelProc;
// Set the
lParam to point at my buffer containing the folder:
bi.lParam = (LPARAM)(csInitial.GetBuffer (1));
csInitial.ReleaseBuffer();
}
else
{
bi.lpfn = NULL;
bi.lParam = 0;
}
// Browse for a folder and
return its PIDL.
pidlBrowse = SHBrowseForFolder(&bi);
if (pidlBrowse
!= NULL)
{
SHGetPathFromIDList(pidlBrowse, lpBuffer);
csSrcFolder = lpBuffer;
m_ctrl_FolderSrcEdit.SetWindowText (csSrcFolder);
// Free the
PIDL returned by SHBrowseForFolder.
pMalloc -> Free (pidlBrowse);
}
// Clean up.
pMalloc->Free (lpBuffer);
}
and here's the callback function. It sends a BFFM_SETSELECTION message back
to the browse window, passing in the LPARAM value we set up in the BROWSEINFO
struct above.
//
------------------------------------------------------------------------
int CALLBACK SetSelProc (HWND hWnd,
UINT uMsg,
LPARAM lParam,
LPARAM lpData)
{
if (uMsg==BFFM_INITIALIZED)
{
::SendMessage(hWnd,
BFFM_SETSELECTION, TRUE, lpData );
}
return 0;
}
and that's all there is to it. But you'd be hard put to figure out it
worked in this weird way.