Link to home
Start Free TrialLog in
Avatar of emitchell
emitchell

asked on

How to find default directory from a CFileDialog

I am trying to extract the initial directory so that the user can start from the folder that they chose last time. I use a CFileDialog to open the file and in Win98 it typically starts in "My Documents". If the user navigates somewhere else, then this folder is remembered for the duration of the session. I want to remember this so that I can set it at the start of the next session. I thought that in the WM_CLOSE of the CMainFrame class I could put the following:

    CFileDialog fd(TRUE);

    CString sFinalDir = fd.m_ofn.lpstrInitialDir;
    theApp.WriteProfileString(SETTINGS_KEY,               INITIALDIR_KEY, sFinalDir);

I could then do the following in the InitInstance of my app:

   // find initial directory, if any
    m_sInitialDir = GetProfileString(SETTINGS_KEY, INITIALDIR_KEY);

and stuff this into the m_ofn struct so that subsequent File Opens would start at the previous folder.

Unfortunately the m_ofn contains a null for the lpstrInitialDir when I do the WM_CLOSE. How can I get at the internal folder that CFileDialog is holding so that I can start there next time. Once I have this how can I ensure that this folder is the place to start next time. I was going to create a CFileDialog and then set the m_ofn.lpstrInitialDir member to the last directory that I can recover from the registry.

Avatar of cypherljk
cypherljk

Howdy,
  Try saving the initial directory immediately after the user closes the dialog box.  When the OK button is pressed, it seem like your waiting to long to get the data and it is no longer valid.

My 2 cents...
Avatar of DanRollins
Here is the basic technique:

When your program starts, use a command like:

  m_sOpenFileDir= theApp.GetProfileString("Prefs", "OpenFileDir", "c:\\My Documents");

Now, at some point, you will bring up the CFileDialog, as follows:

  CFileDialog dlg( TRUE );
  dlg.m_ofn.lpstrInitialDir=(LPCSTR) m_sOpenFileDir;
  int nRet= dlg.DoModal();
  if (nRet== IDOK) {
    // learn the dir by getting the first part of the filepath...
    m_sOpenFileDir= dlg.GetPathName();
    m_sOpenFileDir= m_sOpenFileDir.Left( dlg.m_ofn.nFileOffset); // leave only the d:\dir\dir

    // save that dir for next time...
    theApp.WriteProfileString("Prefs", "OpenFileDir", m_sOpenFileDir );

    // now take the desired action (read the file, etc...)
  }  
 
The result is:
* The very first time your program runs, m_sOpenFileDir will get set to C:\My Documents (a standard default).

* Whenever the user OKs the OpenFile dlg, you will assume that that is his preferred directory, so you save it to the registry.

* Thereafter, each time you use the Open File Dialog, it will remember the last place the user selected .. within the current run AND in future runs.

-- Dan
Avatar of emitchell

ASKER

I had originally thought of saving the directory that the user has chosen but that would require me to put the above code in all the places that the CFileDialog is invoked. Presumably, inside the CFileDialog there is knowledge of what the last folder was since if FileOPen is used twice in a row, the second CFileDialog starts off in the folder where the first one finished.

Isn't there a way to extract from the CFileDialog where it would first open if it were invoked. In this case I just have get hold of this in the WM_CLOSE of the CMainFrame and save it once.
Actually, it is a bit more complicated.  See the info on lpszInitialDir at this link (or in the MDSN documentation on the OPENFILENAME structure):

http://msdn.microsoft.com/library/en-us/winui/hh/winui/commdlg3_1hma.asp

It seems that the system keeps a most-recently-used directory for each program -- probably in the registry somewhere. That will take effect in certain cases where you dont specify the initial dir and don't provide a filename.

>>Isn't there a way to extract from the CFileDialog where it would first open..

Nothing is documented, and simply instatiating a CFileDialog will certainly fail.  You would need to call DoModal(), which defeats the purpose.

>>that would require me to put the above code in all the places that the CFileDialog is invoked.

Most programs use this in only one or two places.  How hard is that?  

However, you can try this:  Never pass in a filename and leave lpszInitialDir pointing to "\0" -- that way the system will handle things in the default manor which you prefer.

-- Dan
See following link for example project:

http://codeguru.earthweb.com/files/getFolder.html
I should have explained what I was trying do better. I want to save the folder from which the user does a File/Open or File/Save As... When the user starts up next time they should be looking in the last folder, wherever it happens to be. In general our users will only use one folder! I would think that this would be a fairly standard thing to do. Our users don't want to always start in "My Documents" since that is where the unsophisticated user will always store stuff. This causes untold clutter.

When I look closer, it is the DocManager that executes OnFileOpen(...) which then passes an empty string on to DocManager::DoPromptFileName(...). If I can replace this DocManager::OnFileOpen() I could call the DoPromptFileName(...) with the last folder opened for the file name. Unfortunately, I can't get at the CFileDialog inside the DoPromptFileName to do my own saving of the file. Nor do I think that I can override any of DocManager's functions.

From Dan's comments, I think that I have to give up on getting at the last folder that is internal to the Windows function ::GetOpenFileName(&m_ofn). As he says, this is probably tucked away in some undocumented place.

One plan up for discussion would be to override the CWinApp::OnFileOpen() so that I can suggest a folder name. This is just a one liner:

void CWinApp::OnFileOpen()
{
     ASSERT(m_pDocManager != NULL);
     m_pDocManager->OnFileOpen();
}
Place within my override the following (comes from CDocManager::OnFileOpen():

    // prompt the user (with all document templates)
    CString newName;
    if (!DoPromptFileName(newName, AFX_IDS_OPENFILE,
        OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, TRUE, NULL))
        return; // open cancelled

    AfxGetApp()->OpenDocumentFile(newName);
    // if returns NULL, the user has already been alerted

and replace the CString newName; with the getting of the first file from the MRU list as in:

CSring newName = GetProfileString(_T("Recent File List"),
    _T("File1"));

Does anyone have a better idea? Any time there is a File/Open or a File/Save the file is placed on the MRU list so my picking up this file would get me to the right place if everything works as expected.
ASKER CERTIFIED SOLUTION
Avatar of DanRollins
DanRollins
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
mitch

ur question seems similar lie the action we used to perform in winzip which stores the last extracted output folder..

y not create a key in the registry..the key  will hold the latest folder where a open or save information is performed..create a key in win registry...write into it the folder path..and b4 the user performs an open operation..or when the user clicks open icon or whatsoever read from the key and then let the folder where the operation ws perfomed be displayed
Dan's suggestion worked like a charm. I was trying to find a virtual function that I could add to when all I needed to do was to handle ID_FILE_OPEN in my main app. For the record I did the following. First pick up File1 from the MRU list in the InitInstance and save it into a member string variable:

    // find initial directory, if any and put it into
    // the string held within this class. it will only
    // be used once for the first open and then will
    // be erased
    m_sInitialDir = GetProfileString(RECENTFILELIST_KEY
     , _T("File1"));

Then handle the ID_FILE_OPEN with the following function:

void
CRaceApp::OnFileOpen()
{
    // use the initial dir the first time we prompt for a file
    // name. after a successful open, the file name will be
    // changed to be the empty string and the default dir
    // will be supplied by the CFileDialog internal memory
    if(!DoPromptFileName(m_sInitialDir, AFX_IDS_OPENFILE,
            OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, TRUE, NULL)) {
        // open cancelled
        return;
    }
    AfxGetApp()->OpenDocumentFile(m_sInitialDir);
    // if returns NULL, the user has already been alerted

    // now erase the initial dir since we only want to use it once
    m_sInitialDir.Empty();
}

The user must open something so we start off in the folder that corresponds to File1 of the MRU. After that, Windows keeps track of which folders we have navigated to so we can use an empty string within the same session.

Thanks Dan. You solved the problem. It was harder than I had originally thought!

Ed
Thanks Dan. You solved the problem. It was harder than I had originally thought!

Ed