-
January 22nd, 2013, 06:46 PM
#1
How to set a starting root directory for this code that selects a folder name?
The following code is useful to allow a user to select a particular folder in the disk directory.
Code:
void OpenFolder()
{
LPMALLOC pMalloc; //,pMalloc2;
CString strDirectory;
BROWSEINFO bi;
// Gets the Shell's default allocator
wchar_t pszBuffer[MAX_PATH];
LPITEMIDLIST pidl;
// Get help on BROWSEINFO struct - it's got all the bit settings.
bi.hwndOwner = ::GetDesktopWindow();
bi.pidlRoot = NULL;
bi.pszDisplayName = pszBuffer;
bi.lpszTitle = _T("Select A Directory");
bi.ulFlags = BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS | BIF_NEWDIA********;
bi.lpfn = NULL;
bi.lParam = 0;
if (::SHGetMalloc(&pMalloc) == NOERROR)
{
if ((pidl = ::SHBrowseForFolder(&bi)) != NULL)
{
if (::SHGetPathFromIDList(pidl, pszBuffer))
{
strDirectory = pszBuffer;
}
pMalloc->Free(pidl);
}
pMalloc->Release();
}
TRACE0("strDirectory =: ");
OutputDebugString(strDirectory); TRACE0("\n");
}
However, if one uses this code over and over, one quickly becomes tired of threading one's way all the way from root directory C:\. There is undoubtedly some way to designate a starting directory. The most obvious candidate to set such a target is the:
Code:
bi.pidlRoot = NULL;
But attempting to set bi.pidlRoot to a CString (directory address) errors because 'no suiitable conversion from CString to LPCITEMIDLIST exists'
How can I set the code to open at a designated address in the directory tree?
mpliam
-
January 22nd, 2013, 07:41 PM
#2
Re: How to set a starting root directory for this code that selects a folder name?
Right now you have:
Change it to something like:
Code:
bi.lpfn = BrowseCallbackProc;
And add the non-member callback
Code:
INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg,LPARAM lp, LPARAM pData)
{
TCHAR szDir[MAX_PATH];
switch(uMsg)
{
case BFFM_INITIALIZED:
_tcscpy_s(szDir,MAX_PATH,_T("C:\\cygwin"));
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)szDir);
break;
}
return 0;
}
-
January 23rd, 2013, 12:47 AM
#3
Re: How to set a starting root directory for this code that selects a folder name?
When the user selects a folder, store it in the registry and then use it the next time the user opens the browse for folder dialog. That's the behavior I expect anyway.
-
January 23rd, 2013, 12:58 AM
#4
Re: How to set a starting root directory for this code that selects a folder name?
Not knowing exactly how to do this, I tried as I believe you suggested:
Code:
bi.pidlRoot = BrowseCallbackProc(NULL, 0, 0, 0);
which was met rather ungraciously with :
value of type "INT" cannot be assigned to type LPCITEMIDLIST
There must be some easier way.
mpliam
-
January 23rd, 2013, 01:00 AM
#5
Re: How to set a starting root directory for this code that selects a folder name?
Exactly Arjay, but how to set the code in question to start in a more convenient (say the last saved root directory - save it where you will). ?
mpliam
-
January 23rd, 2013, 04:11 AM
#6
Re: How to set a starting root directory for this code that selects a folder name?
Originally Posted by Mike Pliam
... The most obvious candidate to set such a target is the:
Code:
bi.pidlRoot = NULL;
But attempting to set bi.pidlRoot to a CString (directory address) errors because 'no suiitable conversion from CString to LPCITEMIDLIST exists'
How can I set the code to open at a designated address in the directory tree?
Originally Posted by Mike Pliam
Not knowing exactly how to do this, I tried as I believe you suggested:
Code:
bi.pidlRoot = BrowseCallbackProc(NULL, 0, 0, 0);
which was met rather ungraciously with :
There must be some easier way.
Mike, as Philip Nicoletti already mentioned, you should pass the string with this starting folder as LPARAM. So do:
Code:
bi.pidlRoot = NULL;
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LPARAM)_T("d:\\My Initial Folder");
Victor Nijegorodov
-
January 23rd, 2013, 09:11 PM
#7
Re: How to set a starting root directory for this code that selects a folder name?
OK. What I've done is to save the last accessed m_csMyLastDir as a CString member variable. Then try:
Code:
bi.pidlRoot = NULL;
bi.lpfn = BrowseCallbackProc;
//bi.lParam = (LPARAM)_T("d:\\My Initial Folder");
bi.lParam = (LPARAM)(m_csMyLastDir.GetBuffer(0)); m_csMyLastDir.ReleaseBuffer();
And here's my BFF:
Code:
// BFFCALLBACK function pointer (Windows)
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb762598(v=vs.85).aspx
// Specifies an application-defined callback function used to send messages to, and
// process messages from, a Browse dialog box displayed in response to a call to
/// SHBrowseForFolder.
INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
{
TCHAR szDir[MAX_PATH];
switch(uMsg)
{
case BFFM_INITIALIZED:
//_tcscpy_s(szDir,MAX_PATH,_T("C:\\"));
//_tcscpy_s(szDir, MAX_PATH, pData);
_tcscpy_s(szDir, MAX_PATH, (LPARAM)pData);
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)szDir);
break;
}
return 0;
}// BrowseCallbackProc(HWND hwnd, UINT uMsg,LPARAM lp, LPARAM pData)
But notice there are 2 LPARAMS in this CALLBACK. I have not figured out how either one can be used.
Please help. Thanks.
mpliam
-
January 23rd, 2013, 10:26 PM
#8
Re: How to set a starting root directory for this code that selects a folder name?
1) Does that compile ? The third parameter to _tcscpy is not an LPARAM. I should probably be:
Code:
_tcscpy_s(szDir,MAX_PATH,reinterpret_cast<LPCTSTR>(pData));
2) I don't think you need to use GetBuffer/ReleaseBuffer
Code:
bi.lParam = (LPARAM)(LPCTSTR)m_csMyLastDir;
-
January 24th, 2013, 09:07 AM
#9
Re: How to set a starting root directory for this code that selects a folder name?
Originally Posted by Mike Pliam
The most obvious candidate to set such a target is the:
Code:
bi.pidlRoot = NULL;
Mike, common sense misguides too often. From MSDN:
pidlRoot
Type: PCIDLIST_ABSOLUTE
A PIDL that specifies the location of the root folder from which to start browsing. Only the specified folder and its subfolders in the namespace hierarchy appear in the dialog box. This member can be NULL; in that case, the namespace root (the Desktop folder) is used.
Setting pidlRoot to some particular IDList has a very special meaning: user will be able to select a path under or equal to the root, and won't be able to go above. Say you select "C:\My data\options" as a root. Then you never be able to select "C:\My data", "C:\" or any other drive. Evidently this is not just a "starting folder" that you want.
But attempting to set bi.pidlRoot to a CString (directory address) errors because 'no suiitable conversion from CString to LPCITEMIDLIST exists'
How can I set the code to open at a designated address in the directory tree?
Seems you never heard about Shell Namespace. Firstly introduced in Windows NT3.51, AFAIR. It's about 1995.
Last edited by Igor Vartanov; January 24th, 2013 at 09:12 AM.
Best regards,
Igor
-
January 24th, 2013, 04:07 PM
#10
Re: How to set a starting root directory for this code that selects a folder name?
Thanks Phil. That works nicely - it also allows the user to navigate in both directions from the newly designated root, up and down the tree. I would never have figured that out myself.
This works (but I always wonder about using *.ReleaseBuffer() as some claim is necessary)
Code:
bi.pidlRoot = NULL;
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LPARAM)(m_csLastEncryptDir.GetBuffer(0)); // this works !!
//bi.lParam = (LPARAM)(LPCTSTR)m_csMyLastDir; // this won't compile
and this works!!
Code:
INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg,LPARAM lp, LPARAM pData)
{
TCHAR szDir[MAX_PATH];
switch(uMsg)
{
case BFFM_INITIALIZED:
_tcscpy_s(szDir,MAX_PATH,reinterpret_cast<LPCTSTR>(pData));
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)szDir);
break;
}
return 0;
}
Last edited by Mike Pliam; January 24th, 2013 at 04:13 PM.
mpliam
-
January 24th, 2013, 05:28 PM
#11
Re: How to set a starting root directory for this code that selects a folder name?
Why wouldn't you want to call ReleaseBuffer?
-
January 24th, 2013, 05:33 PM
#12
Re: How to set a starting root directory for this code that selects a folder name?
Arjay, I know we've discussed this in the past. I have been under the impression that it was good practice to call ReleaseBuffer anytime one uses GetBuffer(0) on a given CString, in order to 'free' the buffer (whatever that implies). I almost always do that, even though it makes the code less 'pretty'. Then one of the gurus, I don't recall which one of you, said something to the effect 'No well-designed function would require such a release', implying either CString is well-designed and consequently should not need such followup, or that CString is poorly designed and does. What the consensus on this?
mpliam
-
January 24th, 2013, 06:19 PM
#13
Re: How to set a starting root directory for this code that selects a folder name?
Originally Posted by Mike Pliam
'No well-designed function would require such a release', implying either CString is well-designed and consequently should not need such followup, or that CString is poorly designed and does. What the consensus on this?
See this MFC/ATL string adapter class:
http://msdn.microsoft.com/en-us/library/08thta63.aspx
This will call ReleaseBuffer() on destruction of whatever MFC/ATL string type that it is constructed with. So if you forget to call ReleaseBuffer(), this class will do it for you.
As a matter fof fact, it is a good idea in general to create or use classes that will "release" or "close" on destruction (RAII). This way, you're not writing code where at every single return point, catch handler, etc., you're scrambling doing this work yourself.
Regards,
Paul McKenzie
Last edited by Paul McKenzie; January 24th, 2013 at 06:24 PM.
-
January 24th, 2013, 06:23 PM
#14
Re: How to set a starting root directory for this code that selects a folder name?
Paul,
This is possible because the wrapper object goes out of scope naturally in the case of an exception or multiple exiting code paths; causing its destructor to free the string resource.
But what if one uses the same string source repeatedly in the same function (scope) ? I suspect it's safer to use ReleaseBuffer in each instance of GetBuffer(0) and let M$ worry about implementing the details of cleanup.
mpliam
-
January 24th, 2013, 06:28 PM
#15
Re: How to set a starting root directory for this code that selects a folder name?
If you forget to call ReleaseBuffer when the function exits, what happens? Does the constructor for CString call ReleaseBuffer()? If it doesn't then I don't know what happens.
Look at it this way, you want a safe way to make sure you aren't leaking memory or resources. The sure-fire way to make sure of this is to use a class that guarantees that on destruction, the proper cleanup occurs.
If an exception is thrown, how is your "ReleaseBuffer" everywhere approach going to work, unless you remember to write a try/catch handler and call it there also? See how unwieldly it gets -- that's why the adapter class was created, so as to make it safer if you do plan to use GetBuffer() and ReleaseBuffer() continuously in your code.
Regards,
Paul McKenzie
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|