Click here to Skip to main content
15,868,016 members
Articles / Desktop Programming / MFC
Article

CDocument::DoSave revealed

Rate me:
Please Sign up or sign in to vote.
4.80/5 (20 votes)
18 Sep 2002CPOL3 min read 186.9K   40   30
Explains how you can suppress the File-Save-As dialog in a Doc/View app, how to save files to multiple formats, and how DoSave is implemented.

Introduction

For one of my recent projects I had a rather strange requirement. It was an image conversion program basically, and it allowed you to open bitmap files. When the user clicked on the save icon in the toolbar or if he took the save-item from the File-menu, I was to save the image in a custom format using the same name as the original bitmap, but replacing the bmp extension with a custom extension. This meant that I should suppress the File-Save-As dialog. Initially I thought I simply had to override OnSaveDocument and refrain from calling the base class, but I quickly discovered that OnSaveDocument was too late to suppress the File-Save-As dialog.

DoSave revealed

I took a look at doccore.cpp and soon figured out the order in which methods got called inside the CDocument class. Essentially when the user tries to save a file, MFC command routing routes the message to

CDocument::OnFileSave
or CDocument::OnFileSaveAs depending on whether you clicked on Save or on Save-As. CDocument::OnFileSave calls CDocument::DoFileSave(). CDocument::DoFileSave() checks to see if the file exists and if it does, it proceeds to call CDocument::DoSave passing the full path of the file, else it calls CDocument::DoSave passing NULL for the file path. CDocument::OnFileSaveAs simply calls CDocument::DoSave passing NULL for the file path. Thus eventually we end up in CDocument::DoSave. So I decided that this was the method to override. CDocument::DoSave is declared thus :-

BOOL CDocument::DoSave(LPCTSTR lpszPathName, BOOL bReplace);
  • lpszPathName :- This is the full path of the file to save. If this is NULL the default implementation will prompt the user for a filename and path using the File-Save-As common dialog.
  • bReplace :- If TRUE it will replace an existing file, if FALSE it won't.

In my particular case I was least bothered with the working of the

DoSave
method. My intention was to get rid of this method totally. So this is what I did - I overrode this member function and did not call the base class implementation.

BOOL CBmpToXyzDoc::DoSave(LPCTSTR lpszPathName, BOOL bReplace)
{   
    //SrcPath is the full path of the current file
    CString DestPath = SrcPath;

    //I replace the extension with the custom one
    DestPath.Replace("bmp","xyz");

    //Now I simply call OnSaveDocument
    OnSaveDocument(DestPath);

    //File saved successfully
    return TRUE;
}

That was just what I had wanted to accomplish. The user never gets prompted and the file is saved using the same name as the original except for the change in extension.

Other plausible applications

While I did not specifically require it for my project, I figured that

DoSave
can be used for some other purposes too. At least one nifty usage came to my mind. Assume that I wanted to do different things based on some flag. For example assume that I want to show a Save-As dialog with JPG filter if the current file is a GIF and might want to show a Save-As dialog with GIF filter if the current file is a JPG. If so, I could show my own CFileDialog after setting the corresponding OPENFILENAME members.

BOOL CBmpToXyzDoc::DoSave(LPCTSTR lpszPathName, BOOL bReplace)
{   
    CFileDialog fd(false);

    if(m_bgif)
    {
        fd.m_ofn.lpstrFilter="JPG Files(*.jpg)\0*.jpg\0\0";
        fd.m_ofn.lpstrDefExt="jpg";
        fd.m_ofn.lpstrTitle ="Save as JPG";
    }
    else
    {
        fd.m_ofn.lpstrFilter="GIF Files(*.gif)\0*.gif\0\0";
        fd.m_ofn.lpstrDefExt="gif";
        fd.m_ofn.lpstrTitle ="Save as GIF";
    }

    if(fd.DoModal()==IDOK)
    {
        if(m_bgif)
            OnSaveJpgDocument(fd.GetPathName());
        else
            OnSaveGifDocument(fd.GetPathName());
    }

    return TRUE;    
}

Tech notes

The CDocument::DoSave implementation is very interesting. If lpszPathName is NULL, it calls CWinApp::DoPromptFileName :-

if (!AfxGetApp()->DoPromptFileName(newName,
    bReplace ? AFX_IDS_SAVEFILE : AFX_IDS_SAVEFILECOPY,
    OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, FALSE, pTemplate))
{
    return FALSE;
}

CWinApp::DoPromptFileName itself calls

CDocManager::DoPromptFileName
.

BOOL CWinApp::DoPromptFileName(CString& fileName, 
                               UINT nIDSTitle, 
                               DWORD lFlags,
                               BOOL bOpenFileDialog, 
                               CDocTemplate* pTemplate)
{
    ASSERT(m_pDocManager != NULL);
    return m_pDocManager->DoPromptFileName(fileName, 
        nIDSTitle, lFlags,  bOpenFileDialog, pTemplate);
}

CDocManager::DoPromptFileName simply uses CFileDialog to prompt for a filename.

BOOL CDocManager::DoPromptFileName(CString& fileName, 
                                   UINT nIDSTitle, 
                                   DWORD lFlags, 
                                   BOOL bOpenFileDialog, 
                                   CDocTemplate* pTemplate)
{
    CFileDialog dlgFile(bOpenFileDialog, NULL, NULL, 
        OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, NULL, NULL, 0);

    CString title;
    VERIFY(title.LoadString(nIDSTitle));

    dlgFile.m_ofn.Flags |= lFlags;

    //...

    INT_PTR nResult = dlgFile.DoModal();

    //...

Of course it does a lot of stuff in addition to just showing the file dialog. For example it will append a *.* filter to your File dialogs, which is why in addition to your document filter, you'll also see a *.* filter in the file type drop-down combo-box. Knowing how the flow proceeds is handy in the sense that if you want to customize it without hooking the window, you might simply override CWinApp::DoPromptFileName and call your own CFileDialog there (remember that this will affect both Open and Save dialogs).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Nish Nishant is a technology enthusiast from Columbus, Ohio. He has over 20 years of software industry experience in various roles including Chief Technology Officer, Senior Solution Architect, Lead Software Architect, Principal Software Engineer, and Engineering/Architecture Team Leader. Nish is a 14-time recipient of the Microsoft Visual C++ MVP Award.

Nish authored C++/CLI in Action for Manning Publications in 2005, and co-authored Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his WordPress blog. Nish is experienced in technology leadership, solution architecture, software architecture, cloud development (AWS and Azure), REST services, software engineering best practices, CI/CD, mentoring, and directing all stages of software development.

Nish's Technology Blog : voidnish.wordpress.com

Comments and Discussions

 
GeneralUseful! Thanks! Pin
wutuma5-Dec-12 22:01
wutuma5-Dec-12 22:01 
GeneralMy vote of 5 Pin
Nithin Sundar15-Feb-11 17:59
Nithin Sundar15-Feb-11 17:59 
GeneralNot Virtual Pin
mvinca14-Jan-05 5:56
mvinca14-Jan-05 5:56 
GeneralRe: Not Virtual Pin
boz11-Feb-05 1:04
boz11-Feb-05 1:04 
GeneralSave As with Encoding option Pin
Anonymous20-May-04 6:26
Anonymous20-May-04 6:26 
Generalproblem in saveas Pin
Member 202398-May-03 0:01
Member 202398-May-03 0:01 
Generalproblem in saveas Pin
Member 202398-May-03 0:01
Member 202398-May-03 0:01 
GeneralNice article Pin
Kannan Kalyanaraman20-Sep-02 5:21
Kannan Kalyanaraman20-Sep-02 5:21 
GeneralRe: Nice article Pin
Nish Nishant20-Sep-02 14:25
sitebuilderNish Nishant20-Sep-02 14:25 
GeneralRe: Nice article Pin
Rosenkilde6-May-03 4:54
Rosenkilde6-May-03 4:54 
GeneralPretty Good Pin
Tom Welch19-Sep-02 10:10
Tom Welch19-Sep-02 10:10 
GeneralRe: Pretty Good Pin
Nish Nishant19-Sep-02 13:41
sitebuilderNish Nishant19-Sep-02 13:41 
GeneralHmm... Pin
Shog919-Sep-02 6:22
sitebuilderShog919-Sep-02 6:22 
GeneralRe: Hmm... Pin
Nish Nishant19-Sep-02 13:43
sitebuilderNish Nishant19-Sep-02 13:43 
GeneralRe: Hmm... Pin
Shog919-Sep-02 13:44
sitebuilderShog919-Sep-02 13:44 
GeneralA tiny little problem Pin
Yawar Maajed19-Sep-02 5:45
Yawar Maajed19-Sep-02 5:45 
GeneralRe: A tiny little problem Pin
Yawar Maajed19-Sep-02 5:50
Yawar Maajed19-Sep-02 5:50 
GeneralRe: A tiny little problem Pin
Nish Nishant19-Sep-02 13:56
sitebuilderNish Nishant19-Sep-02 13:56 
GeneralRe: A tiny little problem Pin
Nish Nishant19-Sep-02 14:00
sitebuilderNish Nishant19-Sep-02 14:00 
GeneralRe: A tiny little problem Pin
Yawar Maajed19-Sep-02 15:30
Yawar Maajed19-Sep-02 15:30 
GeneralRe: A tiny little problem Pin
Nish Nishant19-Sep-02 15:55
sitebuilderNish Nishant19-Sep-02 15:55 
GeneralInteresting... Pin
Roger Allen19-Sep-02 0:10
Roger Allen19-Sep-02 0:10 
GeneralRe: Interesting... Pin
Nish Nishant19-Sep-02 0:12
sitebuilderNish Nishant19-Sep-02 0:12 
GeneralRe: Interesting... Pin
Joao Vaz19-Sep-02 0:27
Joao Vaz19-Sep-02 0:27 
In the past I had do a similar thing , and resolved the same way as you did, so the article is more than enough for this topic in question.
When a article deliver what it promises , well ..., it deserves 5 stars, it's my way of voting the articles.


Cheers,Joao Vaz
And if your dream is to care for your family, to put food on the table, to provide them with an education and a good home, then maybe suffering through an endless, pointless, boring job will seem to have purpose. And you will realize how even a rock can change the world, simply by remaining obstinately stationary.-Shog9
GeneralRe: Interesting... Pin
Nish Nishant19-Sep-02 0:40
sitebuilderNish Nishant19-Sep-02 0:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.