How can I add text to the file I create using IFileOperation? - c++

I looked at the MSDN samples for IFileOperation, and they show how to create a file, but I don't want to create a blank file, I want to add some text in it. Here's what I have from the sample:
IFileOperation *pfo;
HRESULT hr = CreateAndInitializeFileOperation(IID_PPV_ARGS(&pfo));
if (SUCCEEDED(hr))
{
hr = pfo->NewItem(psiFolder, FILE_ATTRIBUTE_NORMAL, c_szFileName, NULL, NULL);
if (SUCCEEDED(hr))
{
hr = pfo->PerformOperations();
}
pfo->Release();
}
In the pfo->NewItem call, the fourth parameter is for the template, but I don't want to define a template.I want to create a .Url file with the content:
[InternetShortcut]
URL=www.google.com
I can't even seem to control the format of the file, let alone the content. Please help!

IFileOperation::NewItem lets you access the functionality of the 'ShellNew' menu (the menu you get if you right-click in a folder, and select New from the context menu). It's designed to let you make a folder or an empty file, or create a new file from a pre-existing template. It doesn't let you make a file containing arbitrary data.
Once you've created a new empty file you could open it using CreateFile() and write your data to it using WriteFile(). But note that CreateFile() could create the file for you as well - there's really no need to use IFileOperation simply to create an empty file (unless you need its support for UAC to create the file in a protected location, but in that case you'll have trouble writing to it afterwards).

Related

C++ MFC CFileDialog won't save

I created a MFC programm with a menu option to Save a file. When I click it it shows the CFileDialog and I can choose the location where I want to save my file. When I Click save in the Dialog, it closes the dialog, but after that it does nothing. AND it didn't save my file. What am I doing wrong?
Here is the code
CFileDialog *dlg = new CFileDialog(FALSE, L"dr", NULL, NULL,
L"Drawings (*.dr)|*.dr|"
L"All Files||");
bool result = dlg->DoModal();
if(result)
{
MessageBox(0, dlg->GetPathName(), L"Draw", 0);
}
The bool result, is purely there to check if there is no problem/error.
The file save dialog is called "file save dialog" because its caption says "Save File" and it allows you to only select a single file. That does not mean that it actually does any saving of files. It just returns to you the filename selected by the user. You are still responsible for writing the code which will save your file using this filename.
The CFileDialog does not save the file for you, it only provides you with a dialog for the user to determine where (and if!) the file should be saved. The return value of DoModal() should be compared to IDOK before proceeding to save. From the dialog's member functions you can get the path and filename the user selected. With that, you can create/open a file and save your data.

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.

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();
}

Hardcoding the resources in application

I have some code which shows a simple dialog box and handles user action (written using plain WinAPI).
// Display dialog and handle user action
LRESULT choice = DialogBoxParam(NULL, MAKEINTRESOURCE(AP_IDD_DIALOG), NULL, (DLGPROC)DialogCallback, NULL);
Is there any way to hardcode the resource file dialog.rc, which is used to build the dialog ?(I would like to get rid of .rc files and I'm pretty sure there is a way, yet I don't know what it is :)
Edit
Also, does someone have any ideas on converting existing .rc files into hardcoded resources? Is this possible?
*.rc (resource) files are source code, they are compiled with the resource compiler and linked into your object (.exe/.dll)
You don't need to ship the resource file or have it present with your app to run it.
If you want to move to programmatically defined windows rather than templates then you might want to be looking at QT/wxWidgets. But thats a fair chunk of overhead for 1 dialog!
I'm surprised I couldn't find an existing app to do this sort of thing, enough hits on google with people trying to do this.
Ok, so the DLGTEMPLATE is a variable length blob of data, normally you let the dialog function pull it from the resource bundle for you, but instead you want to store it in your program.
You need to change your static lib to have a new function to decode some 'blob' back into the dlgtemplate, and you need to generate the blob. (or add the blob in your code without decoding which I don't want to think about right now)
The following code will give you the DLGTemplate data you need to imbed in your app. (cut from larger project)
HGLOBAL LoadResourceImpl(wchar_t *resource, wchar_t *type)
{
HRSRC handle = FindResource(hInstance, resource,type);
if (handle)
{
HGLOBAL hResource = LoadResource(hInstance, handle);
if (hResource)
return LockResource(hResource);
}
return 0;
}
DLGTEMPLATE * LoadDialog(wchar_t *resource)
{
return (DLGTEMPLATE *) LoadResourceImpl(resource,RT_DIALOG);
}
DLGTEMPLATE * LoadDialog(int resource)
{
return (DLGTEMPLATE *) LoadResourceImpl(MAKEINTRESOURCE(resource),RT_DIALOG);
}
Make an app that includes your resource - use the appropriate LoadDialog to get the data.
Now "write out" that blob in a format to include in your app -
step 1 - find out how much data there is by traversing the structure to find the total size including all the controls (control count is in DLGTEMPLATE::cdit)
step 2 - convert the data to something you can compile into your code - like HEX
Add to your static library a new 'HEX' to DLGTEMPLATE method and the hex string you made using the other app.
Can we hard code the .res file into the program?
the resource compiler converts .rc into .res
use a hex dump tool (eg. winhex) to translate the .res into bytes array
(represented in C source code).
add the source code file in the project and compile in the executable.
locate the dialog resource position from the array and use DialogBoxIndirect.
DialogBoxParamIndirect can be used instead. It takes as a parameter the dialog template. Raymond Chen's blog has an example of building a dialog box at runtime rather than from a resource using the DialogBox*Indirect API's.
Per MSDN, dialog box resources are basically composed of the DLGTEMPLATE and DLGITEMTEMPLATE structures. So you should be able to use the resource API's (FindResource, LoadResource, and LockResource) to get at the underlying bits of an existing dialog resource, and embed that within your code.
Note that this is a lot more painful than using the .rc file. It's much more difficult to make changes to your layout, and it's also much less localizable, since localization would now require a code change to update the template in code.
If it's a simple dialog, why use the DLGTEMPLATE at all?
Nothing stops you from simply doing ::CreateWindow'ing those controls directly. If it's a simple dialog with 2-3 buttons and a couple text fields, simply call ::CreateWindow, passing in the window class of whatever common control you're using.
This is essentially what the DialogXxxxx functions do anyway. DLGTEMPLATE is a convenience for declaratively laying out your forms, and having the boilerplate make the appropriate CreateWindow calls, etc.

What is the "Shell Namespace" way to create a new folder?

Obviously this is trivial to do with win32 api - CreateDirectory(). But I'm trying to host an IShellView, and would like to do this the most shell-oriented way. I would have thought that there would be a createobject or createfolder or some such from an IShellFolder. But neither IShellView nor IShellFolder nor even IFolderView seem to have anything quite like this.
Is there a Shell-programming way to create a new folder? Or do I need to create a folder using a pathname, the old-fashioned way?
If I have to do it via CreateDirectory(), then my next question might be: any ideas as to how to get the IShellView / IFolderView to actually see this new object and display it to the user?
Motivation: Creating my own File Dialog replacement and I want to provide the "new folder" toolbar icon functionality of the standard XP-style file dialog.
EDIT: I went ahead and created something that basically works, using CreateDirectory. However, I'm still hoping that there's a better way to do this, but so that you can see how that works, and to offer better ideas as to solve this issue better:
PidlUtils::Pidl pidl(m_folder);
CFilename folderName(GetDisplayNameOf(pidl), "New Folder");
for (int i = 2; folderName.Exists(); ++i)
folderName.SetFullName(FString("New Folder (%d)", i));
if (!CPathname::Create(folderName, false))
throw CContextException("Unable to create a new folder here: ");
// get the PIDL for the newly created folder
PidlUtils::Pidl pidlNew;
#ifdef UNICODE
const wchar_t * wszName = folderName.c_str();
#else
wchar_t wszName[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, folderName.GetFullName(), -1, wszName, MAX_PATH);
#endif
m_hresult = m_folder->ParseDisplayName(NULL, NULL, wszName, NULL, pidlNew, NULL);
if (FAILED(m_hresult))
throw CLabeledException(FString("Unable to get the PIDL for the new folder: 0x%X", m_hresult));
// upgrade our interface so we can select & rename it
CComQIPtr<IShellView2> sv2(m_shell_view);
if (!sv2)
throw CLabeledException("Unable to obtain the IShellView2 we need to rename the newly created folder.");
// force it to see thew new folder
sv2->Refresh();
// select the new folder, and begin the rename process
m_hresult = sv2->SelectAndPositionItem(pidlNew, SVSI_EDIT|SVSI_DESELECTOTHERS|SVSI_ENSUREVISIBLE|SVSI_POSITIONITEM, NULL);
if (FAILED(m_hresult))
throw CLabeledException(FString("Unable to select and position the new folder item: 0x%X", m_hresult));
Yes, you can get IContextMenu and look for sub menus, but why bother, just call SHChangeNotify after you call CreateDirectory
Shell folders usually implement the IStorage interface, so this is pretty simple. For example, the following creates a folder named "abcd" on the desktop:
CComPtr<IShellFolder> pDesktop;
HRESULT hr = SHGetDesktopFolder(&pDesktop);
if (FAILED(hr)) return;
CComQIPtr<IStorage> pStorage(pDesktop);
if (!pStorage) return;
CComPtr<IStorage> dummy;
hr = pStorage->CreateStorage(L"abcd", STGM_FAILIFTHERE, 0, 0, &dummy);
The Win32 API function CreateDirectory seems to be the "correct way". At least, I can find nothing better - and the answers here don't really shed any light on a better way to do it.