CFileDialog with realtive path - c++

I currently maintenance an old MFC application and have problems with opening file dialogs. The Program has multiple different parts were the user select files for loading, eg sound, video and other program specific formats.
Opening a dialog should always open in a "specific" folder, depending on the file ending. Giving an directory path that contains "..\" will accept and the Dialog opens with the "last selected file".
CString fileDirectory = myHelper.getPath();
// fileDirectory is now "C:\coding\svn\source\MyProgram\..\..\bin\..\data\..\Audio\"
CFileDialog FileDialog(true, _T("MP3;WAV"), _T(fileDirectory), OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, _T("All music files (*.WAV;*.MP3)));
if (FileDialog.DoModal() == IDOK)
{ ... }
I use different CDialog classes (about 15, eg for editing audiofiles, for videofiles) and they all have similar code for opening dialogs like above.
How can i support the relative paths for the CFileDialog?

The CFileDialog supports setting up initial/default folder. Here is the code snippet that demonstrates how to use it:
const TCHAR szFilter[] = _T("Parameter Files (*.npf)|*.npf|All Files (*.*)|*.*||");
CFileDialog dlg(TRUE, _T("npf"), NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, szFilter, this);
CString sParametersDir(CUtility::GetParametersDir());
dlg.m_ofn.lpstrInitialDir = sParametersDir.GetBuffer(_MAX_PATH);
if(dlg.DoModal() == IDOK)
{
m_ParametersFileEdit.SetWindowText(dlg.GetPathName());
}
sParametersDir.ReleaseBuffer();
Also regarding your code. There is no need to use _T() macro for CString objects. The CString class does support UNICODE automatically. The _T() macro should only be used for string literals.
You can use CPath class to normalize file path.
CPath path(sPath);
path.AddBackslash();
path.Append(_T("Config"));
path.Canonicalize();

Related

Show Files in CFolderPickerDialog C++

When utilizing CFolderPickerDialog in a MFC application, is there any way I can view the files as well. I understand that there are way cool options to select a folder, but I need to make sure the files I want are in that directory as well. Any help would be greatly appreciated.
The CFolderPickerdialog not allowing users to see the files within the dialog box came as a WYSIWYG moment. SO your option then becomes to utilize the CFileDialog and then trim the path to Folderpath as described in the code below.
CFileDialog dlg (OFNHIDERADONLY | OFN_OVERWRITEPROMPT | BIF_BROWSEINCLUDEFILES, NULL);
...
CString path = dlg.GetPathName();
CString Folderpath = path.Left(path.ReverseFind('\\'));
if (Folderpath.Right(1) != "\\")
Folderpath += "\\";
Therefore, your code may look like C:\Users\foldername\file.txt
but will now look like this C:\Users\foldername\

Issues with CFileDialog for multiple file selection

I'm using the following code to retrieve multiple file selection via UI:
CFileDialog fd(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER,
NULL, hParentWnd ? CWnd::FromHandle(hParentWnd) : NULL);
fd.m_pOFN->Flags |= OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_NODEREFERENCELINKS;
int nLnBuff = 32767;
TCHAR* pBuffFileSelect = new TCHAR[nLnBuff];
memset(pBuffFileSelect, 0, nLnBuff * sizeof(TCHAR));
fd.m_ofn.lpstrFile = pBuffFileSelect;
fd.m_ofn.nMaxFile = nLnBuff;
if(fd.DoModal() == IDOK)
{
POSITION fileNamesPosition = fd.GetStartPosition();
while(fileNamesPosition != NULL)
{
CString strSelPath = fd.GetNextPathName(fileNamesPosition);
TRACE("path: %s\n", CStringA(strSelPath));
}
}
delete[] pBuffFileSelect;
So when I try it on my PC, I run the method above and when the "Open File" dialog open up, just as a test, I navigated to my desktop and selected all files using Ctrl+A shortcut and then clicked Open. As a result I started getting the following paths:
The first path is a link, which is correct (it exists on my Public desktop):
"C:\Users\Public\Desktop\avp.lnk"
But then the second path is wrong. It gives me:
"C:\Users\Public\Desktop\1.txt"
when it's supposed to be (for the desktop that I picked):
"C:\Users\UserName\Desktop\1.txt"
and then every consecutive path has "Public" instead of "UserName".
I should point out that I have several user accounts set up on this PC and the one that I'm testing this method from is a Standard user account. The app that I'm running this method from is not running elevated (or with regular user privileges) so it should not have access to other user accounts anyway.
So what am I doing wrong here?
Checked the sources, and GetOpenFileName assumes that all the items are in fact in the same file path. This isn't true for the Desktop (there are items in different paths merged into one shell view), and so you'll see the bad behavior.
The solution is to use the Common Item dialogs, which use the shell namespace rather than file system paths. All the desktop items are in a common shell path, and then you can use IShellItem::GetDisplayName to convert to a file system path.
Unfortunately, MFC doesn't have a wrapper for the common item dialog, so you'll have to manage that yourself.

Overwrite OnFileOpen and OnFileSave function - registry is not updated?

I have a little problem that I do not know how to solved:
I am writing a program that involves data serialization. I decided to override the OnFileOpen and OnFileSave function because I wanted to modify the File Dialog Box when open/save the file.
My problem is: after I load the file, the program title still remains 'Untitled' and the 'Recent File' list under the 'File' menu bar is not updated either.
I suppose I need to add some code to update the registry? But I struggle to find the solution of it...
Thank you all for the helps! Highly appreciated!
Here are my OnFileOpen and OnFileSave code:
void CXXXDoc::OnFileOpen()
{
// TODO: Add your command handler code here
TCHAR szFilters[]= _T("XXX Type Files (*.xxx)|*.xxx|All Files (*.*)|*.*||");
CFileDialog fileDlg(TRUE, _T("xxx"), _T("*.xxx"), OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, szFilters);
if(fileDlg.DoModal() == IDOK)
{
CFile oldFile;
ASSERT (oldFile != NULL);
oldFile.Open(fileDlg.GetPathName(), CFile::modeRead | CFile::shareExclusive);
FilePathName = fileDlg.GetPathName();
SetTitle(FilePathName);
CArchive loadArchive(&oldFile, CArchive::load | CArchive::bNoFlushOnDelete); // Create the archive to load data, the archive must be closed manually after the loading process
Serialize(loadArchive);
loadArchive.Close();
oldFile.Close();
UpdateAllViews(0);
}
}
void CXXXDoc::OnFileSave()
{
CFile newfile;
ASSERT (newfile != NULL);
newfile.Open(FilePathName, CFile::modeWrite);
CArchive saveArchive(&newfile, CArchive::store); // Create the archive to save data,
Serialize(saveArchive);
saveArchive.Close();
newfile.Close();
}
To keep the most recently used (MRU) list updated, you need to call CWinApp::AddToRecentFileList (MSDN here). So, for your OnFileOpen, you might do this:
AfxGetApp()->AddToRecentFileList(FilePathName);
As the remarks in MSDN say:
You should call the LoadStdProfileSettings member function to load the
current MRU file list before you use this member function.
The framework calls this member function when it opens a file or
executes the Save As command to save a file with a new name.

Windows LoadMenu error: "The specified resource name cannot be found in the image file."

I'm writing a program that, among other things, needs to display a context menu on right-click. I'm trapping WM_NOTIFY, the identifier of the control being clicked on, and NM_RCLICK. That all works great.
The problem comes in when I'm processing that right-click:
case NM_RCLICK:
{
HMENU Popup = LoadMenu(0, MAKEINTRESOURCE(IDR_NED_MENU));
if ( !Popup ) {
DWORD err = GetLastError();
char* buf;
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER, 0, err, 0, buf, 1<<19, 0);
_ERROR("LoadMenu(0, MAKEINTRESOURCE(IDR_NED_MENU)); Error '%s' thrown; no menu loaded.", buf);
delete [] buf;
}
Popup = GetSubMenu(Popup, 0);
CheckMenuItem(Popup, 1, MF_CHECKED|MF_BYPOSITION);
POINT Point;
GetCursorPos(&Point);
switch (TrackPopupMenu(Popup, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, Point.x, Point.y, 0, GetActiveWindow(), NULL)) {
// ...
Primarily, LoadMenu(0, MAKEINTRESOURCE(IDR_NED_MENU)); is returning NULL, and I'm getting an error message that states that "The specified resource name cannot be found in the image file."
Now, IDR_NED_MENU is the ID of a menu I have in the .rc file, and I've included the corresponding .rc.h file in this .cpp file. The actual dialog window IDs contained in the same .rc file work perfectly. This code is further copied and pasted from another project where the LoadMenu call worked perfectly: I did recreate the IDR_NED_MENU from scratch, though, and the IDs are somewhat different (but they do match between the .rc file and the .cpp file that has the code snippet I've pasted here); originally I'd accidentally created the menu in a separate .rc file so I sought to rectify that here. I noticed that in Visual Studio's Resource View, the dialogs are contained in the Dialog folder, while this is contained in the Menu folder (sensible), but I'm not sure what, if any, difference that makes.
Why would I be getting this error? Why can't it find IDR_NED_MENU?
I'm using Visual Studio 2010, and this is not an MFC project. I'm not sure what, if any, other relevant details I should include; let me know in comments and I'll edit-update.
Thanks.
The first parameter to LoadMenu must be a handle to your executable image where the resource resides. The handle is the first HINSTANCE that you get in WinMain. Alternatively you can obtain it by a call to GetModuleHandle(0).

How can I set the default file type for a CFileDialog?

I am using CFileDialog for displaying the open file dialog. I have set the filter as follows:
static TCHAR BASED_CODE szFilter[] = _T("Chart Files (*.xlc)|*.xlc|")
_T("Worksheet Files (*.xls)|*.xls|Data Files (*.xlc;*.xls)|")
_T("*.xlc; *.xls|All Files (*.*)|*.*||");
I need to set the default file type to be "Worksheet Files" whenever I DoModal the dialog box. I am unable to figure out how to do it. MS Paint is doing, it selects the "All Picture files" when we open the open file dialog.
Please let me know how to do it.
You're looking for the SetDefExt function. This allows you to specify the default file extension for an open/save file dialog box. Remember that the string you specify should not contain a period (.).
Of course, you could also just specify this in the constructor. The second parameter is the default extension (lpszDefExt).
You should read and write
This code will do the job during the run time of your program. To be able to display the last used selection next time you run your program, you can store the value of LastIndex in the Registry.
// A dialog box with several filters for various media file types
static int LastIndex = -1; // Holds the last used filter. You can store it in the Registry to use it during next run.
const TCHAR szFilter[] = _T("Video Files (*.mpg, *.mov, *.mp4)|*.mpg;*.mov;*.mp4|Audio Files (*.wav, *.mp3, *.m4a, *.flac)|*.wav;*.mp3;*.m4a;*.flac|MXF Files (*.mxf)|*.mxf|All Files (*.*)|*.*||");
CFileDialog dlg(TRUE, _T("Select Media File"), NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, szFilter, this);
if(LastIndex != -1) dlg.m_ofn.nFilterIndex = LastIndex; // restore last used index
// from last time
if (dlg.DoModal() == IDOK)
{
LastIndex = dlg.m_ofn.nFilterIndex; // Store last used index for next time
CString sFilePath = dlg.GetPathName();
}