Link to home
Start Free TrialLog in
Avatar of Christopher Schene
Christopher ScheneFlag for United States of America

asked on

CFileDialog problem: can't obtain browser window pointer

It seems that the CFileDialog functionality has changed after .net 2003.

When I invoked the CFileDialog to browse in .net 2003 This line of code (see below) returned a pointer to the file browser window but in .net 2005 and .net 2012 it returns null.

How do I obtain the browser dialog window pointer in VS 2012?

Question:

 CWnd *pp = GetParent();                          


I have a class

class MyFolderBrowser : public CFileDialog

Which is derived from CFileDialog.

I construct it and invoke it from a function as follows:

 MyFolderBrowser browser(m_projectBasePath);
   if (browser.DoModal() == IDOK)
   {
      m_projectBasePath = browser.GetPath();
      OnChangeProjectName();
   }



The DoModal invokes this code:

void MyFolderBrowser::OnFolderChange()
{
    CWnd *pp = GetParent();                          
    VERIFY(pp);
    ASSERT(::IsWindow(pp->m_hWnd));

    ASSERT(pp->GetDlgItem(IDC_MC_DIR) != NULL);



In .net 2003 this call: " CWnd *pp = GetParent(); "

would return the browse dialog window pointer,. but in .net 2005 and later it returns a null.

How doi I get  the pointer to the file dialog window in .net 2012?
Avatar of chaau
chaau
Flag of Australia image

A quick look into dlgfile.cpp (that is located in your Visual Studio folder at c:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\atlmfc\src\mfc\, or c:\Program Files\Microsoft Visual Studio 12.0\VC\atlmfc\src\mfc\) shows that GetParent() is only working for the dialogs that are created without the "Vista" style. You should probably do the same thing: When it is in Vista style mode use IFileDialog interface. Otherwise, use GetParent. Have a look for example at the implementation of the GetFolderPath() method:
CString CFileDialog::GetFolderPath() const
{
   CString strResult;
   if (m_bVistaStyle == TRUE)
   {
      IShellItem *psiResult;
      HRESULT hr = (static_cast<IFileDialog*>(m_pIFileDialog))->GetFolder(&psiResult);
      if (SUCCEEDED(hr))
      {
         LPWSTR wcFolderPath = NULL;
         hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, &wcFolderPath);
         if (SUCCEEDED(hr))
         {
            strResult = wcFolderPath;
            CoTaskMemFree(wcFolderPath);
         }
         psiResult->Release();
      }
   }
   else
   {
      ASSERT(::IsWindow(m_hWnd));
      ASSERT(m_ofn.Flags & OFN_EXPLORER);

      if (GetParent()->SendMessage(CDM_GETFOLDERPATH, (WPARAM)MAX_PATH, (LPARAM)strResult.GetBuffer(MAX_PATH)) < 0)
         strResult.Empty();
      else
         strResult.ReleaseBuffer();
   }
   return strResult;
}

Open in new window

Avatar of Christopher Schene

ASKER

I am still a bit confused. I am actually modifying some code I did not write (the original was in VC 6, C++ MFC).

I am not even sure what m_bVistaStyle is.

Is there someway I can modify the code below to accomplish the same functionality.  I see that the original author was using the window to be able to access a dialog field that has the path information in it.

I tried (1) setting m_bVistaStyle=FLASE and it crashed in some internal dialog code (2) Commenting out all the window references and it crashed in some internal on a window ASSERT check after the  CFileDialog::OnFolderChange();

I am still confused as to how I can fix this:

void MyFolderBrowser::OnFolderChange()
{
    CWnd *pp = GetParent();                           // Parent window = the dialog itself
    VERIFY(pp);
    ASSERT(::IsWindow(pp->m_hWnd));

    ASSERT(pp->GetDlgItem(IDC_MC_DIR) != NULL);
    m_sPath = GetFolderPath();
    int len = m_sPath.GetLength();
    if (len > 0 && m_sPath[len-1] != '\\')
    {
        m_sPath += "\\";
        ++len;
    }
    pp->GetDlgItem(IDC_MC_DIR)->SetWindowText(m_sPath);
    m_oEdit.SetSel(len, len);

    CFileDialog::OnFolderChange();

    m_oEdit.SetFocus();
}
Can you also show your OnInitDialog? I do not understand how you are able to create a IDC_MC_DIR if you can't access the parent.
Strange: there is no OnInitDialog in the class: perhaps there is an internal OnInitDialog method as part of the CFileDialog class?



There is the following method:

// only called back if OFN_EXPLORER is set
      virtual void OnInitDone();

What he is doing seems like overkill as the code below will grab the directory just fine.


CString m_DataDir;
CFolderPickerDialog dirDlg(CString("Please Choose a folder"));
 if (dirDlg.DoModal() == IDOK ) {
       m_DataDir = dirDlg.GetFolderPath();
}
Please explain to me a bit more about IFileDialog.

Isn't that used with the Automation Template Library and/or COM?
IFileDialog is COM/ATL interface for the new-looking file Open/Save dialog that you see in the latest versions of Windows (starting from Vista) CFileDialog magically wraps both (classic and new style). Just examine the source code (dlgfile.cpp) and you will see it yourself. BTW, how is IDC_MC_DIR defined in your application?
"how is IDC_MC_DIR defined in your application? "

That is an edit entry field---which is part of the dialog.
I can't see defined anywhere in the MFC library. Looks like it is defined locally somewhere in your code
Ok, I have decided that probably using CFolderPickerDialog is a better approach (do you agree?).

But I have a problem with it: If I specify a valid starting  path in the constructor as follows

CFolderPickerDialog dirDlg(CString("C:\\temp"));

Then dirDlg.GetFolderPath(); always returns the path I set in the constructor, but I I don't specify a valid path

CFolderPickerDialog dirDlg(CString("Please Choose a folder"));

Then dirDlg.GetFolderPath(); will return the path I chose in the dialog.

How do I both specify a starting path AND obtain the chosen path from dirDlg.GetFolderPath()?

CString m_DataDir;
CFolderPickerDialog dirDlg(CString("Please Choose a folder"));
  if (dirDlg.DoModal() == IDOK ) {
        m_DataDir = dirDlg.GetFolderPath();
 }
According to MSDN you can only use GetFolderPath under these conditions:
You can call this method only while the dialog box is being displayed. After the dialog box has been closed, this function will no longer work, and the method will fail with an assertion.
Use GetPathName. This will return the whole path of the selected file.
CString m_DataDir;
CFolderPickerDialog dirDlg(CString("c:\\temp"));
  if (dirDlg.DoModal() == IDOK ) {
        m_DataDir = dirDlg.GetPathName();
 } 

Open in new window

BTW, there is a better alternative. You can use CShellManager::BrowseForFolder, like this:
{
CString m_DataDir;
CString initialPath(_T("C:\\temp"));
if(theApp.GetShellManager ()->BrowseForFolder (m_DataDir, 
			this, initialPath, _T("Please Choose a folder:")))
	{
		// do something. m_DataDir will already contain the selected path
	}
 } 

Open in new window

This might be a really dumb question, but "theApp" is undefined.

What shall I set that to?
Use AfxGetApp(), like this:
{
CString m_DataDir;
CString initialPath(_T("C:\\temp"));
if(AfxGetApp()->GetShellManager ()->BrowseForFolder (m_DataDir, 
			this, initialPath, _T("Please Choose a folder:")))
	{
		// do something. m_DataDir will already contain the selected path
	}
 } 

Open in new window

I found

CWinApp* AFXAPI AfxGetApp();   in  afxwin.h

but the compiler says:

error C2039: 'GetShellManager' : is not a member of 'CWinApp'
In this case then, please try this function. It does the same thing as Shellmanager's BrowseForFolder:
CString ChooseAFolder(HWND hwnd, const CString &strIniDir, const CString &strDisplayName)
{
  CoInitialize(NULL);
  CString strRet;
  LPITEMIDLIST pidlSelected = NULL;

  BROWSEINFO bi = {0};
  bi.hwndOwner = hwnd;
  bi.pidlRoot = NULL;
  bi.pszDisplayName = strRet.GetBuffer(MAX_PATH);
  bi.lpszTitle = strDisplayName;
  bi.ulFlags = 0;
  bi.lpfn = NULL;
  bi.lParam = 0;

  pidlSelected = SHBrowseForFolder(&bi);
  strRet.ReleaseBuffer();

  if(pidlSelected)
  {
    SHGetPathFromIDList(pidlSelected, strRet.GetBuffer(2*MAX_PATH));
    strRet.ReleaseBuffer();
  }
  else
  {
    strRet.ReleaseBuffer();
    strRet.Empty();
  }

  CoUninitialize();
    return strRet;
}

Open in new window

Usage:
{
CString m_DataDir;
CString initialPath(_T("C:\\temp"));
m_DataDir = ChooseAFolder(GetSafeHwnd(), initailPath, CString( _T("Please Choose a folder:")));
if(!m_DataDir.IsEmpty())
	{
		// do something. m_DataDir will already contain the selected path
	}
 } 

Open in new window

This works expect for one thing:

const CString &strIniDir: the initial directory is not used as far as I can tell
I think I need to find a way to set this variable:

 bi.pidlRoot

To the path I wish to search in but there is no conversion that I can figure ouy
ASKER CERTIFIED SOLUTION
Avatar of chaau
chaau
Flag of Australia 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
We are getting closer. Note the two pictures: (1) The old dialog and (2) the new dialog.

the old dialog is the version compiled with .net 2003 (prior to the MFC breaking change) and the new one is the version compiled under .net 2012 (after the breaking change).

How do I get the new one to have the same appearance as the old? Is there some option I can set?
Screenshot-2014-06-19-21.41.59-n.png
Screenshot-2014-06-19-21.41.27-o.png
Hi,

This seems to work OK----except I think I need a few more options. So far I have tried in the constructor

m_bi.ulFlags = dwFlags | BIF_STATUSTEXT | BIF_EDITBOX | BIF_BROWSEINCLUDEFILES | BIF_USENEWUI;

If you know what those options are I would appreciate if you would show me. Otherwise, I'll eventually figure it out on my own,
All these options are described in the MSDN article for BROWSEINFO. I think, your choice of options is quite good, as it includes all the features on can expect from a good folder selection dialog
I've requested that this question be closed as follows:

Accepted answer: 0 points for cschene's comment #a40146410

for the following reason:

It took multiple try's but it is working now.
Sorry-----I made a mistake: I meant I accept the experts answer. Please re-open.