Skip to content

Commit 68b6a2e

Browse files
authored
Added support for formatting drives (2239)
Added support for formatting drives
2 parents 85f1bdc + 17e3fc8 commit 68b6a2e

File tree

10 files changed

+125
-37
lines changed

10 files changed

+125
-37
lines changed

Files.Launcher/Program.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,12 @@ private static async Task parseArguments(AppServiceRequestReceivedEventArgs args
286286
}
287287
break;
288288

289+
case "SetVolumeLabel":
290+
var driveName = (string)args.Request.Message["drivename"];
291+
var newLabel = (string)args.Request.Message["newlabel"];
292+
Win32API.SetVolumeLabel(driveName, newLabel);
293+
break;
294+
289295
case "FileOperation":
290296
await parseFileOperation(args);
291297
break;
@@ -330,7 +336,19 @@ private static object HandleMenuMessage(ValueSet message, Win32API.DisposableDic
330336

331337
case "ExecAndCloseContextMenu":
332338
var cMenuExec = table.GetValue<Win32API.ContextMenu>("MENU");
333-
cMenuExec?.InvokeItem(message.Get("ItemID", -1));
339+
if (message.TryGetValue("ItemID", out var menuId))
340+
{
341+
switch (message.Get("CommandString", (string)null))
342+
{
343+
case "format":
344+
var drivePath = cMenuExec.ItemsPath.First();
345+
Win32API.OpenFormatDriveDialog(drivePath);
346+
break;
347+
default:
348+
cMenuExec?.InvokeItem((int)menuId);
349+
break;
350+
}
351+
}
334352
// The following line is needed to cleanup resources when menu is closed.
335353
// Unfortunately if you uncomment it some menu items will randomly stop working.
336354
// Resource cleanup is currently done on app closing,
@@ -348,7 +366,7 @@ private static Func<string, bool> FilterMenuItems(bool showOpenMenu)
348366
var knownItems = new List<string>() {
349367
"opennew", "openas", "opencontaining", "opennewprocess",
350368
"runas", "runasuser", "pintohome", "PinToStartScreen",
351-
"cut", "copy", "paste", "delete", "properties", "link", "format",
369+
"cut", "copy", "paste", "delete", "properties", "link",
352370
"Windows.ModernShare", "Windows.Share", "setdesktopwallpaper",
353371
Win32API.ExtractStringFromDLL("shell32.dll", 30312), // SendTo menu
354372
Win32API.ExtractStringFromDLL("shell32.dll", 34593), // Add to collection

Files.Launcher/Win32API.cs

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -93,26 +93,6 @@ public static string[] CommandLineToArgs(string commandLine)
9393
}
9494
}
9595

96-
public static void UnlockBitlockerDrive(string drive, string password)
97-
{
98-
try
99-
{
100-
Process process = new Process();
101-
process.StartInfo.UseShellExecute = true;
102-
process.StartInfo.Verb = "runas";
103-
process.StartInfo.FileName = "powershell.exe";
104-
process.StartInfo.CreateNoWindow = true;
105-
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
106-
process.StartInfo.Arguments = $"-command \"$SecureString = ConvertTo-SecureString '{password}' -AsPlainText -Force; Unlock-BitLocker -MountPoint '{drive}' -Password $SecureString\"";
107-
process.Start();
108-
process.WaitForExit(30 * 1000);
109-
}
110-
catch (Win32Exception)
111-
{
112-
// If user cancels UAC
113-
}
114-
}
115-
11696
public static (string icon, bool isCustom) GetFileOverlayIcon(string path)
11797
{
11898
var shfi = new Shell32.SHFILEINFO();
@@ -134,6 +114,47 @@ public static (string icon, bool isCustom) GetFileOverlayIcon(string path)
134114
return (Convert.ToBase64String(bitmapData, 0, bitmapData.Length), isCustom);
135115
}
136116

117+
private static void RunPowershellCommand(string command, bool runAsAdmin)
118+
{
119+
try
120+
{
121+
Process process = new Process();
122+
if (runAsAdmin)
123+
{
124+
process.StartInfo.UseShellExecute = true;
125+
process.StartInfo.Verb = "runas";
126+
}
127+
process.StartInfo.FileName = "powershell.exe";
128+
process.StartInfo.CreateNoWindow = true;
129+
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
130+
process.StartInfo.Arguments = command;
131+
process.Start();
132+
process.WaitForExit(30 * 1000);
133+
}
134+
catch (Win32Exception)
135+
{
136+
// If user cancels UAC
137+
}
138+
}
139+
140+
public static void UnlockBitlockerDrive(string drive, string password)
141+
{
142+
RunPowershellCommand($"-command \"$SecureString = ConvertTo-SecureString '{password}' -AsPlainText -Force; Unlock-BitLocker -MountPoint '{drive}' -Password $SecureString\"", true);
143+
}
144+
145+
public static void OpenFormatDriveDialog(string drive)
146+
{
147+
// format requires elevation
148+
int driveIndex = drive.ToUpperInvariant()[0] - 'A';
149+
RunPowershellCommand($"-command \"$Signature = '[DllImport(\\\"shell32.dll\\\", SetLastError = false)]public static extern uint SHFormatDrive(IntPtr hwnd, uint drive, uint fmtID, uint options);'; $SHFormatDrive = Add-Type -MemberDefinition $Signature -Name \"Win32SHFormatDrive\" -Namespace Win32Functions -PassThru; $SHFormatDrive::SHFormatDrive(0, {driveIndex}, 0xFFFF, 0x0001)\"", true);
150+
}
151+
152+
public static void SetVolumeLabel(string driveName, string newLabel)
153+
{
154+
// rename requires elevation
155+
RunPowershellCommand($"-command \"$Signature = '[DllImport(\\\"kernel32.dll\\\", SetLastError = false)]public static extern bool SetVolumeLabel(string lpRootPathName, string lpVolumeName);'; $SetVolumeLabel = Add-Type -MemberDefinition $Signature -Name \"Win32SetVolumeLabel\" -Namespace Win32Functions -PassThru; $SetVolumeLabel::SetVolumeLabel('{driveName}', '{newLabel}')\"", true);
156+
}
157+
137158
// There is usually no need to define Win32 COM interfaces/P-Invoke methods here.
138159
// The Vanara library contains the definitions for all members of Shell32.dll, User32.dll and more
139160
// The ones below are due to bugs in the current version of the library and can be removed once fixed

Files.Launcher/Win32API_ContextMenu.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,13 @@ public class ContextMenu : Win32ContextMenu, IDisposable
130130
{
131131
private Shell32.IContextMenu cMenu;
132132
private User32.SafeHMENU hMenu;
133+
public List<string> ItemsPath { get; }
133134

134-
public ContextMenu(Shell32.IContextMenu cMenu, User32.SafeHMENU hMenu)
135+
public ContextMenu(Shell32.IContextMenu cMenu, User32.SafeHMENU hMenu, IEnumerable<string> itemsPath)
135136
{
136137
this.cMenu = cMenu;
137138
this.hMenu = hMenu;
139+
this.ItemsPath = itemsPath.ToList();
138140
this.Items = new List<Win32ContextMenuItem>();
139141
}
140142

@@ -199,15 +201,15 @@ public static ContextMenu GetContextMenuForFiles(string[] filePathList, Shell32.
199201
}
200202
}
201203

202-
public static ContextMenu GetContextMenuForFiles(ShellItem[] shellItems, Shell32.CMF flags, Func<string, bool> itemFilter = null)
204+
private static ContextMenu GetContextMenuForFiles(ShellItem[] shellItems, Shell32.CMF flags, Func<string, bool> itemFilter = null)
203205
{
204206
if (shellItems == null || !shellItems.Any())
205207
return null;
206208
using var sf = shellItems.First().Parent; // HP: the items are all in the same folder
207209
Shell32.IContextMenu menu = sf.GetChildrenUIObjects<Shell32.IContextMenu>(null, shellItems);
208210
var hMenu = User32.CreatePopupMenu();
209211
menu.QueryContextMenu(hMenu, 0, 1, 0x7FFF, flags);
210-
var contextMenu = new ContextMenu(menu, hMenu);
212+
var contextMenu = new ContextMenu(menu, hMenu, shellItems.Select(x => x.ParsingName));
211213
ContextMenu.EnumMenuItems(menu, hMenu, contextMenu.Items, itemFilter);
212214
return contextMenu;
213215
}

Files/BaseLayout.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,8 @@ private async void MenuLayoutItem_Click(object sender, RoutedEventArgs e)
385385
await Connection.SendMessageAsync(new ValueSet() {
386386
{ "Arguments", "ExecAndCloseContextMenu" },
387387
{ "Handle", menuHandle },
388-
{ "ItemID", menuItem.ID } });
388+
{ "ItemID", menuItem.ID },
389+
{ "CommandString", menuItem.CommandString }});
389390
}
390391
}
391392
}

Files/Filesystem/DriveItem.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ namespace Files.Filesystem
1717
public class DriveItem : ObservableObject, INavigationControlItem
1818
{
1919
public string Glyph { get; set; }
20-
public string Text { get; set; }
2120
public string Path { get; set; }
2221
public StorageFolder Root { get; set; }
2322
public NavigationControlItemType ItemType { get; set; } = NavigationControlItemType.Drive;
@@ -38,6 +37,13 @@ public DriveType Type
3837
}
3938
}
4039

40+
private string _text;
41+
public string Text
42+
{
43+
get => _text;
44+
set => SetProperty(ref _text, value);
45+
}
46+
4147
private string _spaceText;
4248
public string SpaceText
4349
{
@@ -60,7 +66,21 @@ public DriveItem(StorageFolder root, DriveType type)
6066
CoreApplication.MainView.ExecuteOnUIThreadAsync(() => GetDriveItemProperties());
6167
}
6268

63-
private async Task GetDriveItemProperties()
69+
public async Task Update()
70+
{
71+
try
72+
{
73+
// Delay is needed to apply the new name
74+
var properties = await Root.Properties.RetrievePropertiesAsync(new[] { "System.ItemNameDisplay" })
75+
.AsTask().WithTimeout(TimeSpan.FromSeconds(5));
76+
Text = (string)properties["System.ItemNameDisplay"];
77+
}
78+
catch (NullReferenceException)
79+
{
80+
}
81+
}
82+
83+
private async void GetDriveItemProperties()
6484
{
6585
try
6686
{

Files/UserControls/SidebarControl.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
Padding="0"
7171
AllowDrop="True"
7272
BorderThickness="0.8"
73-
Content="{x:Bind Text}"
73+
Content="{x:Bind Text, Mode=OneWay}"
7474
DataContext="{x:Bind}"
7575
DragEnter="NavigationViewItem_DragEnter"
7676
DragLeave="NavigationViewItem_DragLeave"

Files/UserControls/SidebarControl.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ private async void NavigationViewDriveItem_DragOver(object sender, DragEventArgs
392392
var storageItems = await e.DataView.GetStorageItemsAsync();
393393

394394
if (storageItems.Count == 0 ||
395-
"Unknown".Equals(driveItem.SpaceText, StringComparison.OrdinalIgnoreCase) ||
395+
"DriveCapacityUnknown".GetLocalized().Equals(driveItem.SpaceText, StringComparison.OrdinalIgnoreCase) ||
396396
storageItems.AreItemsAlreadyInFolder(driveItem.Path))
397397
{
398398
e.AcceptedOperation = DataPackageOperation.None;

Files/UserControls/Widgets/DrivesWidget.xaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,14 @@
6767
VerticalAlignment="Stretch"
6868
HorizontalContentAlignment="Stretch"
6969
VerticalContentAlignment="Stretch"
70-
AutomationProperties.Name="{x:Bind Text}"
70+
AutomationProperties.Name="{x:Bind Text, Mode=OneWay}"
7171
Background="{ThemeResource YourHomeCardBackgroundColor}"
7272
BorderThickness="1"
7373
Click="Button_Click"
7474
CornerRadius="4"
7575
Style="{StaticResource ButtonRevealStyle}"
7676
Tag="{x:Bind Path}"
77-
ToolTipService.ToolTip="{x:Bind Text}">
77+
ToolTipService.ToolTip="{x:Bind Text, Mode=OneWay}">
7878
<Grid
7979
Margin="12"
8080
HorizontalAlignment="Stretch"
@@ -98,7 +98,7 @@
9898
x:Name="ItemLocationName"
9999
FontSize="14"
100100
FontWeight="Medium"
101-
Text="{x:Bind Text}"
101+
Text="{x:Bind Text, Mode=OneWay}"
102102
TextWrapping="NoWrap" />
103103
<muxc:ProgressBar Maximum="{x:Bind MaxSpace.GigaBytes, Mode=OneWay}" Value="{x:Bind SpaceUsed.GigaBytes, Mode=OneWay}" />
104104
<TextBlock Text="{x:Bind SpaceText, Mode=OneWay}" ToolTipService.ToolTip="{x:Bind SpaceText, Mode=OneWay}" />

Files/View Models/Properties/DriveProperties.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public override void GetBaseProperties()
2525
ViewModel.DriveItemGlyphSource = Drive.Glyph;
2626
ViewModel.LoadDriveItemGlyph = true;
2727
ViewModel.ItemName = Drive.Text;
28+
ViewModel.OriginalItemName = Drive.Text;
2829
ViewModel.ItemType = Drive.Type.ToString();
2930
}
3031
}

Files/Views/Pages/PropertiesGeneral.xaml.cs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
using Files.Filesystem;
2+
using Files.Helpers;
23
using Files.View_Models.Properties;
34
using Microsoft.Toolkit.Uwp.Helpers;
5+
using System;
46
using System.Threading.Tasks;
57
using Windows.ApplicationModel;
68
using Windows.ApplicationModel.Core;
9+
using Windows.Foundation.Collections;
710

811
namespace Files
912
{
@@ -17,11 +20,33 @@ public PropertiesGeneral()
1720

1821
public async Task SaveChanges(ListedItem item)
1922
{
20-
if (ViewModel.OriginalItemName != null)
23+
if (BaseProperties is DriveProperties)
2124
{
22-
await CoreApplication.MainView.ExecuteOnUIThreadAsync(() => AppInstance.InteractionOperations.RenameFileItem(item,
23-
ViewModel.OriginalItemName,
24-
ViewModel.ItemName));
25+
var drive = (BaseProperties as DriveProperties).Drive;
26+
if (!string.IsNullOrWhiteSpace(ViewModel.ItemName) && ViewModel.OriginalItemName != ViewModel.ItemName)
27+
{
28+
if (AppInstance.FilesystemViewModel != null)
29+
{
30+
await AppInstance.FilesystemViewModel.Connection.SendMessageAsync(new ValueSet() {
31+
{ "Arguments", "SetVolumeLabel" },
32+
{ "drivename", drive.Path },
33+
{ "newlabel", ViewModel.ItemName }});
34+
_ = CoreApplication.MainView.ExecuteOnUIThreadAsync(async () =>
35+
{
36+
await drive.Update();
37+
await AppInstance.FilesystemViewModel.SetWorkingDirectory(drive.Path);
38+
});
39+
}
40+
}
41+
}
42+
else
43+
{
44+
if (!string.IsNullOrWhiteSpace(ViewModel.ItemName) && ViewModel.OriginalItemName != ViewModel.ItemName)
45+
{
46+
await CoreApplication.MainView.ExecuteOnUIThreadAsync(() => AppInstance.InteractionOperations.RenameFileItem(item,
47+
ViewModel.OriginalItemName,
48+
ViewModel.ItemName));
49+
}
2550
}
2651
}
2752
}

0 commit comments

Comments
 (0)