comdlg32: Correctly handle filters with multiple file extensions in Save As dialogs (try 3)

Alex Henrie alexhenrie24 at gmail.com
Fri Jan 20 15:01:04 CST 2012


Fixes bug 29362.

The MSDN documentation of this behavior can be found at
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646960%28v=vs.85%29.aspx#_win32_Filters
---
  dlls/comdlg32/filedlg.c       |   61 ++++++++++++++++++++---------
  dlls/comdlg32/tests/filedlg.c |   85 ++++++++++++++++++++++++++++++-----------
  2 files changed, 104 insertions(+), 42 deletions(-)

diff --git a/dlls/comdlg32/filedlg.c b/dlls/comdlg32/filedlg.c
index 9b725ba..76b8e45 100644
--- a/dlls/comdlg32/filedlg.c
+++ b/dlls/comdlg32/filedlg.c
@@ -2534,12 +2534,12 @@ BOOL FILEDLG95_OnOpen(HWND hwnd)

          /* Attach the file extension with file name*/
          ext = PathFindExtensionW(lpstrPathAndFile);
-        if (! *ext)
+        if (! *ext && fodInfos->defext)
          {
              /* if no extension is specified with file name, then */
              /* attach the extension from file filter or default one */

-            const WCHAR *filterExt = NULL;
+            WCHAR *filterExt = NULL;
              LPWSTR lpstrFilter = NULL;
              static const WCHAR szwDot[] = {'.',0};
              int PathLength = lstrlenW(lpstrPathAndFile);
@@ -2549,15 +2549,39 @@ BOOL FILEDLG95_OnOpen(HWND hwnd)
                                               fodInfos->ofnInfos->nFilterIndex-1);

              if (lpstrFilter != (LPWSTR)CB_ERR)  /* control is not empty */
-                filterExt = PathFindExtensionW(lpstrFilter);
+            {
+                WCHAR* filterAtSemicolon;
+                filterExt = HeapAlloc(GetProcessHeap(), 0, lstrlenW(lpstrFilter) * sizeof(WCHAR) + sizeof(WCHAR));
+                StrCpyW(filterExt, lpstrFilter);
+
+                /* if a semicolon-separated list of file extensions was given, do not include the
+                   semicolon or anything after it in the extension.
+                   example: if filterExt was "*.abc;*.def", it will become "*.abc" */
+                filterAtSemicolon = StrChrW(filterExt, ';');
+                if (filterAtSemicolon)
+                {
+                    filterAtSemicolon[0] = '\0';
+                }
+
+                /* strip the * or anything else from the extension, "*.abc" becomes "abc" */
+                StrCpyW(filterExt, PathFindExtensionW(filterExt) + 1);
+
+                /* if the extension contains a glob, ignore it */
+                if (strchrW(filterExt, '*') || strchrW(filterExt, '?'))
+                {
+                    HeapFree(GetProcessHeap(), 0, filterExt);
+                    filterExt = NULL;
+                }
+            }

-            if ( filterExt && *filterExt ) /* attach the file extension from file type filter*/
-                filterExt = filterExt + 1;
-            else if ( fodInfos->defext ) /* attach the default file extension*/
-                filterExt = fodInfos->defext;
+            if (!filterExt)
+            {
+                /* use the default file extension */
+                filterExt = HeapAlloc(GetProcessHeap(), 0, lstrlenW(fodInfos->defext) * sizeof(WCHAR) + sizeof(WCHAR));
+                StrCpyW(filterExt, fodInfos->defext);
+            }

-            /* If extension contains a glob, ignore it */
-            if ( filterExt && !strchrW(filterExt, '*') && !strchrW(filterExt, '?') )
+            if (*filterExt) /* ignore filterExt="" */
              {
                  /* Attach the dot*/
                  lstrcatW(lpstrPathAndFile, szwDot);
@@ -2565,20 +2589,19 @@ BOOL FILEDLG95_OnOpen(HWND hwnd)
                  lstrcatW(lpstrPathAndFile, filterExt );
              }

+            HeapFree(GetProcessHeap(), 0, filterExt);
+
              /* In Open dialog: if file does not exist try without extension */
              if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
                    lpstrPathAndFile[PathLength] = '\0';
-        }

-    if (fodInfos->defext) /* add default extension */
-    {
-      /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
-      if (*ext)
-        ext++;
-      if (!lstrcmpiW(fodInfos->defext, ext))
-        fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
-      else
-        fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
+            /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
+            if (*ext)
+                ext++;
+            if (!lstrcmpiW(fodInfos->defext, ext))
+                fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
+            else
+                fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
      }

      /* In Save dialog: check if the file already exists */
diff --git a/dlls/comdlg32/tests/filedlg.c b/dlls/comdlg32/tests/filedlg.c
index 8539d3d..0d1112f 100644
--- a/dlls/comdlg32/tests/filedlg.c
+++ b/dlls/comdlg32/tests/filedlg.c
@@ -1044,51 +1044,90 @@ static UINT_PTR WINAPI test_extension_wndproc(HWND dlg, UINT msg, WPARAM wParam,
      return FALSE;
  }

-static const char *defext_filters[] = {
-    "TestFilter (*.pt*)\0*.pt*\0",
-    "TestFilter (*.ab?)\0*.ab?\0",
-    "TestFilter (*.*)\0*.*\0",
-    NULL    /* is a test, not an endmark! */
-};
-
  #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))

+static void test_extension_helper(OPENFILENAME* ofn, const char *filter,
+                                  const char *expected_filename)
+{
+    char *filename_ptr;
+    DWORD ret;
+    BOOL boolret;
+
+    strcpy(ofn->lpstrFile, "deadbeef");
+    ofn->lpstrFilter = filter;
+
+    boolret = GetSaveFileNameA(ofn);
+    ok(boolret, "%s: expected TRUE\n", filter);
+
+    ret = CommDlgExtendedError();
+    ok(!ret, "%s: CommDlgExtendedError returned %#x\n", filter, ret);
+
+    filename_ptr = ofn->lpstrFile + ofn->nFileOffset;
+    ok(strcmp(filename_ptr, expected_filename) == 0,
+        "%s: Filename is %s, expected %s\n", filter, filename_ptr, expected_filename);
+}
+
  static void test_extension(void)
  {
      OPENFILENAME ofn = { sizeof(OPENFILENAME)};
      char filename[1024] = {0};
      char curdir[MAX_PATH];
-    char *filename_ptr;
-    const char *test_file_name = "deadbeef";
      unsigned int i;
-    DWORD ret;
      BOOL boolret;

+    const char *defext_concrete_filters[] = {
+        "TestFilter (*.abc)\0*.abc\0",
+        "TestFilter (*.abc;)\0*.abc;\0",
+        "TestFilter (*.abc;*.def)\0*.abc;*.def\0",
+    };
+
+    const char *defext_wildcard_filters[] = {
+        "TestFilter (*.pt*)\0*.pt*\0",
+        "TestFilter (*.pt*;*.abc)\0*.pt*;*.abc\0",
+        "TestFilter (*.ab?)\0*.ab?\0",
+        "TestFilter (*.*)\0*.*\0",
+        NULL    /* is a test, not an endmark! */
+    };
+
      boolret = GetCurrentDirectoryA(sizeof(curdir), curdir);
      ok(boolret, "Failed to get current dir err %d\n", GetLastError());

-    /* Ignore .* extension */
      ofn.lStructSize = sizeof(ofn);
      ofn.hwndOwner = NULL;
      ofn.lpstrFile = filename;
      ofn.nMaxFile = MAX_PATH;
      ofn.Flags = OFN_EXPLORER | OFN_ENABLEHOOK;
-    ofn.lpstrDefExt = NULL;
      ofn.lpstrInitialDir = curdir;
      ofn.lpfnHook = test_extension_wndproc;
      ofn.nFileExtension = 0;

-    for (i = 0; i < ARRAY_SIZE(defext_filters); i++) {
-        ofn.lpstrFilter = defext_filters[i];
-        strcpy(filename, test_file_name);
-        boolret = GetSaveFileNameA(&ofn);
-        ok(boolret, "%u: expected true\n", i);
-        ret = CommDlgExtendedError();
-        ok(!ret, "%u: CommDlgExtendedError returned %#x\n", i, ret);
-        filename_ptr = ofn.lpstrFile + strlen( ofn.lpstrFile ) - strlen( test_file_name );
-        ok( strlen(ofn.lpstrFile) >= strlen(test_file_name), "Filename %s is too short\n", ofn.lpstrFile );
-        ok( strcmp(filename_ptr, test_file_name) == 0,
-            "Filename is %s, expected %s\n", filename_ptr, test_file_name );
+    ofn.lpstrDefExt = NULL;
+
+    /* Without lpstrDefExt, append no extension */
+    test_extension_helper(&ofn, "TestFilter (*.abc) lpstrDefExt=NULL\0*.abc\0", "deadbeef");
+    test_extension_helper(&ofn, "TestFilter (*.ab?) lpstrDefExt=NULL\0*.ab?\0", "deadbeef");
+
+    ofn.lpstrDefExt = "";
+
+    /* If lpstrDefExt="" and the filter has a concrete extension, append it */
+    test_extension_helper(&ofn, "TestFilter (*.abc) lpstrDefExt=\"\"\0*.abc\0", "deadbeef.abc");
+
+    /* If lpstrDefExt="" and the filter has a wildcard extension, do nothing */
+    test_extension_helper(&ofn, "TestFilter (*.ab?) lpstrDefExt=\"\"\0*.ab?\0", "deadbeef");
+
+    ofn.lpstrDefExt = "xyz";
+
+    /* Append concrete extensions from filters */
+    for (i = 0; i < ARRAY_SIZE(defext_concrete_filters); i++) {
+        test_extension_helper(&ofn, defext_concrete_filters[i], "deadbeef.abc");
+    }
+
+    /* Append nothing from this filter */
+    test_extension_helper(&ofn, "TestFilter (*.)\0*.\0", "deadbeef");
+
+    /* Ignore wildcard extensions in filters */
+    for (i = 0; i < ARRAY_SIZE(defext_wildcard_filters); i++) {
+        test_extension_helper(&ofn, defext_wildcard_filters[i], "deadbeef.xyz");
      }
  }

-- 
1.7.4.1




More information about the wine-patches mailing list