HOWTO: Handle FNERR_BUFFERTOOSMALL in Windows 95

ID: Q131462

The information in this article applies to:

SUMMARY

When an application uses the Open File common dialog with the OFN_ALLOWMULTISELECT flag, there is a danger that the buffer passed to the common dialog in the OPENFILENAME.lpstrFile field will be too small. In this situation, GetOpenFileName() will return an error value and CommDlgExtendedError() will return FNERR_BUFFERTOOSMALL.

To work around this problem, watch for the Open or OK button to be pressed in the dialog hook; then reallocate the buffer if necessary.

This technique works on Windows version 3.1, Windows NT, and Windows 95, but the implementation details are different when dealing with Windows 95 Explorer-type dialog boxes versus traditional Open and Save common dialog boxes. This article explains how to do it in Windows 95.

MORE INFORMATION

With the introduction of the new common dialogs for Windows 95, a new way of handling the FNERR_BUFFERTOOSMALL error was developed. It is still necessary to watch for the Open button to be pressed and reallocate the buffer if needed, but the way to watch for the OK is much different.

When you install a hook on the Open File common dialog in Windows 95 using the OPENFILENAME.lpfnHook member, the dialog you are hooking is a child of the main Open File dialog. Therefore, to intercept the OK button, you need to subclass the parent dialog. To do this, you can install the hook procedure and watch for the CDN_INITDONE notification. The Open File dialog will send this as part of a WM_NOTIFY message when the initialization for the dialog is complete. For example:

   LRESULT CALLBACK DialogHook(HWND hwnd, UINT uMsg, WPARAM wParam,
                               LPARAM lParam)
   {
       static HWND hwndParentDialog;
       LPOFNOTIFY lpofn;

       switch (uMsg)
       {
           case WM_INITDIALOG:
               // You need to use a copy of the OPENFILENAME struct used to
               // create this dialog. You can store a pointer to the
               // OPENFILENAME struct in the ofn.lCustData so you can
               // retrieve it here in the lParam. Once you have it, you
               // need to hang on to it. Using window properties provides a
               // good thread safe solution to using a global variable.

               SetProp(hwnd, "OFN", lParam);
               return (0);

           case WM_NOTIFY:
               // The OFNOTIFY struct is passed in the lParam of this
               // message.

               lpofn = (LPOFNOTIFY) lParam;

               switch (lpofn->hdr.code)
               {
                   case CDN_INITDONE:
                       // Subclass the parent dialog to watch for the OK
                       // button.

                       hwndParentDialog = GetParent(hwnd);
                       g_lpfnDialogProc =
                               (FARPROC) SetWindowLong(hwndParentDialog,
                                                       DWL_DLGPROC,
                                                     OpenFileSubclassProc);
                       break;

               }
               return (0);

           case WM_DESTROY:
               // Need to clean up the subclassing we did on the dialog.
               SetWindowLong(hwndParentDialog, DWL_DLGPROC,
                             g_lpfnDialogProc);

               // Also need to free the property with the OPENFILENAME
               // struct.
               RemoveProp(hwnd, "OFN");
               return (0);
       }
       return (0);
   }

Once the parent dialog is subclassed, the program can watch for the actual Open button. When the program gets the Open button command, it needs to check to see if the buffer originally allocated is large enough to handle all the files selected. The CommDlg_OpenSave_GetFilePath() API will return the length needed. Here is an example of the subclass procedure:

   LRESULT CALLBACK OpenFileSubclassProc(HWND hwnd, UINT uMsg, WPARAM
                                         wParam, LPARAM lParam)
   {
       LPTSTR lpsz;
       WORD   cbLength;

       switch (uMsg)
       {
           case WM_COMMAND:
               switch (LOWORD(wParam))
               {
                   case IDOK:
                       // Need to verify the original buffer size is large
                       // enough to handle the files selected. The
                       // CommDlg_OpenSave_GetFilePath() API will return
                       // the length needed for this buffer.

                       cbLength = CommDlg_OpenSave_GetFilePath(hwnd,
                                                               NULL, 0);

                       // OFN_BUFFER_SIZE is the size of the buffer
                       // originally used in the OPENFILENAME.lpstrFile
                       // member.

                       if (OFN_BUFFER_SIZE < cbLength)
                       {
                           // The buffer is too small, so allocate a
                           // new buffer.
                           lpsz = (LPTSTR) HeapAlloc(GetProcessHeap(),
                                                     HEAP_ZERO_MEMORY,
                                                     cbLength);
                           if (lpsz)
                           {
                               // The OFN struct is stored in a property of
                               // the dialog window.

                               lpofn = (LPOPENFILENAME) GetProp(hwnd,
                                                                "OFN");

                               lpofn->lpstrFile = lpsz;
                               lpofn->nMaxFile  = cbLength;
                           }
                       }

                       // Now let the dialog handle the message normally.
                       break;
               }
               break;
       }

       return (CallWindowProc(g_lpfnDialogProc, hwnd, uMsg, wParam,
                              lParam));
   }

The dialog should now return without error. Be aware that the buffer allocated in the subclass procedure needs to be freed once the dialog returns.

Finally, this technique only works for 32-bit applications that are using the Explorer-type common dialogs. For 32-bit applications that don't use the OFN_EXPLORER flag, Windows 95 thunks to the 16-bit common dialog and the hook function only gets a copy of the OPENFILENAME structure.

Additional query words:

Keywords          : kbcode kbCmnDlg kbCmnDlgFileO kbGrpUser kbWinOS95 
Issue type        : kbhowto

Last Reviewed: December 26, 1998