Add support for MSW unique volume names to wxFileName.

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
This commit is contained in:
Vadim Zeitlin 2009-12-05 17:31:00 +00:00
parent 2450225e87
commit e01a788ee0
5 changed files with 117 additions and 6 deletions

View File

@ -432,6 +432,7 @@ All:
wxVariant. wxVariant.
- wxDateTime timezone functions now dynamic (no caching). - wxDateTime timezone functions now dynamic (no caching).
- Added wxHttp::GetCookie and wxHttp::HasCookies (dodge). - Added wxHttp::GetCookie and wxHttp::HasCookies (dodge).
- Added support for unique volume names to wxFileName (Neno Ganchev).
Unix: Unix:

View File

@ -433,6 +433,11 @@ public:
// is the char a path separator for this format? // is the char a path separator for this format?
static bool IsPathSeparator(wxChar ch, wxPathFormat format = wxPATH_NATIVE); 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 // Dir accessors
size_t GetDirCount() const { return m_dirs.size(); } size_t GetDirCount() const { return m_dirs.size(); }
void AppendDir(const wxString& dir); void AppendDir(const wxString& dir);

View File

@ -819,6 +819,24 @@ public:
static bool IsPathSeparator(wxChar ch, static bool IsPathSeparator(wxChar ch,
wxPathFormat format = wxPATH_NATIVE); 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. Returns @true if this filename is not absolute.
*/ */

View File

@ -22,7 +22,16 @@
drive:\dir1\dir2\...\dirN\filename.ext where drive is a single drive:\dir1\dir2\...\dirN\filename.ext where drive is a single
letter. "." and ".." as for Unix but no "~". 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 wxPATH_MAC: Mac OS 8/9 and Mac OS X under CodeWarrior 7 format, absolute file
names have the form names have the form
@ -296,8 +305,19 @@ static wxString wxGetVolumeString(const wxString& volume, wxPathFormat format)
// although I didn't find any authoritative docs on this) // although I didn't find any authoritative docs on this)
if ( format == wxPATH_DOS && volume.length() > 1 ) if ( format == wxPATH_DOS && volume.length() > 1 )
{ {
// 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; path << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << volume;
} }
}
else if ( format == wxPATH_DOS || format == wxPATH_VMS ) else if ( format == wxPATH_DOS || format == wxPATH_VMS )
{ {
path << volume << wxFileName::GetVolumeSeparator(format); path << volume << wxFileName::GetVolumeSeparator(format);
@ -326,6 +346,13 @@ static bool IsUNCPath(const wxString& path, wxPathFormat format)
!IsDOSPathSep(path[2u]); !IsDOSPathSep(path[2u]);
} }
// ----------------------------------------------------------------------------
// private constants
// ----------------------------------------------------------------------------
// length of \\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\ string
static const size_t wxMSWUniqueVolumePrefixLength = 49;
} // anonymous namespace } // anonymous namespace
// ============================================================================ // ============================================================================
@ -620,12 +647,17 @@ bool wxFileName::DirExists( const wxString &dirPath )
#if defined(__WINDOWS__) || defined(__OS2__) #if defined(__WINDOWS__) || defined(__OS2__)
// Windows fails to find directory named "c:\dir\" even if "c:\dir" exists, // 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 // 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) ) while ( wxEndsWithPathSeparator(strPath) )
{ {
size_t len = strPath.length(); 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; break;
}
strPath.Truncate(len - 1); 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; 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 // path components manipulation
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -2194,9 +2238,26 @@ wxFileName::SplitVolume(const wxString& fullpathWithVolume,
wxString fullpath = fullpathWithVolume; wxString fullpath = fullpathWithVolume;
// special Windows UNC paths hack: transform \\share\path into share:path if ( IsMSWUniqueVolumeNamePath(fullpath, format) )
if ( IsUNCPath(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); fullpath.erase(0, 2);
size_t posFirstSlash = size_t posFirstSlash =

View File

@ -69,6 +69,10 @@ static struct TestFileNameInfo
{ "c:foo.bar", "c", "", "foo", "bar", false, wxPATH_DOS }, { "c:foo.bar", "c", "", "foo", "bar", false, wxPATH_DOS },
{ "c:\\foo.bar", "c", "\\", "foo", "bar", true, wxPATH_DOS }, { "c:\\foo.bar", "c", "\\", "foo", "bar", true, wxPATH_DOS },
{ "c:\\Windows\\command.com", "c", "\\Windows", "command", "com", 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 #if 0
// NB: when using the wxFileName::GetLongPath() function on these two // NB: when using the wxFileName::GetLongPath() function on these two
@ -125,6 +129,7 @@ private:
CPPUNIT_TEST( TestShortLongPath ); CPPUNIT_TEST( TestShortLongPath );
#endif // __WINDOWS__ #endif // __WINDOWS__
CPPUNIT_TEST( TestUNC ); CPPUNIT_TEST( TestUNC );
CPPUNIT_TEST( TestVolumeUniqueName );
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
void TestConstruction(); void TestConstruction();
@ -138,6 +143,7 @@ private:
void TestShortLongPath(); void TestShortLongPath();
#endif // __WINDOWS__ #endif // __WINDOWS__
void TestUNC(); void TestUNC();
void TestVolumeUniqueName();
DECLARE_NO_COPY_CLASS(FileNameTestCase) DECLARE_NO_COPY_CLASS(FileNameTestCase)
}; };
@ -507,3 +513,23 @@ void FileNameTestCase::TestUNC()
CPPUNIT_ASSERT_EQUAL( "\\path2", fn.GetPath(wxPATH_NO_SEPARATOR, wxPATH_DOS) ); 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) );
}