]> git.saurik.com Git - wxWidgets.git/commitdiff
Add support for MSW unique volume names to wxFileName.
authorVadim Zeitlin <vadim@wxwidgets.org>
Sat, 5 Dec 2009 17:31:00 +0000 (17:31 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Sat, 5 Dec 2009 17:31:00 +0000 (17:31 +0000)
Recognize the paths starting with "\\?\Volume{GUID}" under MSW and provide a
way to test for them.

Closes #8874.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@62782 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/changes.txt
include/wx/filename.h
interface/wx/filename.h
src/common/filename.cpp
tests/filename/filenametest.cpp

index dda8f8f2794a815c09091d6d3295302fe5b25358..a642866566e7649b85d2bf31973896908d58f887 100644 (file)
@@ -432,6 +432,7 @@ All:
   wxVariant.
 - wxDateTime timezone functions now dynamic (no caching).
 - Added wxHttp::GetCookie and wxHttp::HasCookies (dodge).
+- Added support for unique volume names to wxFileName (Neno Ganchev).
 
 Unix:
 
index 7bb7a09460929f07a222a943f473c9d2d570033f..1a6abd73ebf23ea0ee19ef5a2a42312b6130af37 100644 (file)
@@ -433,6 +433,11 @@ public:
     // is the char a path separator for this format?
     static bool IsPathSeparator(wxChar ch, wxPathFormat format = wxPATH_NATIVE);
 
+    // is this is a DOS path which beings with a windows unique volume name
+    // ('\\?\Volume{guid}\')?
+    static bool IsMSWUniqueVolumeNamePath(const wxString& path,
+                                          wxPathFormat format = wxPATH_NATIVE);
+
     // Dir accessors
     size_t GetDirCount() const { return m_dirs.size(); }
     void AppendDir(const wxString& dir);
index 050141ad6721f8d9b4f0244d2bc8d3b303422841..3f40781ab79894e22b073792223f4435b69e9fef 100644 (file)
@@ -819,6 +819,24 @@ public:
     static bool IsPathSeparator(wxChar ch,
                                 wxPathFormat format = wxPATH_NATIVE);
 
+    /**
+        Returns @true if the volume part of the path is a unique volume name.
+
+        This function will always return @false if the path format is not
+        wxPATH_DOS.
+
+        Unique volume names are Windows volume identifiers which remain the same
+        regardless of where the volume is actually mounted. Example of a path
+        using a volume name could be
+        @code
+            \\?\Volume{8089d7d7-d0ac-11db-9dd0-806d6172696f}\Program Files\setup.exe
+        @endcode
+
+        @since 2.9.1
+    */
+    static bool IsMSWUniqueVolumeNamePath(const wxString& path,
+                                          wxPathFormat format = wxPATH_NATIVE);
+
     /**
         Returns @true if this filename is not absolute.
     */
index 49c5ed0f573c2d26042a1b6d484bcaf017bbf9fb..ce4e9f2d31c45019b3049787a15c5c6ca28ce3a0 100644 (file)
                 drive:\dir1\dir2\...\dirN\filename.ext where drive is a single
                 letter. "." and ".." as for Unix but no "~".
 
-                There are also UNC names of the form \\share\fullpath
+                There are also UNC names of the form \\share\fullpath and
+                MSW unique volume names of the form \\?\Volume{GUID}\fullpath.
+
+                The latter provide a uniform way to access a volume regardless of
+                its current mount point, i.e. you can change a volume's mount
+                point from D: to E:, or even remove it, and still be able to
+                access it through its unique volume name. More on the subject can
+                be found in MSDN's article "Naming a Volume" that is currently at
+                http://msdn.microsoft.com/en-us/library/aa365248(VS.85).aspx.
+
 
    wxPATH_MAC:  Mac OS 8/9 and Mac OS X under CodeWarrior 7 format, absolute file
                 names have the form
@@ -296,7 +305,18 @@ static wxString wxGetVolumeString(const wxString& volume, wxPathFormat format)
         // although I didn't find any authoritative docs on this)
         if ( format == wxPATH_DOS && volume.length() > 1 )
         {
-            path << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << volume;
+            // We also have to check for Windows unique volume names here and
+            // return it with '\\?\' prepended to it
+            if ( wxFileName::IsMSWUniqueVolumeNamePath("\\\\?\\" + volume + "\\",
+                                                       format) )
+            {
+                path << "\\\\?\\" << volume;
+            }
+            else
+            {
+                // it must be a UNC path
+                path << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << volume;
+            }
         }
         else if  ( format == wxPATH_DOS || format == wxPATH_VMS )
         {
@@ -326,6 +346,13 @@ static bool IsUNCPath(const wxString& path, wxPathFormat format)
                             !IsDOSPathSep(path[2u]);
 }
 
+// ----------------------------------------------------------------------------
+// private constants
+// ----------------------------------------------------------------------------
+
+// length of \\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\ string
+static const size_t wxMSWUniqueVolumePrefixLength = 49;
+
 } // anonymous namespace
 
 // ============================================================================
@@ -620,12 +647,17 @@ bool wxFileName::DirExists( const wxString &dirPath )
 #if defined(__WINDOWS__) || defined(__OS2__)
     // Windows fails to find directory named "c:\dir\" even if "c:\dir" exists,
     // so remove all trailing backslashes from the path - but don't do this for
-    // the paths "d:\" (which are different from "d:") nor for just "\"
+    // the paths "d:\" (which are different from "d:"), for just "\" or for
+    // windows unique volume names ("\\?\Volume{GUID}\")
     while ( wxEndsWithPathSeparator(strPath) )
     {
         size_t len = strPath.length();
-        if ( len == 1 || (len == 3 && strPath[len - 2] == wxT(':')) )
+        if ( len == 1 || (len == 3 && strPath[len - 2] == wxT(':')) ||
+            (len == wxMSWUniqueVolumePrefixLength &&
+             wxFileName::IsMSWUniqueVolumeNamePath(strPath)))
+        {
             break;
+        }
 
         strPath.Truncate(len - 1);
     }
@@ -1810,6 +1842,18 @@ bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format)
     return ch != wxT('\0') && GetPathSeparators(format).Find(ch) != wxNOT_FOUND;
 }
 
+/* static */
+bool
+wxFileName::IsMSWUniqueVolumeNamePath(const wxString& path, wxPathFormat format)
+{
+    // return true if the format used is the DOS/Windows one and the string begins
+    // with a Windows unique volume name ("\\?\Volume{guid}\")
+    return format == wxPATH_DOS &&
+            path.length() >= wxMSWUniqueVolumePrefixLength &&
+             path.StartsWith(wxS("\\\\?\\Volume{")) &&
+              path[wxMSWUniqueVolumePrefixLength - 1] == wxFILE_SEP_PATH_DOS;
+}
+
 // ----------------------------------------------------------------------------
 // path components manipulation
 // ----------------------------------------------------------------------------
@@ -2194,9 +2238,26 @@ wxFileName::SplitVolume(const wxString& fullpathWithVolume,
 
     wxString fullpath = fullpathWithVolume;
 
-    // special Windows UNC paths hack: transform \\share\path into share:path
-    if ( IsUNCPath(fullpath, format) )
+    if ( IsMSWUniqueVolumeNamePath(fullpath, format) )
     {
+        // special Windows unique volume names hack: transform
+        // \\?\Volume{guid}\path into Volume{guid}:path
+        // note: this check must be done before the check for UNC path
+
+        // we know the last backslash from the unique volume name is located
+        // there from IsMSWUniqueVolumeNamePath
+        fullpath[wxMSWUniqueVolumePrefixLength - 1] = wxFILE_SEP_DSK;
+
+        // paths starting with a unique volume name should always be absolute
+        fullpath.insert(wxMSWUniqueVolumePrefixLength, 1, wxFILE_SEP_PATH_DOS);
+
+        // remove the leading "\\?\" part
+        fullpath.erase(0, 4);
+    }
+    else if ( IsUNCPath(fullpath, format) )
+    {
+        // special Windows UNC paths hack: transform \\share\path into share:path
+
         fullpath.erase(0, 2);
 
         size_t posFirstSlash =
index 70d3dced98bc513760d4e0b082cbea4b17dc022a..c1a9c5e6d43fedd42c595f8bce49cbc687f5e8a2 100644 (file)
@@ -69,6 +69,10 @@ static struct TestFileNameInfo
     { "c:foo.bar", "c", "", "foo", "bar", false, wxPATH_DOS },
     { "c:\\foo.bar", "c", "\\", "foo", "bar", true, wxPATH_DOS },
     { "c:\\Windows\\command.com", "c", "\\Windows", "command", "com", true, wxPATH_DOS },
+    { "\\\\?\\Volume{8089d7d7-d0ac-11db-9dd0-806d6172696f}\\",
+      "Volume{8089d7d7-d0ac-11db-9dd0-806d6172696f}", "\\", "", "", true, wxPATH_DOS },
+    { "\\\\?\\Volume{8089d7d7-d0ac-11db-9dd0-806d6172696f}\\Program Files\\setup.exe",
+      "Volume{8089d7d7-d0ac-11db-9dd0-806d6172696f}", "\\Program Files", "setup", "exe", true, wxPATH_DOS },
 
 #if 0
     // NB: when using the wxFileName::GetLongPath() function on these two
@@ -125,6 +129,7 @@ private:
         CPPUNIT_TEST( TestShortLongPath );
 #endif // __WINDOWS__
         CPPUNIT_TEST( TestUNC );
+        CPPUNIT_TEST( TestVolumeUniqueName );
     CPPUNIT_TEST_SUITE_END();
 
     void TestConstruction();
@@ -138,6 +143,7 @@ private:
     void TestShortLongPath();
 #endif // __WINDOWS__
     void TestUNC();
+    void TestVolumeUniqueName();
 
     DECLARE_NO_COPY_CLASS(FileNameTestCase)
 };
@@ -507,3 +513,23 @@ void FileNameTestCase::TestUNC()
     CPPUNIT_ASSERT_EQUAL( "\\path2", fn.GetPath(wxPATH_NO_SEPARATOR, wxPATH_DOS) );
 }
 
+void FileNameTestCase::TestVolumeUniqueName()
+{
+    wxFileName fn("\\\\?\\Volume{8089d7d7-d0ac-11db-9dd0-806d6172696f}\\",
+                  wxPATH_DOS);
+    CPPUNIT_ASSERT_EQUAL( "Volume{8089d7d7-d0ac-11db-9dd0-806d6172696f}",
+                          fn.GetVolume() );
+    CPPUNIT_ASSERT_EQUAL( "\\", fn.GetPath(wxPATH_NO_SEPARATOR, wxPATH_DOS) );
+    CPPUNIT_ASSERT_EQUAL( "\\\\?\\Volume{8089d7d7-d0ac-11db-9dd0-806d6172696f}\\",
+                          fn.GetFullPath(wxPATH_DOS) );
+
+    fn.Assign("\\\\?\\Volume{8089d7d7-d0ac-11db-9dd0-806d6172696f}\\"
+              "Program Files\\setup.exe", wxPATH_DOS);
+    CPPUNIT_ASSERT_EQUAL( "Volume{8089d7d7-d0ac-11db-9dd0-806d6172696f}",
+                          fn.GetVolume() );
+    CPPUNIT_ASSERT_EQUAL( "\\Program Files",
+                          fn.GetPath(wxPATH_NO_SEPARATOR, wxPATH_DOS) );
+    CPPUNIT_ASSERT_EQUAL( "\\\\?\\Volume{8089d7d7-d0ac-11db-9dd0-806d6172696f}\\"
+                          "Program Files\\setup.exe",
+                          fn.GetFullPath(wxPATH_DOS) );
+}