Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code Quality: Removed Vanara.PInvoke.Mpr & Vanara.PInvoke.DwmApi #15292

Merged
merged 11 commits into from
May 26, 2024
83 changes: 56 additions & 27 deletions src/Files.App/Data/Models/NetworkConnectionDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
using System.Windows.Forms;
using Vanara.Extensions;
using Vanara.InteropServices;
using static Vanara.PInvoke.Mpr;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.NetworkManagement.WNet;
using Windows.Win32.Security.Credentials;

namespace Files.App.Data.Models
{
Expand All @@ -17,40 +20,46 @@ namespace Files.App.Data.Models
/// </remarks>
public sealed class NetworkConnectionDialog : CommonDialog
{
private readonly NETRESOURCE netRes = new();
private CONNECTDLGSTRUCT dialogOptions;
private NETRESOURCEW netRes = new();
private CONNECTDLGSTRUCTW dialogOptions;

/// <summary>Initializes a new instance of the <see cref="NetworkConnectionDialog"/> class.</summary>
public NetworkConnectionDialog()
{
dialogOptions.cbStructure = (uint)Marshal.SizeOf(typeof(CONNECTDLGSTRUCT));
netRes.dwType = NETRESOURCEType.RESOURCETYPE_DISK;
dialogOptions.cbStructure = (uint)Marshal.SizeOf(typeof(CONNECTDLGSTRUCTW));
netRes.dwType = NET_RESOURCE_TYPE.RESOURCETYPE_DISK;
}

/// <summary>Gets the connected device number. This value is only valid after successfully running the dialog.</summary>
/// <value>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.</value>
[Browsable(false)]
public int ConnectedDeviceNumber => dialogOptions.dwDevNum;
public int ConnectedDeviceNumber => (int)dialogOptions.dwDevNum;

/// <summary>Gets or sets a value indicating whether to hide the check box allowing the user to restore the connection at logon.</summary>
/// <value><c>true</c> if hiding restore connection check box; otherwise, <c>false</c>.</value>
[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);
get => dialogOptions.dwFlags.HasFlag(CONNECTDLGSTRUCT_FLAGS.CONNDLG_HIDE_BOX);
set
{
if (value)
dialogOptions.dwFlags |= CONNECTDLGSTRUCT_FLAGS.CONNDLG_HIDE_BOX;
else
dialogOptions.dwFlags &= ~CONNECTDLGSTRUCT_FLAGS.CONNDLG_HIDE_BOX;
}
}

/// <summary>Gets or sets a value indicating whether restore the connection at logon.</summary>
/// <value><c>true</c> to restore connection at logon; otherwise, <c>false</c>.</value>
[DefaultValue(false), Category("Behavior"), Description("Restore the connection at logon.")]
public bool PersistConnectionAtLogon
{
get => dialogOptions.dwFlags.IsFlagSet(CONN_DLG.CONNDLG_PERSIST);
get => dialogOptions.dwFlags.IsFlagSet(CONNECTDLGSTRUCT_FLAGS.CONNDLG_PERSIST);
set
{
dialogOptions.dwFlags = dialogOptions.dwFlags.SetFlags(CONN_DLG.CONNDLG_PERSIST, value);
dialogOptions.dwFlags = dialogOptions.dwFlags.SetFlags(CONN_DLG.CONNDLG_NOT_PERSIST, !value);
dialogOptions.dwFlags = dialogOptions.dwFlags.SetFlags(CONNECTDLGSTRUCT_FLAGS.CONNDLG_PERSIST, value);
dialogOptions.dwFlags = dialogOptions.dwFlags.SetFlags(CONNECTDLGSTRUCT_FLAGS.CONNDLG_NOT_PERSIST, !value);
}
}

Expand All @@ -65,52 +74,72 @@ public bool PersistConnectionAtLogon
/// <summary>Gets or sets the name of the remote network.</summary>
/// <value>The name of the remote network.</value>
[DefaultValue(null), Category("Behavior"), Description("The value displayed in the path field.")]
public string RemoteNetworkName { get => netRes.lpRemoteName; set => netRes.lpRemoteName = value; }
public string RemoteNetworkName
{
get => netRes.lpRemoteName.ToString();
set
{
unsafe
{
fixed (char* lpcRemoteName = value)
netRes.lpRemoteName = lpcRemoteName;
}
}
}

/// <summary>Gets or sets a value indicating whether to enter the most recently used paths into the combination box.</summary>
/// <value><c>true</c> to use MRU path; otherwise, <c>false</c>.</value>
/// <exception cref="InvalidOperationException">UseMostRecentPath</exception>
[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);
get => dialogOptions.dwFlags.IsFlagSet(CONNECTDLGSTRUCT_FLAGS.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);
dialogOptions.dwFlags = dialogOptions.dwFlags.SetFlags(CONNECTDLGSTRUCT_FLAGS.CONNDLG_USE_MRU, value);
}
}

/// <inheritdoc/>
public override void Reset()
0x5bfa marked this conversation as resolved.
Show resolved Hide resolved
{
dialogOptions.dwDevNum = -1;
dialogOptions.dwFlags = 0;
dialogOptions.lpConnRes = IntPtr.Zero;
ReadOnlyPath = false;
unsafe
{
dialogOptions.dwDevNum = 0;
dialogOptions.dwFlags = 0;
dialogOptions.lpConnRes = null;
ReadOnlyPath = false;
}
}

/// <inheritdoc/>
protected override bool RunDialog(IntPtr hwndOwner)
0x5bfa marked this conversation as resolved.
Show resolved Hide resolved
{
using var lpNetResource = SafeCoTaskMemHandle.CreateFromStructure(netRes);
0x5bfa marked this conversation as resolved.
Show resolved Hide resolved

dialogOptions.hwndOwner = hwndOwner;
dialogOptions.lpConnRes = lpNetResource.DangerousGetHandle();
unsafe
{
dialogOptions.hwndOwner = new(hwndOwner);

fixed (NETRESOURCEW* lpConnRes = &netRes)
dialogOptions.lpConnRes = lpConnRes;

if (ReadOnlyPath && !string.IsNullOrEmpty(netRes.lpRemoteName))
dialogOptions.dwFlags |= CONN_DLG.CONNDLG_RO_PATH;
if (ReadOnlyPath && !string.IsNullOrEmpty(netRes.lpRemoteName.ToString()))
dialogOptions.dwFlags |= CONNECTDLGSTRUCT_FLAGS.CONNDLG_RO_PATH;

var result = WNetConnectionDialog1(dialogOptions);
var result = PInvoke.WNetConnectionDialog1W(ref dialogOptions);

dialogOptions.lpConnRes = IntPtr.Zero;
dialogOptions.lpConnRes = null;

if (result == unchecked((uint)-1))
0x5bfa marked this conversation as resolved.
Show resolved Hide resolved
return false;
if (result == unchecked((uint)-1))
return false;

result.ThrowIfFailed();
if (result == 0)
throw new Win32Exception("Cannot display dialog");
}

return true;
}
Expand Down
4 changes: 1 addition & 3 deletions src/Files.App/Files.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,8 @@
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
<PackageReference Include="TagLibSharp" Version="2.3.0" />
<PackageReference Include="Tulpep.ActiveDirectoryObjectPicker" Version="3.0.11" />
<PackageReference Include="Vanara.PInvoke.DwmApi" Version="4.0.1" />
<PackageReference Include="Vanara.Windows.Extensions" Version="4.0.1" />
<PackageReference Include="WinUIEx" Version="2.3.4" />
<PackageReference Include="Vanara.PInvoke.Mpr" Version="4.0.1" />
<PackageReference Include="Vanara.Windows.Extensions" Version="4.0.1" />
<PackageReference Include="Vanara.Windows.Shell" Version="4.0.1" />
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" />
<PackageReference Include="Microsoft.Management.Infrastructure.Runtime.Win" Version="3.0.0" />
Expand Down
12 changes: 11 additions & 1 deletion src/Files.App/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,14 @@ MoveFileFromApp
DeleteFileFromApp
RemoveDirectoryFromApp
GetKeyState
CreateDirectoryFromApp
CreateDirectoryFromApp
WNetCancelConnection2
NET_USE_CONNECT_FLAGS
NETRESOURCEW
WNetAddConnection3
CREDENTIALW
CredWrite
WNetConnectionDialog1
CONNECTDLGSTRUCTW
DwmSetWindowAttribute
WIN32_ERROR
65 changes: 40 additions & 25 deletions src/Files.App/Services/NetworkDrivesService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@

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;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.NetworkManagement.WNet;
using Windows.Win32.Security.Credentials;

namespace Files.App.Services
{
Expand Down Expand Up @@ -54,11 +53,11 @@ public async IAsyncEnumerable<ILocatableFolder> GetDrivesAsync()
var networkLocations = await Win32Helper.StartSTATask(() =>
{
var locations = new List<ShellLinkItem>();
using (var netHood = new ShellFolder(Shell32.KNOWNFOLDERID.FOLDERID_NetHood))
using (var netHood = new Vanara.Windows.Shell.ShellFolder(Vanara.PInvoke.Shell32.KNOWNFOLDERID.FOLDERID_NetHood))
{
foreach (var item in netHood)
{
if (item is ShellLink link)
if (item is Vanara.Windows.Shell.ShellLink link)
{
locations.Add(ShellFolderExtensions.GetShellLinkItem(link));
}
Expand Down Expand Up @@ -124,7 +123,11 @@ await foreach (ILocatableFolder item in GetDrivesAsync())
/// <inheritdoc/>
public bool DisconnectNetworkDrive(ILocatableFolder drive)
{
return WNetCancelConnection2(drive.Path.TrimEnd('\\'), CONNECT.CONNECT_UPDATE_PROFILE, true).Succeeded;
return
PInvoke.WNetCancelConnection2W(
drive.Path.TrimEnd('\\'),
(uint)NET_USE_CONNECT_FLAGS.CONNECT_UPDATE_PROFILE,
true) is 0u;
0x5bfa marked this conversation as resolved.
Show resolved Hide resolved
}

/// <inheritdoc/>
Expand All @@ -147,39 +150,51 @@ public Task OpenMapNetworkDriveDialogAsync()
/// <inheritdoc/>
public async Task<bool> AuthenticateNetworkShare(string path)
{
var netRes = new NETRESOURCE()
var netRes = new NETRESOURCEW() { dwType = NET_RESOURCE_TYPE.RESOURCETYPE_DISK };

unsafe
{
dwType = NETRESOURCEType.RESOURCETYPE_DISK,
lpRemoteName = path
};
fixed (char* lpcPath = path)
netRes.lpRemoteName = new PWSTR(lpcPath);
}

// If credentials are saved, this will return NO_ERROR
Win32Error connectionError = WNetAddConnection3(HWND.NULL, netRes, null, null, 0);
var res = (WIN32_ERROR)PInvoke.WNetAddConnection3W(new(nint.Zero), netRes, null, null, 0);
0x5bfa marked this conversation as resolved.
Show resolved Hide resolved

if (connectionError == Win32Error.ERROR_LOGON_FAILURE || connectionError == Win32Error.ERROR_ACCESS_DENIED)
if (res == WIN32_ERROR.ERROR_LOGON_FAILURE || res == WIN32_ERROR.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)
res = (WIN32_ERROR)PInvoke.WNetAddConnection3W(new(nint.Zero), netRes, credentialsReturned[1], credentialsReturned[0], 0);
if (credentialsReturned[2] == "y" && res == WIN32_ERROR.NO_ERROR)
{
var creds = new CREDENTIAL
var creds = new CREDENTIALW()
{
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);
unsafe
{
fixed (char* lpcTargetName = path.Substring(2))
creds.TargetName = new(lpcTargetName);

fixed (char* lpcUserName = credentialsReturned[0])
creds.UserName = new(lpcUserName);

byte[] bPassword = Encoding.Unicode.GetBytes(credentialsReturned[1]);
creds.CredentialBlobSize = (uint)bPassword.Length;

fixed (byte* lpCredentialBlob = Encoding.UTF8.GetBytes(credentialsReturned[1]))
0x5bfa marked this conversation as resolved.
Show resolved Hide resolved
creds.CredentialBlob = lpCredentialBlob;
}

PInvoke.CredWrite(creds, 0);
}
}
else
Expand All @@ -188,13 +203,13 @@ public async Task<bool> AuthenticateNetworkShare(string path)
}
}

if (connectionError == Win32Error.NO_ERROR)
if (res == WIN32_ERROR.NO_ERROR)
{
return true;
}
else
{
await DialogDisplayHelper.ShowDialogAsync("NetworkFolderErrorDialogTitle".GetLocalizedResource(), connectionError.ToString().Split(":")[1].Trim());
await DialogDisplayHelper.ShowDialogAsync("NetworkFolderErrorDialogTitle".GetLocalizedResource(), res.ToString());
0x5bfa marked this conversation as resolved.
Show resolved Hide resolved

return false;
}
Expand Down