|
SBJ wrote: Can this technique be used to select a special folder, say My Documents?
Yes, set WPARAM to FALSE, and set LPARAM to the PIDL for My Documents.
Regards,
Nish
|
|
|
|
|
|
Here is a version that allows you to specify a starting location, but you cannot go up from it, only down:
bool GetFolder(std::string& folderpath, LPCTSTR szCaption = NULL, LPCWSTR szStartPath = NULL, HWND hOwner = NULL)
{
bool retVal = false;
LPITEMIDLIST pIDLRoot = NULL;
if (szStartPath != NULL)
{
LPSHELLFOLDER pShellFolder = NULL;
HRESULT hResult = NULL;
ULONG chUsed = 0L;
BSTR bstrPath = NULL;
bstrPath = ::SysAllocString ( szStartPath );
if ( NULL != bstrPath )
{
if (::SHGetDesktopFolder (&pShellFolder) == NOERROR)
{
hResult =
pShellFolder->ParseDisplayName
(
NULL,
NULL,
bstrPath,
&chUsed,
&pIDLRoot,
NULL
);
if (FAILED(hResult))
{
pIDLRoot = NULL;
}
pShellFolder->Release();
}
}
::SysFreeString(bstrPath);
}
BROWSEINFO bi;
memset(&bi, 0, sizeof(bi));
bi.pidlRoot = pIDLRoot;
bi.ulFlags = BIF_USENEWUI;
bi.hwndOwner = hOwner;
bi.lpszTitle = szCaption;
::OleInitialize(NULL);
LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi);
if(pIDL != NULL)
{
char buffer[_MAX_PATH] = {'\0'};
if(::SHGetPathFromIDList(pIDL, buffer) != 0)
{
folderpath = buffer;
retVal = true;
}
::CoTaskMemFree(pIDL);
}
::OleUninitialize();
return retVal;
}
~Nitron.
ññòòïðïðB A start
|
|
|
|
|
I have something for what you are looking for. It's a wrapper class called CShellHelpers that execute all the commands needed to :
1 - Set the starting path of SHBrowseForFolder before opening of the dialog
2 - Then Retrieve the selected path when the dialog is closed
With this wrapper, you can go up from your starting location. It use the BFFM_INITIALIZED message in the BrowseCtrlCallback function to sets the startup folder location.
There are also some other functions like :
1 - Retrieve the icon belonging to the selected folder.
2 - Shell context menu for a given path.
3 - Retrieve volume serial number
4 - Shell About..
Whenever I need to browse for a folder, I use it!
I don't remember when I did this code. From what I can remember, I took some portion of code here and there and assembled it to make this wrapper. The class is still in constant devellopment and is not really ready for release to public but since I came across this discussion, I thought it would be kind from me to let you have my code. I included a small example that show how it work.
Email me if you want to have it. I can't find the damn button to "Add files" to this post!
Have fun!
Stef
Programming looks like taking drugs...
I think I did an overdose.
|
|
|
|
|
You can get the pidl using SHSimpleIDListFromPath. Then you can assign a callback function to your BROWSEINFO struct to set the initial start path upon initialization of the window.
LPITEMIDLIST getPidl(const CString& str)
{
CStringW wStr(str);
return ::SHSimpleIDListFromPath(wStr);
}
int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
switch(uMsg)
{
case BFFM_INITIALIZED:
return SendMessage(hwnd, BFFM_SETSELECTIONA, false, lpData);
}
return 0;
}
Then, just set the BROWSEINFO struct info as follows and you have the start path of your choosing. You can now navigate anywhere in the directory structure.
void ChooseDirectoryDialog(){
BROWSEINFO bi;
ZeroMemory(&bi, sizeof(bi))
bi.lpszTitle = "Choose a Directory";
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LPARAM)getPidl("C:\MyPath");
LPITEMIDLIST idList = SHBrowseForFolder(&bi);
if (idList != NULL)
{
//get info from struct...
}
return 0;
}
Note: This isn't copied straight from source code so it may not compile right away.
Cheers,
Rich
|
|
|
|
|
MSDN: "To close the COM library gracefully, each successful call to OleInitialize, including those that return S_FALSE, must be balanced by a corresponding call to OleUninitialize."
CoTaskMemFree is the counterpart to SHGetPathFromIDList?
MSDN: CoTaskMemFree. "Frees a block of task memory previously allocated through a call to the CoTaskMemAlloc or CoTaskMemRealloc function."
Anyway, your refactored code confirms that SESE (Single Entry Single Exit) is superior to multiple return statements.
|
|
|
|
|
Roland Pibinger wrote: MSDN: "To close the COM library gracefully, each successful call to OleInitialize, including those that return S_FALSE, must be balanced by a corresponding call to OleUninitialize."
Roland Pibinger wrote: CoTaskMemFree is the counterpart to SHGetPathFromIDList?
Well, here's how it works (so I think)... The ::SHBrowseForFolder will return a pointer to a ITEMIDLIST that's created using CoTaskAlloc under the hood, and the user is responsible for freeing the memory if the returned pointer is not NULL. So in essence, CoTaskMemFree is freeing allocated memory from SHBrowseForFolder, not SHGetPathFromIDList.
~Nitron.
ññòòïðïðB A start
|
|
|
|
|
ok, fixed. Thanks for pointing that out!
~Nitron.
ññòòïðïðB A start
|
|
|
|
|
Nitron wrote: ok, fixed. Thanks for pointing that out!
You'd have thought this morning that a simple article like that would have been pretty smooth, and here you end up with nasty people like Roland and me making you update the article every 1 hour.
Btw, just kidding - this will be useful for a lot of people!
Regards,
Nish
|
|
|
|
|
Nishant Sivakumar wrote: You'd have thought this morning that a simple article like that would have been pretty smooth, and here you end up with nasty people like Roland and me making you update the article every 1 hour.
Well, I must say, after it's all said and done, I think there's a bit of production-quality code here. Thanks for the reviews!
~Nitron.
ññòòïðïðB A start
|
|
|
|
|
Your call to OleUninitialize() without regards to the result of OleInitialize() might be an incorrect interpretation of what MSDN states on the matter. Someone correctly pointed out that S_FALSE is actually a successful result for OleInitialize() and requires a balanced call to OleUninitialize() but the current revision of your code neglects to account for the other potential unsuccessful result codes from OleInitialize() that would not require a balanced call to OleUnitialize(). The SUCCEEDED macro might be in order here.
Contemplate this...
HRESULT hr;
hr=::OleInitialize(NULL);
if (SUCCEEDED(hr)) {
TRACE("Got here 1, %d %d\n",hr,S_OK);
hr=::OleInitialize(NULL);
if (SUCCEEDED(hr)) {
// Not an error
TRACE("Got here 2, %d %d\n",hr,S_FALSE);
// These would be errors
TRACE("%d %d %d %d\n",E_INVALIDARG,E_OUTOFMEMORY,
E_UNEXPECTED,RPC_E_CHANGED_MODE);
if ((SUCCEEDED(E_INVALIDARG)) ||
(SUCCEEDED(E_OUTOFMEMORY)) ||
(SUCCEEDED(E_UNEXPECTED)) ||
(SUCCEEDED(RPC_E_CHANGED_MODE))) {
TRACE("Never get here\n");
}
/*
SUCCEEDED macro still treats S_FALSE as a successful result.
#define SUCCEEDED(Status) ((HRESULT)(Status) >= 0)
*/
::OleUninitialize();
}
::OleUninitialize();
}
|
|
|
|
|
Roland Pibinger wrote: CoTaskMemFree is the counterpart to SHGetPathFromIDList?
MSDN: CoTaskMemFree. "Frees a block of task memory previously allocated through a call to the CoTaskMemAlloc or CoTaskMemRealloc function."
SHBrowseForFolder allocates the memory that has to be deleted using CoTaskMemFree .
Regards,
Nish
|
|
|
|
|
Most of this code was grabbed from CodeProject.com and Microsoft's usenet. It's a hack, complete with a global variable, but it works
#ifndef BIF_NEWDIALOGSTYLE
#define BIF_NEWDIALOGSTYLE 0x0040
#endif
#define BIF_USENEWUI (BIF_NEWDIALOGSTYLE | BIF_EDITBOX)
TCHAR start_folder_global [MAX_PATH];
int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lp, LPARAM pData)
{
TCHAR szDir[MAX_PATH];
switch(uMsg)
{
case BFFM_INITIALIZED:
{
if (start_folder_global ==_T(""))
GetCurrentDirectory(sizeof(szDir)/sizeof(TCHAR),szDir);
#ifdef _UNICODE
else wcscpy (szDir, start_folder_global);
#else
else strcpy (szDir, start_folder_global);
#endif
SendMessage(hwnd,BFFM_SETSELECTION,TRUE,(LPARAM)szDir);
break;
}
case BFFM_SELCHANGED:
{
if (SHGetPathFromIDList((LPITEMIDLIST) lp ,szDir))
SendMessage(hwnd,BFFM_SETSTATUSTEXT,0,(LPARAM)szDir);
break;
}
default:
break;
}
return 0;
}
void my_app::select_a_folder (CString start_path)
{
BROWSEINFO bi;
TCHAR szDir[MAX_PATH];
LPITEMIDLIST pidl;
LPMALLOC pMalloc;
CString tmp_path;
if (SUCCEEDED(SHGetMalloc(&pMalloc)))
{
CString t_str = _T("Select the destination folder...");
#ifdef _UNICODE
wcscpy (start_folder_global, start_path);
wcscpy (szDir, start_path);
#else
strcpy (start_folder_global, start_path);
strcpy (szDir, start_path);
#endif
ZeroMemory(&bi,sizeof(bi));
bi.hwndOwner = NULL;
bi.pszDisplayName = 0;
bi.pidlRoot = 0;
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_USENEWUI;
bi.lpfn = BrowseCallbackProc;
bi.lpszTitle = t_str;
pidl = SHBrowseForFolder(&bi);
if (pidl)
if (SHGetPathFromIDList(pidl,szDir))
// do something with szDir, the selected folder...
}
pMalloc->Free(pidl); pMalloc->Release();
}
/*I'm still wondering if it's possible to put the currently selected pathname into the blank space below the title.*/
-- modified at 16:05 Thursday 16th February, 2006
|
|
|
|
|
Hey Nitron,
I don't see what you are using SHGetMalloc for in your code snippet. You don't seem to be allocating anything there.
And another - SHGetMalloc is now obsolete, instead you need to use CoTaskMemAlloc .
Regards,
Nish
-- modified at 12:54 Thursday 16th February, 2006
|
|
|
|
|
Just read the docs. SHGetMalloc is needed! Ignore my post please.
But since SHGetMalloc is now deprecated, I wonder if there's some way to use CoTaskMemAlloc here.
Regards,
Nish
|
|
|
|
|
Hmmm, actually you have a memory leak in there. You need to do this before the call to Release .
pMalloc->Free(pIDL);
Regards,
Nish
|
|
|
|
|
Perhaps, CoTaskMemFree(pIDL) would be the better way (since SHGetMalloc is obsolete).
Regards,
Nish
|
|
|
|
|
I have nothing to say, but it looked like you were having fun posting so I thought I would join you.;P
You may be right
I may be crazy
But it just may be a lunatic you’re looking for
-- Billy Joel --
Within you lies the power for good - Use it!
|
|
|
|
|
PJ Arends wrote: I have nothing to say, but it looked like you were having fun posting so I thought I would join you.
Reminds me of the old days when we did this just for fun!
Regards,
Nish
|
|
|
|
|
Nishant Sivakumar wrote: Perhaps, CoTaskMemFree(pIDL) would be the better way (since SHGetMalloc is obsolete).
I'll check it out and update in a minute.
~Nitron.
ññòòïðïðB A start
|
|
|
|
|
Good catch! thx! I'll update accordingly...
~Nitron.
ññòòïðïðB A start
|
|
|
|
|
Nishant Sivakumar wrote: since SHGetMalloc is now deprecated
Where did you see that has been depricated? Is that in VS2K5? The MSDN for 2K3 doesn't say anything about it...
~Nitron.
ññòòïðïðB A start
|
|
|
|
|
|
yeah, I just saw that...
~Nitron.
ññòòïðïðB A start
|
|
|
|
|
I didn't need to Alloc, as SHBrowseForFolder does that, however I do need to free the memory, and I changed the code to use CoTaskMemFree . Should be current and undepricated now (As well as free of any memory leaks... )
~Nitron.
ññòòïðïðB A start
|
|
|
|
|