diff --git a/src/Files.App/Data/Contracts/INetworkDrivesService.cs b/src/Files.App/Data/Contracts/INetworkDrivesService.cs
index fc3e2a500847..39aed55b8522 100644
--- a/src/Files.App/Data/Contracts/INetworkDrivesService.cs
+++ b/src/Files.App/Data/Contracts/INetworkDrivesService.cs
@@ -8,11 +8,22 @@ namespace Files.App.Data.Contracts
public interface INetworkDrivesService
{
///
- /// Enumerates network storage devices
+ /// Gets enumerated network storage drives.
+ ///
+ ObservableCollection Drives { get; }
+
+ ///
+ /// Enumerates network storage drives.
///
/// A collection of network storage devices
IAsyncEnumerable GetDrivesAsync();
+ ///
+ /// Updates network storage drives to up-to-date.
+ ///
+ ///
+ Task UpdateDrivesAsync();
+
///
/// Displays the operating system dialog for connecting to a network storage device
///
@@ -25,5 +36,12 @@ public interface INetworkDrivesService
/// An item representing the network storage device to disconnect from
/// True or false to indicate status
bool DisconnectNetworkDrive(ILocatableFolder drive);
+
+ ///
+ /// Authenticates the specified network share point.
+ ///
+ /// A path to the network share point.
+ /// True If succeeds; otherwise, false.
+ Task AuthenticateNetworkShare(string path);
}
}
diff --git a/src/Files.App/Data/Models/ItemViewModel.cs b/src/Files.App/Data/Models/ItemViewModel.cs
index cc53e8114c4a..9e8464e922c4 100644
--- a/src/Files.App/Data/Models/ItemViewModel.cs
+++ b/src/Files.App/Data/Models/ItemViewModel.cs
@@ -49,6 +49,7 @@ public sealed class ItemViewModel : ObservableObject, IDisposable
private readonly IWindowsJumpListService jumpListService = Ioc.Default.GetRequiredService();
private readonly IDialogService dialogService = Ioc.Default.GetRequiredService();
private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService();
+ private readonly INetworkDrivesService NetworkDrivesService = Ioc.Default.GetRequiredService();
private readonly IFileTagsSettingsService fileTagsSettingsService = Ioc.Default.GetRequiredService();
private readonly ISizeProvider folderSizeProvider = Ioc.Default.GetRequiredService();
private readonly IStorageCacheService fileListCache = Ioc.Default.GetRequiredService();
@@ -1506,7 +1507,7 @@ private async Task EnumerateItemsFromStandardFolderAsync(string path, Cance
if (isNetwork)
{
- var auth = await NetworkDrivesAPI.AuthenticateNetworkShare(path);
+ var auth = await NetworkDrivesService.AuthenticateNetworkShare(path);
if (!auth)
return -1;
}
diff --git a/src/Files.App/Data/Models/NetworkConnectionDialog.cs b/src/Files.App/Data/Models/NetworkConnectionDialog.cs
new file mode 100644
index 000000000000..e9d3c35ee553
--- /dev/null
+++ b/src/Files.App/Data/Models/NetworkConnectionDialog.cs
@@ -0,0 +1,118 @@
+// Copyright (c) 2024 Files Community
+// Licensed under the MIT License. See the LICENSE.
+
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using Vanara.Extensions;
+using Vanara.InteropServices;
+using static Vanara.PInvoke.Mpr;
+
+namespace Files.App.Data.Models
+{
+ ///
+ /// A dialog box that allows the user to browse and connect to network resources.
+ ///
+ ///
+ /// Forked from Vanara.
+ ///
+ public sealed class NetworkConnectionDialog : CommonDialog
+ {
+ private readonly NETRESOURCE netRes = new();
+ private CONNECTDLGSTRUCT dialogOptions;
+
+ /// Initializes a new instance of the class.
+ public NetworkConnectionDialog()
+ {
+ dialogOptions.cbStructure = (uint)Marshal.SizeOf(typeof(CONNECTDLGSTRUCT));
+ netRes.dwType = NETRESOURCEType.RESOURCETYPE_DISK;
+ }
+
+ /// Gets the connected device number. This value is only valid after successfully running the dialog.
+ /// The connected device number. The value is 1 for A:, 2 for B:, 3 for C:, and so on. If the user made a deviceless connection, the value is –1.
+ [Browsable(false)]
+ public int ConnectedDeviceNumber => dialogOptions.dwDevNum;
+
+ /// Gets or sets a value indicating whether to hide the check box allowing the user to restore the connection at logon.
+ /// true if hiding restore connection check box; otherwise, false.
+ [DefaultValue(false), Category("Appearance"), Description("Hide the check box allowing the user to restore the connection at logon.")]
+ public bool HideRestoreConnectionCheckBox
+ {
+ get => dialogOptions.dwFlags.IsFlagSet(CONN_DLG.CONNDLG_HIDE_BOX);
+ set => dialogOptions.dwFlags = dialogOptions.dwFlags.SetFlags(CONN_DLG.CONNDLG_HIDE_BOX, value);
+ }
+
+ /// Gets or sets a value indicating whether restore the connection at logon.
+ /// true to restore connection at logon; otherwise, false.
+ [DefaultValue(false), Category("Behavior"), Description("Restore the connection at logon.")]
+ public bool PersistConnectionAtLogon
+ {
+ get => dialogOptions.dwFlags.IsFlagSet(CONN_DLG.CONNDLG_PERSIST);
+ set
+ {
+ dialogOptions.dwFlags = dialogOptions.dwFlags.SetFlags(CONN_DLG.CONNDLG_PERSIST, value);
+ dialogOptions.dwFlags = dialogOptions.dwFlags.SetFlags(CONN_DLG.CONNDLG_NOT_PERSIST, !value);
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether to display a read-only path instead of allowing the user to type in a path. This is only
+ /// valid if is not .
+ ///
+ /// true to display a read only path; otherwise, false.
+ [DefaultValue(false), Category("Appearance"), Description("Display a read-only path instead of allowing the user to type in a path.")]
+ public bool ReadOnlyPath { get; set; }
+
+ /// Gets or sets the name of the remote network.
+ /// The name of the remote network.
+ [DefaultValue(null), Category("Behavior"), Description("The value displayed in the path field.")]
+ public string RemoteNetworkName { get => netRes.lpRemoteName; set => netRes.lpRemoteName = value; }
+
+ /// Gets or sets a value indicating whether to enter the most recently used paths into the combination box.
+ /// true to use MRU path; otherwise, false.
+ /// UseMostRecentPath
+ [DefaultValue(false), Category("Behavior"), Description("Enter the most recently used paths into the combination box.")]
+ public bool UseMostRecentPath
+ {
+ get => dialogOptions.dwFlags.IsFlagSet(CONN_DLG.CONNDLG_USE_MRU);
+ set
+ {
+ if (value && !string.IsNullOrEmpty(RemoteNetworkName))
+ throw new InvalidOperationException($"{nameof(UseMostRecentPath)} cannot be set to true if {nameof(RemoteNetworkName)} has a value.");
+
+ dialogOptions.dwFlags = dialogOptions.dwFlags.SetFlags(CONN_DLG.CONNDLG_USE_MRU, value);
+ }
+ }
+
+ ///
+ public override void Reset()
+ {
+ dialogOptions.dwDevNum = -1;
+ dialogOptions.dwFlags = 0;
+ dialogOptions.lpConnRes = IntPtr.Zero;
+ ReadOnlyPath = false;
+ }
+
+ ///
+ protected override bool RunDialog(IntPtr hwndOwner)
+ {
+ using var lpNetResource = SafeCoTaskMemHandle.CreateFromStructure(netRes);
+
+ dialogOptions.hwndOwner = hwndOwner;
+ dialogOptions.lpConnRes = lpNetResource.DangerousGetHandle();
+
+ if (ReadOnlyPath && !string.IsNullOrEmpty(netRes.lpRemoteName))
+ dialogOptions.dwFlags |= CONN_DLG.CONNDLG_RO_PATH;
+
+ var result = WNetConnectionDialog1(dialogOptions);
+
+ dialogOptions.lpConnRes = IntPtr.Zero;
+
+ if (result == unchecked((uint)-1))
+ return false;
+
+ result.ThrowIfFailed();
+
+ return true;
+ }
+ }
+}
diff --git a/src/Files.App/Data/Models/NetworkDrivesViewModel.cs b/src/Files.App/Data/Models/NetworkDrivesViewModel.cs
deleted file mode 100644
index 30c6e6a99e5e..000000000000
--- a/src/Files.App/Data/Models/NetworkDrivesViewModel.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2024 Files Community
-// Licensed under the MIT License. See the LICENSE.
-
-using Files.App.Data.Items;
-using Files.App.Services;
-using Files.Core.Storage.LocatableStorage;
-
-namespace Files.App.Data.Models
-{
- public sealed class NetworkDrivesViewModel : ObservableObject
- {
- public ObservableCollection Drives
- {
- get => drives;
- private set => SetProperty(ref drives, value);
- }
-
- private ObservableCollection drives;
- private readonly INetworkDrivesService networkDrivesService;
-
- public NetworkDrivesViewModel(INetworkDrivesService networkDrivesService)
- {
- this.networkDrivesService = networkDrivesService;
- drives = [];
-
- var networkItem = new DriveItem
- {
- DeviceID = "network-folder",
- Text = "Network".GetLocalizedResource(),
- Path = Constants.UserEnvironmentPaths.NetworkFolderPath,
- Type = DriveType.Network,
- ItemType = NavigationControlItemType.Drive,
- };
- networkItem.MenuOptions = new ContextMenuOptions
- {
- IsLocationItem = true,
- ShowShellItems = true,
- ShowEjectDevice = networkItem.IsRemovable,
- ShowProperties = true
- };
-
- lock (drives)
- {
- drives.Add(networkItem);
- }
- }
-
- public async Task UpdateDrivesAsync()
- {
- var unsortedDrives = new List
- {
- drives.Single(x => x is DriveItem o && o.DeviceID == "network-folder")
- };
- await foreach (ILocatableFolder item in networkDrivesService.GetDrivesAsync())
- {
- unsortedDrives.Add(item);
- }
-
- var orderedDrives = unsortedDrives.Cast()
- .OrderByDescending(o => o.DeviceID == "network-folder")
- .ThenBy(o => o.Text);
-
- Drives.Clear();
- foreach (ILocatableFolder item in orderedDrives)
- {
- Drives.AddIfNotPresent(item);
- }
- }
-
- public void DisconnectNetworkDrive(ILocatableFolder drive)
- => networkDrivesService.DisconnectNetworkDrive(drive);
-
- public Task OpenMapNetworkDriveDialogAsync()
- => networkDrivesService.OpenMapNetworkDriveDialogAsync();
- }
-}
diff --git a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs
index a1eb4b0203b5..27571fb9c3c5 100644
--- a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs
+++ b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs
@@ -196,7 +196,6 @@ public static IHost ConfigureHost()
.AddSingleton()
.AddSingleton()
.AddSingleton()
- .AddSingleton()
.AddSingleton()
.AddSingleton()
.AddTransient()
diff --git a/src/Files.App/Helpers/Navigation/NavigationHelpers.cs b/src/Files.App/Helpers/Navigation/NavigationHelpers.cs
index b6c369daad1e..e76430b91f90 100644
--- a/src/Files.App/Helpers/Navigation/NavigationHelpers.cs
+++ b/src/Files.App/Helpers/Navigation/NavigationHelpers.cs
@@ -15,7 +15,7 @@ public static class NavigationHelpers
{
private static MainPageViewModel MainPageViewModel { get; } = Ioc.Default.GetRequiredService();
private static DrivesViewModel DrivesViewModel { get; } = Ioc.Default.GetRequiredService();
- private static NetworkDrivesViewModel NetworkDrivesViewModel { get; } = Ioc.Default.GetRequiredService();
+ private static INetworkDrivesService NetworkDrivesService { get; } = Ioc.Default.GetRequiredService();
public static Task OpenPathInNewTab(string? path, bool focusNewTab)
{
@@ -169,7 +169,7 @@ private static async Task UpdateTabInfoAsync(TabBarItem tabItem, object navigati
}
else if (PathNormalization.NormalizePath(PathNormalization.GetPathRoot(currentPath)) == normalizedCurrentPath) // If path is a drive's root
{
- var matchingDrive = NetworkDrivesViewModel.Drives.Cast().FirstOrDefault(netDrive => normalizedCurrentPath.Contains(PathNormalization.NormalizePath(netDrive.Path), StringComparison.OrdinalIgnoreCase));
+ var matchingDrive = NetworkDrivesService.Drives.Cast().FirstOrDefault(netDrive => normalizedCurrentPath.Contains(PathNormalization.NormalizePath(netDrive.Path), StringComparison.OrdinalIgnoreCase));
matchingDrive ??= DrivesViewModel.Drives.Cast().FirstOrDefault(drive => normalizedCurrentPath.Contains(PathNormalization.NormalizePath(drive.Path), StringComparison.OrdinalIgnoreCase));
tabLocationHeader = matchingDrive is not null ? matchingDrive.Text : normalizedCurrentPath;
}
diff --git a/src/Files.App/Services/NetworkDrivesService.cs b/src/Files.App/Services/NetworkDrivesService.cs
index 6fa1ccbca846..00729e557136 100644
--- a/src/Files.App/Services/NetworkDrivesService.cs
+++ b/src/Files.App/Services/NetworkDrivesService.cs
@@ -1,28 +1,62 @@
-// Copyright (c) 2024 Files Community
+// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.
-using Files.App.Utils.Shell;
-using Files.Core.Storage.LocatableStorage;
+using System.Runtime.InteropServices;
+using System.Text;
+using Vanara.InteropServices;
using Vanara.PInvoke;
using Vanara.Windows.Shell;
+using static Vanara.PInvoke.AdvApi32;
+using static Vanara.PInvoke.Mpr;
namespace Files.App.Services
{
- public sealed class NetworkDrivesService : INetworkDrivesService
+ public sealed class NetworkDrivesService : ObservableObject, INetworkDrivesService
{
- public bool DisconnectNetworkDrive(ILocatableFolder drive)
+ private ObservableCollection _Drives;
+ ///
+ public ObservableCollection Drives
{
- return NetworkDrivesAPI.DisconnectNetworkDrive(drive.Path);
+ get => _Drives;
+ private set => SetProperty(ref _Drives, value);
}
+ ///
+ /// Initializes an instance of .
+ ///
+ public NetworkDrivesService()
+ {
+ _Drives = [];
+
+ var networkItem = new DriveItem()
+ {
+ DeviceID = "network-folder",
+ Text = "Network".GetLocalizedResource(),
+ Path = Constants.UserEnvironmentPaths.NetworkFolderPath,
+ Type = DriveType.Network,
+ ItemType = NavigationControlItemType.Drive,
+ };
+
+ networkItem.MenuOptions = new ContextMenuOptions()
+ {
+ IsLocationItem = true,
+ ShowEjectDevice = networkItem.IsRemovable,
+ ShowShellItems = true,
+ ShowProperties = true,
+ };
+ lock (_Drives)
+ _Drives.Add(networkItem);
+ }
+
+ ///
public async IAsyncEnumerable GetDrivesAsync()
{
var networkLocations = await Win32Helper.StartSTATask(() =>
{
var locations = new List();
- using (var nethood = new ShellFolder(Shell32.KNOWNFOLDERID.FOLDERID_NetHood))
+ using (var netHood = new ShellFolder(Shell32.KNOWNFOLDERID.FOLDERID_NetHood))
{
- foreach (var item in nethood)
+ foreach (var item in netHood)
{
if (item is ShellLink link)
{
@@ -30,44 +64,140 @@ public async IAsyncEnumerable GetDrivesAsync()
}
else
{
- var linkPath = (string)item.Properties["System.Link.TargetParsingPath"];
+ var linkPath = (string?)item?.Properties["System.Link.TargetParsingPath"];
if (linkPath is not null)
{
var linkItem = ShellFolderExtensions.GetShellFileItem(item);
- locations.Add(new ShellLinkItem(linkItem) { TargetPath = linkPath });
+ locations.Add(new(linkItem) { TargetPath = linkPath });
}
}
}
}
+
return locations;
});
foreach (var item in networkLocations ?? Enumerable.Empty())
{
- var networkItem = new DriveItem
+ var networkItem = new DriveItem()
{
- Text = System.IO.Path.GetFileNameWithoutExtension(item.FileName),
+ Text = SystemIO.Path.GetFileNameWithoutExtension(item.FileName),
Path = item.TargetPath,
DeviceID = item.FilePath,
Type = DriveType.Network,
ItemType = NavigationControlItemType.Drive,
};
- networkItem.MenuOptions = new ContextMenuOptions
+
+ networkItem.MenuOptions = new ContextMenuOptions()
{
IsLocationItem = true,
ShowEjectDevice = networkItem.IsRemovable,
ShowShellItems = true,
ShowProperties = true,
};
-
yield return networkItem;
}
}
+ ///
+ public async Task UpdateDrivesAsync()
+ {
+ var unsortedDrives = new List()
+ {
+ _Drives.Single(x => x is DriveItem o && o.DeviceID == "network-folder")
+ };
+
+ await foreach (ILocatableFolder item in GetDrivesAsync())
+ unsortedDrives.Add(item);
+
+ var orderedDrives =
+ unsortedDrives.Cast()
+ .OrderByDescending(o => o.DeviceID == "network-folder")
+ .ThenBy(o => o.Text);
+
+ Drives.Clear();
+
+ foreach (ILocatableFolder item in orderedDrives)
+ Drives.AddIfNotPresent(item);
+ }
+
+ ///
+ public bool DisconnectNetworkDrive(ILocatableFolder drive)
+ {
+ return WNetCancelConnection2(drive.Path.TrimEnd('\\'), CONNECT.CONNECT_UPDATE_PROFILE, true).Succeeded;
+ }
+
+ ///
public Task OpenMapNetworkDriveDialogAsync()
{
- var handle = MainWindow.Instance.WindowHandle.ToInt64();
- return NetworkDrivesAPI.OpenMapNetworkDriveDialog(handle);
+ var hWnd = MainWindow.Instance.WindowHandle.ToInt64();
+
+ return Win32Helper.StartSTATask(() =>
+ {
+ using var ncd = new NetworkConnectionDialog
+ {
+ UseMostRecentPath = true,
+ HideRestoreConnectionCheckBox = false
+ };
+
+ return ncd.ShowDialog(Win32Helper.Win32Window.FromLong(hWnd)) == System.Windows.Forms.DialogResult.OK;
+ });
+ }
+
+ ///
+ public async Task AuthenticateNetworkShare(string path)
+ {
+ var netRes = new NETRESOURCE()
+ {
+ dwType = NETRESOURCEType.RESOURCETYPE_DISK,
+ lpRemoteName = path
+ };
+
+ // If credentials are saved, this will return NO_ERROR
+ Win32Error connectionError = WNetAddConnection3(HWND.NULL, netRes, null, null, 0);
+
+ if (connectionError == Win32Error.ERROR_LOGON_FAILURE || connectionError == Win32Error.ERROR_ACCESS_DENIED)
+ {
+ var dialog = DynamicDialogFactory.GetFor_CredentialEntryDialog(path);
+ await dialog.ShowAsync();
+ var credentialsReturned = dialog.ViewModel.AdditionalData as string[];
+
+ if (credentialsReturned is not null && credentialsReturned[1] != null)
+ {
+ connectionError = WNetAddConnection3(HWND.NULL, netRes, credentialsReturned[1], credentialsReturned[0], 0);
+ if (credentialsReturned[2] == "y" && connectionError == Win32Error.NO_ERROR)
+ {
+ var creds = new CREDENTIAL
+ {
+ TargetName = new StrPtrAuto(path.Substring(2)),
+ UserName = new StrPtrAuto(credentialsReturned[0]),
+ Type = CRED_TYPE.CRED_TYPE_DOMAIN_PASSWORD,
+ AttributeCount = 0,
+ Persist = CRED_PERSIST.CRED_PERSIST_ENTERPRISE
+ };
+
+ byte[] bPassword = Encoding.Unicode.GetBytes(credentialsReturned[1]);
+ creds.CredentialBlobSize = (uint)bPassword.Length;
+ creds.CredentialBlob = Marshal.StringToCoTaskMemUni(credentialsReturned[1]);
+ CredWrite(creds, 0);
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ if (connectionError == Win32Error.NO_ERROR)
+ {
+ return true;
+ }
+ else
+ {
+ await DialogDisplayHelper.ShowDialogAsync("NetworkFolderErrorDialogTitle".GetLocalizedResource(), connectionError.ToString().Split(":")[1].Trim());
+
+ return false;
+ }
}
}
}
diff --git a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs
index 56cce2566a8f..c5e5227e2f99 100644
--- a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs
+++ b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs
@@ -22,8 +22,8 @@ public sealed partial class DrivesWidget : BaseWidgetViewModel, IWidgetViewModel
public IUserSettingsService userSettingsService { get; } = Ioc.Default.GetRequiredService();
private IHomePageContext HomePageContext { get; } = Ioc.Default.GetRequiredService();
- private DrivesViewModel drivesViewModel = Ioc.Default.GetRequiredService();
- private NetworkDrivesViewModel networkDrivesViewModel = Ioc.Default.GetRequiredService();
+ private readonly DrivesViewModel drivesViewModel = Ioc.Default.GetRequiredService();
+ private readonly INetworkDrivesService NetworkDrivesService = Ioc.Default.GetRequiredService();
public delegate void DrivesWidgetInvokedEventHandler(object sender, DrivesWidgetInvokedEventArgs e);
public event DrivesWidgetInvokedEventHandler DrivesWidgetInvoked;
@@ -222,7 +222,7 @@ public override List GetItemMenuItems(WidgetCard
private Task DoNetworkMapDriveAsync()
{
- return networkDrivesViewModel.OpenMapNetworkDriveDialogAsync();
+ return NetworkDrivesService.OpenMapNetworkDriveDialogAsync();
}
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
@@ -314,7 +314,7 @@ private void MenuFlyout_Opening(object sender, object e)
private void DisconnectNetworkDrive(WidgetDriveCardItem item)
{
- networkDrivesViewModel.DisconnectNetworkDrive(item.Item);
+ NetworkDrivesService.DisconnectNetworkDrive(item.Item);
}
private void GoToStorageSense_Click(object sender, RoutedEventArgs e)
diff --git a/src/Files.App/Utils/Global/NetworkDrivesAPI.cs b/src/Files.App/Utils/Global/NetworkDrivesAPI.cs
deleted file mode 100644
index d391e30a33b0..000000000000
--- a/src/Files.App/Utils/Global/NetworkDrivesAPI.cs
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright (c) 2024 Files Community
-// Licensed under the MIT License. See the LICENSE.
-
-using Files.App.Extensions;
-using Files.App.Helpers;
-using Files.App.Utils.Shell;
-using System;
-using System.ComponentModel;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-using Vanara.Extensions;
-using Vanara.InteropServices;
-using static Vanara.PInvoke.AdvApi32;
-using Vanara.PInvoke;
-using static Vanara.PInvoke.Mpr;
-
-namespace Files.App.Utils
-{
- public sealed class NetworkDrivesAPI
- {
- ///
- /// A dialog box that allows the user to browse and connect to network resources.
- ///
- public sealed class NetworkConnectionDialog : CommonDialog
- {
- private readonly NETRESOURCE nres = new NETRESOURCE();
- private CONNECTDLGSTRUCT opts;
-
- /// Initializes a new instance of the class.
- public NetworkConnectionDialog()
- {
- opts.cbStructure = (uint)Marshal.SizeOf(typeof(CONNECTDLGSTRUCT));
- nres.dwType = NETRESOURCEType.RESOURCETYPE_DISK;
- }
-
- /// Gets the connected device number. This value is only valid after successfully running the dialog.
- /// The connected device number. The value is 1 for A:, 2 for B:, 3 for C:, and so on. If the user made a deviceless connection, the value is –1.
- [Browsable(false)]
- public int ConnectedDeviceNumber => opts.dwDevNum;
-
- /// Gets or sets a value indicating whether to hide the check box allowing the user to restore the connection at logon.
- /// true if hiding restore connection check box; otherwise, false.
- [DefaultValue(false), Category("Appearance"), Description("Hide the check box allowing the user to restore the connection at logon.")]
- public bool HideRestoreConnectionCheckBox
- {
- get => opts.dwFlags.IsFlagSet(CONN_DLG.CONNDLG_HIDE_BOX);
- set => opts.dwFlags = opts.dwFlags.SetFlags(CONN_DLG.CONNDLG_HIDE_BOX, value);
- }
-
- /// Gets or sets a value indicating whether restore the connection at logon.
- /// true to restore connection at logon; otherwise, false.
- [DefaultValue(false), Category("Behavior"), Description("Restore the connection at logon.")]
- public bool PersistConnectionAtLogon
- {
- get => opts.dwFlags.IsFlagSet(CONN_DLG.CONNDLG_PERSIST);
- set
- {
- opts.dwFlags = opts.dwFlags.SetFlags(CONN_DLG.CONNDLG_PERSIST, value);
- opts.dwFlags = opts.dwFlags.SetFlags(CONN_DLG.CONNDLG_NOT_PERSIST, !value);
- }
- }
-
- ///
- /// Gets or sets a value indicating whether to display a read-only path instead of allowing the user to type in a path. This is only
- /// valid if is not .
- ///
- /// true to display a read only path; otherwise, false.
- [DefaultValue(false), Category("Appearance"), Description("Display a read-only path instead of allowing the user to type in a path.")]
- public bool ReadOnlyPath { get; set; }
-
- /// Gets or sets the name of the remote network.
- /// The name of the remote network.
- [DefaultValue(null), Category("Behavior"), Description("The value displayed in the path field.")]
- public string RemoteNetworkName { get => nres.lpRemoteName; set => nres.lpRemoteName = value; }
-
- /// Gets or sets a value indicating whether to enter the most recently used paths into the combination box.
- /// true to use MRU path; otherwise, false.
- /// UseMostRecentPath
- [DefaultValue(false), Category("Behavior"), Description("Enter the most recently used paths into the combination box.")]
- public bool UseMostRecentPath
- {
- get => opts.dwFlags.IsFlagSet(CONN_DLG.CONNDLG_USE_MRU);
- set
- {
- if (value && !string.IsNullOrEmpty(RemoteNetworkName))
- throw new InvalidOperationException($"{nameof(UseMostRecentPath)} cannot be set to true if {nameof(RemoteNetworkName)} has a value.");
- opts.dwFlags = opts.dwFlags.SetFlags(CONN_DLG.CONNDLG_USE_MRU, value);
- }
- }
-
- ///
- public override void Reset()
- {
- opts.dwDevNum = -1;
- opts.dwFlags = 0;
- opts.lpConnRes = IntPtr.Zero;
- ReadOnlyPath = false;
- }
-
- ///
- protected override bool RunDialog(IntPtr hwndOwner)
- {
- using var lpnres = SafeCoTaskMemHandle.CreateFromStructure(nres);
- opts.hwndOwner = hwndOwner;
- opts.lpConnRes = lpnres.DangerousGetHandle();
- if (ReadOnlyPath && !string.IsNullOrEmpty(nres.lpRemoteName))
- opts.dwFlags |= CONN_DLG.CONNDLG_RO_PATH;
- var ret = WNetConnectionDialog1(opts);
- opts.lpConnRes = IntPtr.Zero;
- if (ret == unchecked((uint)-1)) return false;
- ret.ThrowIfFailed();
- return true;
- }
- }
-
- public static Task OpenMapNetworkDriveDialog(long hwnd)
- {
- return Win32Helper.StartSTATask(() =>
- {
- using var ncd = new NetworkConnectionDialog { UseMostRecentPath = true };
- ncd.HideRestoreConnectionCheckBox = false;
- return ncd.ShowDialog(Win32Helper.Win32Window.FromLong(hwnd)) == System.Windows.Forms.DialogResult.OK;
- });
- }
-
- public static async Task AuthenticateNetworkShare(string path)
- {
- NETRESOURCE nr = new NETRESOURCE
- {
- dwType = NETRESOURCEType.RESOURCETYPE_DISK,
- lpRemoteName = path
- };
-
- Win32Error connectionError = WNetAddConnection3(HWND.NULL, nr, null, null, 0); // if creds are saved, this will return NO_ERROR
-
- if (connectionError == Win32Error.ERROR_LOGON_FAILURE || connectionError == Win32Error.ERROR_ACCESS_DENIED)
- {
- var dialog = DynamicDialogFactory.GetFor_CredentialEntryDialog(path);
- await dialog.ShowAsync();
- var credentialsReturned = dialog.ViewModel.AdditionalData as string[];
-
- if (credentialsReturned is string[] && credentialsReturned[1] != null)
- {
- connectionError = WNetAddConnection3(HWND.NULL, nr, credentialsReturned[1], credentialsReturned[0], 0);
- if (credentialsReturned[2] == "y" && connectionError == Win32Error.NO_ERROR)
- {
- CREDENTIAL creds = new CREDENTIAL();
- creds.TargetName = new StrPtrAuto(path.Substring(2));
- creds.UserName = new StrPtrAuto(credentialsReturned[0]);
- creds.Type = CRED_TYPE.CRED_TYPE_DOMAIN_PASSWORD;
- creds.AttributeCount = 0;
- creds.Persist = CRED_PERSIST.CRED_PERSIST_ENTERPRISE;
- byte[] bpassword = Encoding.Unicode.GetBytes(credentialsReturned[1]);
- creds.CredentialBlobSize = (UInt32)bpassword.Length;
- creds.CredentialBlob = Marshal.StringToCoTaskMemUni(credentialsReturned[1]);
- CredWrite(creds, 0);
- }
- }
- else
- return false;
- }
-
- if (connectionError == Win32Error.NO_ERROR)
- return true;
- else
- {
- await DialogDisplayHelper.ShowDialogAsync("NetworkFolderErrorDialogTitle".GetLocalizedResource(), connectionError.ToString().Split(":")[1].Trim());
- return false;
- }
- }
-
- public static bool DisconnectNetworkDrive(string drive)
- {
- return WNetCancelConnection2(drive.TrimEnd('\\'), CONNECT.CONNECT_UPDATE_PROFILE, true).Succeeded;
- }
- }
-}
diff --git a/src/Files.App/ViewModels/MainPageViewModel.cs b/src/Files.App/ViewModels/MainPageViewModel.cs
index 6277edaab594..6f15460f5704 100644
--- a/src/Files.App/ViewModels/MainPageViewModel.cs
+++ b/src/Files.App/ViewModels/MainPageViewModel.cs
@@ -16,7 +16,7 @@ public sealed class MainPageViewModel : ObservableObject
// Dependency injections
private IAppearanceSettingsService AppearanceSettingsService { get; } = Ioc.Default.GetRequiredService();
- private NetworkDrivesViewModel NetworkDrivesViewModel { get; } = Ioc.Default.GetRequiredService();
+ private INetworkDrivesService NetworkDrivesService { get; } = Ioc.Default.GetRequiredService();
private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService();
private IResourcesService ResourcesService { get; } = Ioc.Default.GetRequiredService();
private DrivesViewModel DrivesViewModel { get; } = Ioc.Default.GetRequiredService();
@@ -179,7 +179,7 @@ UserSettingsService.GeneralSettingsService.LastSessionTabList is not null &&
await Task.WhenAll(
DrivesViewModel.UpdateDrivesAsync(),
- NetworkDrivesViewModel.UpdateDrivesAsync());
+ NetworkDrivesService.UpdateDrivesAsync());
}
// Command methods
diff --git a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs
index 53e42b94ced2..ac792a8b34b9 100644
--- a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs
+++ b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs
@@ -24,13 +24,12 @@ namespace Files.App.ViewModels.UserControls
{
public sealed class SidebarViewModel : ObservableObject, IDisposable, ISidebarViewModel
{
+ private INetworkDrivesService NetworkDrivesService { get; } = Ioc.Default.GetRequiredService();
private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService();
private ICommandManager Commands { get; } = Ioc.Default.GetRequiredService();
private readonly DrivesViewModel drivesViewModel = Ioc.Default.GetRequiredService();
private readonly IFileTagsService fileTagsService;
- private readonly NetworkDrivesViewModel networkDrivesViewModel = Ioc.Default.GetRequiredService();
-
private IPaneHolder paneHolder;
public IPaneHolder PaneHolder
{
@@ -250,7 +249,7 @@ public SidebarViewModel()
App.LibraryManager.DataChanged += Manager_DataChanged;
drivesViewModel.Drives.CollectionChanged += (x, args) => Manager_DataChanged(SectionType.Drives, args);
CloudDrivesManager.DataChanged += Manager_DataChanged;
- networkDrivesViewModel.Drives.CollectionChanged += (x, args) => Manager_DataChanged(SectionType.Network, args);
+ NetworkDrivesService.Drives.CollectionChanged += (x, args) => Manager_DataChanged(SectionType.Network, args);
WSLDistroManager.DataChanged += Manager_DataChanged;
App.FileTagsManager.DataChanged += Manager_DataChanged;
SidebarDisplayMode = UserSettingsService.AppearanceSettingsService.IsSidebarOpen ? SidebarDisplayMode.Expanded : SidebarDisplayMode.Compact;
@@ -283,7 +282,7 @@ await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
SectionType.Pinned => App.QuickAccessManager.Model.PinnedFolderItems,
SectionType.CloudDrives => CloudDrivesManager.Drives,
SectionType.Drives => drivesViewModel.Drives.Cast().ToList().AsReadOnly(),
- SectionType.Network => networkDrivesViewModel.Drives.Cast().ToList().AsReadOnly(),
+ SectionType.Network => NetworkDrivesService.Drives.Cast().ToList().AsReadOnly(),
SectionType.WSL => WSLDistroManager.Distros,
SectionType.Library => App.LibraryManager.Libraries,
SectionType.FileTag => App.FileTagsManager.FileTags,
@@ -578,7 +577,7 @@ public async Task UpdateSectionVisibilityAsync(SectionType sectionType, bool sho
{
SectionType.CloudDrives when generalSettingsService.ShowCloudDrivesSection => CloudDrivesManager.UpdateDrivesAsync,
SectionType.Drives => drivesViewModel.UpdateDrivesAsync,
- SectionType.Network when generalSettingsService.ShowNetworkDrivesSection => networkDrivesViewModel.UpdateDrivesAsync,
+ SectionType.Network when generalSettingsService.ShowNetworkDrivesSection => NetworkDrivesService.UpdateDrivesAsync,
SectionType.WSL when generalSettingsService.ShowWslSection => WSLDistroManager.UpdateDrivesAsync,
SectionType.FileTag when generalSettingsService.ShowFileTagsSection => App.FileTagsManager.UpdateFileTagsAsync,
SectionType.Library => App.LibraryManager.UpdateLibrariesAsync,
@@ -645,7 +644,7 @@ public void Dispose()
App.LibraryManager.DataChanged -= Manager_DataChanged;
drivesViewModel.Drives.CollectionChanged -= (x, args) => Manager_DataChanged(SectionType.Drives, args);
CloudDrivesManager.DataChanged -= Manager_DataChanged;
- networkDrivesViewModel.Drives.CollectionChanged -= (x, args) => Manager_DataChanged(SectionType.Network, args);
+ NetworkDrivesService.Drives.CollectionChanged -= (x, args) => Manager_DataChanged(SectionType.Network, args);
WSLDistroManager.DataChanged -= Manager_DataChanged;
App.FileTagsManager.DataChanged -= Manager_DataChanged;
}