I am writing a program to map a network drive (WebDAV) from a graphical UI (Windows Forms).
I am using the Windows Networking API that lives in the mpr.dll.
Now, when the user enters a wrong password WNetAddConnection2 returns the NotAuthenticated result. So far so good. I throw a custom exception in my wrapper in this case. When the user then goes ahead and enters a correct password I still get a NotAuthenticated. Why is this?
Edit: Also interesting: If I set a breakpoint to the second try statement and let the debugger sit there for about 30 seconds the mapping works after I continue execution.
Edit 2: The problem only occurs under Windows 7 and 10
Here is some code to illustrate what I am doing
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace WNetStackOverflow
{
class Program
{
static void Main(string[] args)
{
char driveLetter = 'Z';
string userName = "user";
string password = "passwor";
try
{
Console.WriteLine("First attempt (wrong pass)");
WNetWrapper.CreateNewNetDrive(driveLetter, userName, password);
}
catch (WNetException e)
{
Console.WriteLine($"First attempt failed: {e.Error}");
Console.WriteLine(e);
}
try
{
Console.WriteLine("Second attempt (correct pass)");
WNetWrapper.CreateNewNetDrive(driveLetter, userName, password + "d");
Console.WriteLine("Success!");
}
catch (WNetException e)
{
Console.WriteLine($"Second attempt failed: {e.Error}");
Console.WriteLine(e);
}
Console.ReadKey();
}
}
internal static class WNetWrapper
{
[DllImport("mpr.dll")]
private static extern WNetError WNetAddConnection2(
NetResource netResource,
string password,
string username,
uint flags);
[DllImport("mpr.dll")]
public static extern int WNetGetLastError(
ref int lpError,
StringBuilder lpErrorBuf,
int nErrorBufSize,
StringBuilder lpNameBuf,
int nNameBufSize);
const string WebDavUri = "yourDomain.com";
public static void CreateNewNetDrive(char driveLetter, string userName, string password)
{
var netResource = new NetResource
{
dwScope = ResourceScope.Globalnet,
dwDisplayType = ResourceDisplayType.Share,
dwType = ResourceType.Disk,
lpRemoteName = $"https://{userName}.{WebDavUri}",
lpLocalName = driveLetter + ":"
};
var returnCode = WNetAddConnection2(netResource, password, userName, 1);
if (returnCode != WNetError.NoError)
{
var errorInformation = GetErrorInformation(returnCode);
errorInformation.Add("DriveLetter", driveLetter.ToString());
errorInformation.Add("UserName", userName);
throw new WNetException("Failed to create net drive", returnCode, errorInformation);
}
}
private static IDictionary GetErrorInformation(WNetError wNetError)
{
var data = new Dictionary<string, object>
{
{"ErrorCode", $"{wNetError} (ErrorCode {(int) wNetError})"}
};
var sbErrorBuf = new StringBuilder(500);
var sbNameBuf = new StringBuilder(500);
var errorNumber = 0;
WNetGetLastError(ref errorNumber, sbErrorBuf, sbErrorBuf.Capacity, sbNameBuf, sbNameBuf.Capacity);
if (errorNumber != 0)
{
data.Add("WNetLastErrorNumber", errorNumber);
data.Add("WNetLastErrorMessage", sbErrorBuf);
}
Console.WriteLine($"ErrorNumber {errorNumber}, ErrorMessage {sbErrorBuf}");
return data;
}
}
internal enum WNetError
{
NoError = 0,
BadNetName = 67,
AlreadyAssigned = 85,
NoMoreItems = 259,
BadDevice = 1200,
NotAuthenticated = 1244
}
internal class WNetException : Exception
{
public WNetError Error { get; }
public WNetException(string message, WNetError error, IDictionary data) : base(message)
{
Error = error;
foreach (var key in data.Keys)
{
Data[key] = data[key];
}
}
}
internal enum ResourceScope
{
Connected = 1,
Globalnet,
Remembered,
Recent,
Context
}
internal enum ResourceType
{
Any,
Disk,
Print,
Reserved
}
[StructLayout(LayoutKind.Sequential)]
internal class NetResource
{
public ResourceScope dwScope = 0;
public ResourceType dwType = 0;
public ResourceDisplayType dwDisplayType = 0;
public ResourceUsage dwUsage = 0;
public string lpLocalName;
public string lpRemoteName;
public string lpComment;
public string lpProvider;
}
public enum ResourceDisplayType
{
Generic,
Domain,
Server,
Share,
File,
Group,
Network,
Root,
Shareadmin,
Directory,
Tree,
Ndscontainer
}
[Flags]
public enum ResourceUsage
{
Connectable = 0x00000001,
Container = 0x00000002,
Nolocaldevice = 0x00000004,
Sibling = 0x00000008,
Attached = 0x00000010,
All = Connectable | Container | Attached
}
}