PRB: GetSaveFileName Allows You to Select Invalid Folder (245718)



The information in this article applies to:

  • Microsoft Win32 Application Programming Interface (API), when used with:
    • Microsoft Windows 98
    • Microsoft Windows NT Server 4.0
    • Microsoft Windows NT Workstation 4.0
    • Microsoft Windows 95

This article was previously published under Q245718

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

Modification Type:MinorLast Reviewed:12/20/2004
Keywords:kbCmnDlg kbCmnDlgSave kbprb KB245718