The curious behavior of CFileDialog::GetFolderPath function

 

In most of the implementations to get the folder path of the selected file from a file dialog, people tend implement their own implementation to find the last “\” character and trim the string. CFileDialog::GetFolderPath in fact it exposes the very same functionality but it shows different behavior across platform and MFC libraries.

The typical usage of a file dialog would be as follows

CFileDialog f (true);
if( IDOK == f.DoModal())
{
   CString str = f.GetFolderPath();
}

If you compile this code with Visual Studio 2008 and above the code will succeeds and give you the folder of the filename selected by the user. But if you compile the code with MFC version 8.0 (VS 2005) or the prior version the code you will eventually you will reach in exception.
On stepping in to the code, you can see the following code with the MFC 8.0 implementation.

CString strResult;

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;

The internal implementation uses the typical window messaging system. The return of the message points to the string buffer which will be copied to the CString object and will be returned.
Though GetFolderPath can’t be used in a typical scenario, it can be used when you’ve your own implementation of the CFileDialog. You can use them in the internal callbacks and own implementations of override-able function as long as the window is being displayed.
E.g. see an implementation in a class derived from CFileDialog. Here the window is active. The call CFileDialog::OnOK will destroy the window object in the end.

void MyFileDialog::OnOK()
{
	CString str = GetFolderPath();

	// Do something like some validation
	MessageBox( str );
	CFileDialog::OnOK();
}

Changes in Windows Vista

In Windows Vista and above there are there several new shell interfaces introduced with extended functionality. The new changes in the file dialog and the Windows shell are incorporated in the new IFileDialog implementation. These interfaces are nothing but lightweight COM object and not part of MFC library. To provide the backward compatibility while exploiting the new features in the platform, Microsoft has changed the MFC implementation of CFileDialog with the help of using IFileDialog interface.
The constructor of the new CFileDialog now takes a new (default) parameter in the end called bVistaStyle. Inside the constructor of CFileDialog, the m_bVistaStyle parameter is set with this input parameter and the whole implementation of CFileDialog is implemented with condition checks. If it’s Vista Style File dialog, IFileDialog interface will be used throughout the life of the object.

See the description from MSDN

Both the appearance and the functionality of the CFileDialog with Windows Vista differ from the earlier versions of Windows. The default CFileDialog automatically uses the new Windows Vista style without code changes if a program is compiled and run under Windows Vista. Use the bVistaStyle parameter in the constructor to manually override this automatic update.

Obviously the old Open and Save file dialog API based implementations are remained intact and wrapped around with m_bVistaStyle condition check. You can find the implementation in the dlgfile.cpp (located underVC\atlmfc\src\mfc ).

Visual Studio 2008 (MFC 9.0) library has incorporated the above mentioned changes and behave properly.

For instance you can clearly see the code for GetFolderPath with the latest version of MFC library here. You can clearly see it uses m_pIFileDialog member of the class being used to get the folder path. The previous implementation is wrapped in the else case.

CString CFileDialog::GetFolderPath() const
{
	CString strResult;
	if (m_bVistaStyle == TRUE)
	{
		IShellItem *psiResult;
		HRESULT hr = (static_cast(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;
}

Conclusion

You can make use of this call if you’re using Visual Studio 2008 (MFC 9.0) and above, you’re passing bVistaStyle as true in the constructor and the platform is Windows Vista and above. Also If it’s not required to provide backward compatibility, you can make use of IFileDialog interface than using CFileDialog class.

Links

CFileDialog::GetFolderPath
IFileDialog interface

 

Visual Studio Command Aliases and Find Window

 

Command Window in Visual Studio is a boon to the power users. With Visual Studio, you can also use Find Window to get the power of command window.

for e.g. if you want to open a file when you’re lost with directory structures and sea of source files, you can go ahead to the find window and use

> open <filename.ext> which will even give you auto complete for the file names with close match.

There are lot of other handy commands as well. Go ahead and check it. It will work with both command and Find with with > operator. This will work with almost all new versions of Visual Studio (tested in VS 2005,2008 and 2010)
Visual Studio Command Aliases
 

How to capture the Debug Trace from Win32 Services?

 

This is a simply entry level tip but extremely helpful for the beginners.

Most of the people are familiar withthe famous Debug Trace tool Debug View which is one of the best and popular tools from Sysinternals (Part of Microsoft Now)

Download and launch DebugView with administrator privilege (choose Run as Administrator )

Enable Capture->Capture Global Win32 from the menu bar.

 

 

 

 

 

 

 

You can alternatively do this from elevated command prompt or explorer using WinDbg.exe /g

 

 

Proudly powered by WordPress
Theme: Esquire by Matthew Buchanan.