diff --git a/mcs/class/corlib/ReferenceSources/win32native.cs b/mcs/class/corlib/ReferenceSources/win32native.cs index 06b5c9ea478a..a4e4351549e2 100644 --- a/mcs/class/corlib/ReferenceSources/win32native.cs +++ b/mcs/class/corlib/ReferenceSources/win32native.cs @@ -1,12 +1,14 @@ using System; using System.IO; using System.Runtime.InteropServices; +using System.Text; namespace Microsoft.Win32 { static class Win32Native { internal const string ADVAPI32 = "advapi32.dll"; + internal const String KERNEL32 = "kernel32.dll"; // Error codes from WinError.h internal const int ERROR_SUCCESS = 0x0; @@ -79,5 +81,38 @@ internal class WIN32_FIND_DATA internal int dwFileAttributes = 0; internal String cFileName = null; } + + // From WinBase.h + internal const int SEM_FAILCRITICALERRORS = 1; + + [DllImport(KERNEL32, CharSet=CharSet.Auto, SetLastError=true, BestFitMapping=false)] + internal static extern bool GetVolumeInformation(String drive, [Out]StringBuilder volumeName, int volumeNameBufLen, out int volSerialNumber, out int maxFileNameLen, out int fileSystemFlags, [Out]StringBuilder fileSystemName, int fileSystemNameBufLen); + + [DllImport(KERNEL32, CharSet=CharSet.Auto, SetLastError=true, BestFitMapping=false)] + internal static extern bool SetVolumeLabel(String driveLetter, String volumeName); + + [DllImport(KERNEL32, SetLastError=false, EntryPoint="SetErrorMode", ExactSpelling=true)] + private static extern int SetErrorMode_VistaAndOlder(int newMode); + + [DllImport(KERNEL32, SetLastError=true, EntryPoint="SetThreadErrorMode")] + private static extern bool SetErrorMode_Win7AndNewer(int newMode, out int oldMode); + + // RTM versions of Win7 and Windows Server 2008 R2 + private static readonly Version ThreadErrorModeMinOsVersion = new Version(6, 1, 7600); + + // this method uses the thread-safe version of SetErrorMode on Windows 7 / Windows Server 2008 R2 operating systems. + // + internal static int SetErrorMode(int newMode) + { +#if !FEATURE_CORESYSTEM // ARMSTUB + if (Environment.OSVersion.Version >= ThreadErrorModeMinOsVersion) + { + int oldMode; + SetErrorMode_Win7AndNewer(newMode, out oldMode); + return oldMode; + } +#endif + return SetErrorMode_VistaAndOlder(newMode); + } } -} \ No newline at end of file +} diff --git a/mcs/class/corlib/System.IO/DriveInfo.cs b/mcs/class/corlib/System.IO/DriveInfo.cs index a247f55dbdc6..f39594cc3565 100644 --- a/mcs/class/corlib/System.IO/DriveInfo.cs +++ b/mcs/class/corlib/System.IO/DriveInfo.cs @@ -21,6 +21,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // +using Microsoft.Win32; using System; using System.Collections.Generic; using System.Text; @@ -106,7 +107,61 @@ public long TotalSize { } } - [MonoTODO ("Currently get only works on Mono/Unix; set not implemented")] +#if PLATFORM_WINDOWS + // Null is a valid volume label. + public String VolumeLabel { + get { + // NTFS uses a limit of 32 characters for the volume label, + // as of Windows Server 2003. + const int volNameLen = 50; + StringBuilder volumeName = new StringBuilder(volNameLen); + const int fileSystemNameLen = 50; + StringBuilder fileSystemName = new StringBuilder(fileSystemNameLen); + int serialNumber, maxFileNameLen, fileSystemFlags; + + int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); + try { + bool r = Win32Native.GetVolumeInformation(Name, volumeName, volNameLen, out serialNumber, out maxFileNameLen, out fileSystemFlags, fileSystemName, fileSystemNameLen); + if (!r) { + int errorCode = Marshal.GetLastWin32Error(); + // Win9x appears to return ERROR_INVALID_DATA when a + // drive doesn't exist. + if (errorCode == Win32Native.ERROR_INVALID_DATA) + errorCode = Win32Native.ERROR_INVALID_DRIVE; + if (errorCode == Win32Native.ERROR_PATH_NOT_FOUND || errorCode == Win32Native.ERROR_INVALID_DRIVE) + throw new DriveNotFoundException(Name); + if (errorCode != 0) + Marshal.ThrowExceptionForHR(Win32Native.MakeHRFromErrorCode(errorCode)); + } + } + finally { + Win32Native.SetErrorMode(oldMode); + } + return volumeName.ToString(); + } + set { + String demandPath = Name + '.'; + + int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); + try { + bool r = Win32Native.SetVolumeLabel(Name, value); + if (!r) { + int errorCode = Marshal.GetLastWin32Error(); + // Provide better message + if (errorCode == Win32Native.ERROR_ACCESS_DENIED) + throw new UnauthorizedAccessException(Environment.GetResourceString("InvalidOperation_SetVolumeLabelFailed")); + if (errorCode == Win32Native.ERROR_PATH_NOT_FOUND || errorCode == Win32Native.ERROR_INVALID_DRIVE) + throw new DriveNotFoundException(Name); + if (errorCode != 0) + Marshal.ThrowExceptionForHR(Win32Native.MakeHRFromErrorCode(errorCode)); + } + } + finally { + Win32Native.SetErrorMode(oldMode); + } + } + } +#else public string VolumeLabel { get { return path; @@ -115,6 +170,7 @@ public string VolumeLabel { throw new NotImplementedException (); } } +#endif public string DriveFormat { get {