PRB: GetSaveFileName Allows You to Select Invalid Folder

ID: Q245718


The information in this article applies to:
  • Microsoft Win32 Application Programming Interface (API), on platform(s):
    • Microsoft Windows 98
    • Microsoft Windows NT 4.0
    • Microsoft Windows 95


SYMPTOMS

When a Save As dialog box is displayed using GetSaveFileName, the dialog box allows you to select an invalid folder as the destination folder when you click Save. When an invalid folder is selected, GetSaveFileName uses the current working directory when returning the full path and filename. Examples of invalid folders are Network Neighborhood and network server names, such as \\MyMachine.


CAUSE

The GetSaveFileName does not validate the destination folder to see if it is part of the file system.


RESOLUTION

An application using GetSaveFileName can verify if the destination folder is part of the file system by using a hook procedure that handles the CDN_FILEOK notification.

When handling the CDN_FILEOK notification, the hook procedure obtains a pointer to the ITEMIDLIST (PIDL) of the selected folder by sending the dialog box a CDM_GETFOLDERIDLIST message. The returned PIDL is then passed to SHGetFileInfo to request the attributes of the folder. If the folder has the SFGAO_FILESYSTEM attribute it is part of the file system and can be used as the destination folder.


MORE INFORMATION

The following code sample implements a hook procedure that handles the CDN_FILEOK notification and validates the destination folder when you click Save. If an invalid folder is selected, an error is displayed and the dialog box remains open.


// 
//  SaveDialog.cpp
// 

#include <windows.h>
#include <commdlg.h>
#include <shellapi.h>
#include <shlobj.h>


UINT_PTR WINAPI SaveAs_OnFileOK (HWND hWnd, OFNOTIFY *pofn)
{
    LPMALLOC pMalloc = NULL;
    LPITEMIDLIST pidlFolder = NULL;
    SHFILEINFO shfi = {0};
    INT cbPidl = 0;
    UINT_PTR nReturn = 0;
    UINT_PTR nResult = 0;

    // 
    //  Get a pointer to the shell's IMalloc interface
    //  
    if (FAILED(SHGetMalloc(&pMalloc)))
        goto Cleanup;

    // 
    //  Get the size of the pidl
    // 
    cbPidl = SendMessage (pofn->hdr.hwndFrom, CDM_GETFOLDERIDLIST, 0, 0);
    if (cbPidl == -1)
        goto Cleanup;

    // 
    //  Allocate memory for the pidl
    // 
    pidlFolder = (LPITEMIDLIST) pMalloc->Alloc (cbPidl);
    if (pidlFolder == NULL)
        goto Cleanup;

    // 
    //  Get the pidl
    // 
    cbPidl = SendMessage (pofn->hdr.hwndFrom, CDM_GETFOLDERIDLIST, 
                          (WPARAM) cbPidl, (LPARAM) pidlFolder);
    if (cbPidl == -1)
        goto Cleanup;

    // 
    //  Determine if the folder has the SFGAO_FILESYSTEM attribute
    // 
    if (!SHGetFileInfo ((LPCTSTR) pidlFolder, 0, &shfi, 
                        sizeof(SHFILEINFO), 
                        SHGFI_PIDL | SHGFI_ATTRIBUTES))
    goto Cleanup;

    if (!(shfi.dwAttributes & SFGAO_FILESYSTEM))
    {
        // 
        //  Display an error and prevent the dialog from closing by
        //  returning 1 from the hook proc and by setting 
        //  DWL_MSGRESULT to 1.
        // 
        MessageBox (hWnd, 
                    TEXT("You cannot save in the folder you specified. Please choose another location."),
                    TEXT("Save As"), MB_OK | MB_ICONINFORMATION);
        nResult = nReturn = 1;
    }

Cleanup:
    if (pMalloc)
    {
        if (pidlFolder)
            pMalloc->Free ((LPVOID)pidlFolder);
        pMalloc->Release();
    }

    SetWindowLong (hWnd, DWL_MSGRESULT, nResult);
    return nReturn;
}


UINT_PTR WINAPI SaveAs_OnNotify (HWND hWnd, NMHDR *phdr)
{
    switch (phdr->code)
    {
        case CDN_FILEOK:
            return SaveAs_OnFileOK (hWnd, (OFNOTIFY *) phdr);
    }
    return 0;
}


UINT_PTR CALLBACK SaveAsHookProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_NOTIFY:
            return SaveAs_OnNotify (hWnd, (NMHDR *)lParam);
    }
    return 0;
}


INT WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                    LPSTR lpCmdLine, INT nShowCmd)
{
    TCHAR szFilename[MAX_PATH] = TEXT("");
    BOOL bResult = FALSE;
    DWORD dwError = NOERROR;
    OPENFILENAME ofn = {0};

    ofn.lStructSize = sizeof (OPENFILENAME);
    ofn.lpstrFilter = TEXT("All Files\0*.*\0\0");
    ofn.lpstrFile = szFilename;
    ofn.nMaxFile = MAX_PATH;
    ofn.Flags = OFN_EXPLORER | 
                OFN_ENABLEHOOK | 
                OFN_HIDEREADONLY | 
                OFN_NOCHANGEDIR | 
                OFN_PATHMUSTEXIST;
    ofn.lpfnHook = (LPOFNHOOKPROC) SaveAsHookProc;

    bResult = GetSaveFileName (&ofn);
    if (bResult == FALSE)
    {
        dwError = CommDlgExtendedError ();
        return dwError;
    }

    MessageBox (NULL, szFilename, TEXT("SaveAs returned..."), MB_OK);
    return 0;
} 

Additional query words:

Keywords : kbCmnDlg kbCmnDlgSave kbNTOS400 kbSDKWin32 kbGrpUser kbWinOS95 kbWinOS98 kbDSupport
Version : winnt:4.0
Platform : winnt
Issue type : kbprb


Last Reviewed: November 24, 1999
© 2000 Microsoft Corporation. All rights reserved. Terms of Use.