Kategorie szkoleń | Egzaminy | Kontakt
  • 1
  • 0
  • 589

Mam na komputerze dysk sieciowy zmapowany pod literą X. Gdy w Eksploratorze Windows klikam na ten dysk, jestem proszony o podanie nazwy użytkownika i hasła. Po uwierzytelnieniu mogę przeglądać zawartość dysku. Chciałbym uzyskać dostęp do plików z tego dysku programowo w języku C#. Potrzebuję programowo zrealizować uwierzytelnianie przez podanie hasła i nazwy użytkownika oraz np. wyświetlić listę plików z wybranego katalogu. Próbowałem użyć System.IO.Directory.GetFiles(), jednak w tym przypadku ta metoda nie działa.

Michał_Szot
  • Zapytał
  • @ Michał_Szot | 19.02.2015
    • 5
    • 0
    • 2
Zaloguj się aby zadać pytanie
Pokrewne

Odpowiedź (1)

  • 3

Aby zmapować ścieżkę UNC jako dysk, można posłużyć się standardowym kreatorem Windows. Niestety, w wypadku gdy aplikacja C# próbowałaby skorzystać z takiego zasobu, rzeczywiście pojawi się błąd brak dostępu. Częściowym rozwiązaniem może być użycie polecenia NET USE, które daje możliwość mapowania ścieżek i zapamiętania poświadczeń. Wciąż jednak nie jest to rozwiązanie "programowane", nasuwa jednak odpowiedź na Pana pytanie - potrzebne będzie skorzystanie z Windowsowego API.

 

Aby sięgnąć do zdalnego zasobu z użyciem innych poświadczeń, musimy posłużyć się WNetUseConnection. Ważna uwaga - wspomniana wyżej funkcja daje możliwość skorzystania z zasobu bezpośrednio, a nie poprzez mapowany dysk, np. \\nazwakomputera\c$\program files\Folder\plik.txt.

 

Korzystanie z WNetUseConnection to sięganie poza zarządzaną pamięć .NET Framework, dlatego będzie niezbędne skorzystanie z usług InteropServices. 

 

Poniżej przykład kodu, który umożliwi ściągnięcie do takiego zabezpieczonego zasobu. 

 

Najpierw przygotuj dostęp do WNetUseConnection:

 

using System;
using System.Runtime.InteropServices ;
using System.Threading;

namespace ExtremeMirror
{
	public class PinvokeWindowsNetworking
	{
		#region Consts
		const int RESOURCE_CONNECTED = 0x00000001;
		const int RESOURCE_GLOBALNET = 0x00000002;
		const int RESOURCE_REMEMBERED = 0x00000003;

		const int RESOURCETYPE_ANY = 0x00000000;
		const int RESOURCETYPE_DISK = 0x00000001;
		const int RESOURCETYPE_PRINT = 0x00000002;

		const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
		const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
		const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
		const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
		const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
		const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;

		const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
		const int RESOURCEUSAGE_CONTAINER = 0x00000002;


		const int CONNECT_INTERACTIVE = 0x00000008;
		const int CONNECT_PROMPT = 0x00000010;
		const int CONNECT_REDIRECT = 0x00000080;
		const int CONNECT_UPDATE_PROFILE = 0x00000001;
		const int CONNECT_COMMANDLINE = 0x00000800;
		const int CONNECT_CMD_SAVECRED = 0x00001000;

		const int CONNECT_LOCALDRIVE = 0x00000100;
		#endregion

		#region Errors
		const int NO_ERROR = 0;

		const int ERROR_ACCESS_DENIED = 5;
		const int ERROR_ALREADY_ASSIGNED = 85;
		const int ERROR_BAD_DEVICE = 1200;
		const int ERROR_BAD_NET_NAME = 67;
		const int ERROR_BAD_PROVIDER = 1204;
		const int ERROR_CANCELLED = 1223;
		const int ERROR_EXTENDED_ERROR = 1208;
		const int ERROR_INVALID_ADDRESS = 487;
		const int ERROR_INVALID_PARAMETER = 87;
		const int ERROR_INVALID_PASSWORD = 1216;
		const int ERROR_MORE_DATA = 234;
		const int ERROR_NO_MORE_ITEMS = 259;
		const int ERROR_NO_NET_OR_BAD_PATH = 1203;
		const int ERROR_NO_NETWORK = 1222;

		const int ERROR_BAD_PROFILE = 1206;
		const int ERROR_CANNOT_OPEN_PROFILE = 1205;
		const int ERROR_DEVICE_IN_USE = 2404;
		const int ERROR_NOT_CONNECTED = 2250;
		const int ERROR_OPEN_FILES  = 2401;

		private struct ErrorClass 
		{
			public int num;
			public string message;
			public ErrorClass(int num, string message) 
			{
				this.num = num;
				this.message = message;
			}
		}


		// Created with excel formula:
		// ="new ErrorClass("&A1&", """&PROPER(SUBSTITUTE(MID(A1,7,LEN(A1)-6), "_", " "))&"""), "
		private static ErrorClass[] ERROR_LIST = new ErrorClass[] {
			new ErrorClass(ERROR_ACCESS_DENIED, "Error: Access Denied"), 
			new ErrorClass(ERROR_ALREADY_ASSIGNED, "Error: Already Assigned"), 
			new ErrorClass(ERROR_BAD_DEVICE, "Error: Bad Device"), 
			new ErrorClass(ERROR_BAD_NET_NAME, "Error: Bad Net Name"), 
			new ErrorClass(ERROR_BAD_PROVIDER, "Error: Bad Provider"), 
			new ErrorClass(ERROR_CANCELLED, "Error: Cancelled"), 
			new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), 
			new ErrorClass(ERROR_INVALID_ADDRESS, "Error: Invalid Address"), 
			new ErrorClass(ERROR_INVALID_PARAMETER, "Error: Invalid Parameter"), 
			new ErrorClass(ERROR_INVALID_PASSWORD, "Error: Invalid Password"), 
			new ErrorClass(ERROR_MORE_DATA, "Error: More Data"), 
			new ErrorClass(ERROR_NO_MORE_ITEMS, "Error: No More Items"), 
			new ErrorClass(ERROR_NO_NET_OR_BAD_PATH, "Error: No Net Or Bad Path"), 
			new ErrorClass(ERROR_NO_NETWORK, "Error: No Network"), 
			new ErrorClass(ERROR_BAD_PROFILE, "Error: Bad Profile"), 
			new ErrorClass(ERROR_CANNOT_OPEN_PROFILE, "Error: Cannot Open Profile"), 
			new ErrorClass(ERROR_DEVICE_IN_USE, "Error: Device In Use"), 
			new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), 
			new ErrorClass(ERROR_NOT_CONNECTED, "Error: Not Connected"), 
			new ErrorClass(ERROR_OPEN_FILES, "Error: Open Files"), 
		};

		private static string getErrorForNumber(int errNum) 
		{
			foreach (ErrorClass er in ERROR_LIST) 
			{
				if (er.num == errNum) return er.message;
			}
			return "Error: Unknown, " + errNum;
		}
		#endregion

		[DllImport("Mpr.dll")] private static extern int WNetUseConnection(
			IntPtr hwndOwner,
			NETRESOURCE lpNetResource,
			string lpPassword,
			string lpUserID,
			int dwFlags,
			string lpAccessName,
			string lpBufferSize,
			string lpResult
		);

		[DllImport("Mpr.dll")] private static extern int WNetCancelConnection2(
			string lpName,
			int dwFlags,
			bool fForce
		);

		[StructLayout(LayoutKind.Sequential)] private class NETRESOURCE
		{ 
			public int dwScope = 0;
			public int dwType = 0;
			public int dwDisplayType = 0;
			public int dwUsage = 0;
			public string lpLocalName = "";
			public string lpRemoteName = "";
			public string lpComment = "";
			public string lpProvider = "";
		}


		public static string connectToRemote(string remoteUNC, string username, string password) 
		{
			return connectToRemote(remoteUNC, username, password, false);
		}

		public static string connectToRemote(string remoteUNC, string username, string password, bool promptUser) 
		{
			NETRESOURCE nr = new NETRESOURCE();
			nr.dwType = RESOURCETYPE_DISK;
			nr.lpRemoteName = remoteUNC;
			//			nr.lpLocalName = "F:";

			int ret;
			if (promptUser) 
				ret = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
			else 
				ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);

			if (ret == NO_ERROR) return null;
			return getErrorForNumber(ret);
		}

		public static string disconnectRemote(string remoteUNC) 
		{
			int ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false);
			if (ret == NO_ERROR) return null;
			return getErrorForNumber(ret);
		}
	}
}

 

Kolejny krok - przyda nam się wrapper, który usprawni korzystanie z zasobu w "klasyczny" sposób:

 

/// <summary>
/// Provides access to a network share.
/// </summary>
public class NetworkShareAccesser : IDisposable
{
    private string _remoteUncName;
    private string _remoteComputerName;

    public string RemoteComputerName
    {
        get
        {
            return this._remoteComputerName;
        }
        set
        {
            this._remoteComputerName = value;
            this._remoteUncName = @"\\" + this._remoteComputerName;
        }
    }

    public string UserName
    {
        get;
        set;
    }
    public string Password
    {
        get;
        set;
    }

    #region Consts

    private const int RESOURCE_CONNECTED = 0x00000001;
    private const int RESOURCE_GLOBALNET = 0x00000002;
    private const int RESOURCE_REMEMBERED = 0x00000003;

    private const int RESOURCETYPE_ANY = 0x00000000;
    private const int RESOURCETYPE_DISK = 0x00000001;
    private const int RESOURCETYPE_PRINT = 0x00000002;

    private const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
    private const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
    private const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
    private const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
    private const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
    private const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;

    private const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
    private const int RESOURCEUSAGE_CONTAINER = 0x00000002;


    private const int CONNECT_INTERACTIVE = 0x00000008;
    private const int CONNECT_PROMPT = 0x00000010;
    private const int CONNECT_REDIRECT = 0x00000080;
    private const int CONNECT_UPDATE_PROFILE = 0x00000001;
    private const int CONNECT_COMMANDLINE = 0x00000800;
    private const int CONNECT_CMD_SAVECRED = 0x00001000;

    private const int CONNECT_LOCALDRIVE = 0x00000100;

    #endregion

    #region Errors

    private const int NO_ERROR = 0;

    private const int ERROR_ACCESS_DENIED = 5;
    private const int ERROR_ALREADY_ASSIGNED = 85;
    private const int ERROR_BAD_DEVICE = 1200;
    private const int ERROR_BAD_NET_NAME = 67;
    private const int ERROR_BAD_PROVIDER = 1204;
    private const int ERROR_CANCELLED = 1223;
    private const int ERROR_EXTENDED_ERROR = 1208;
    private const int ERROR_INVALID_ADDRESS = 487;
    private const int ERROR_INVALID_PARAMETER = 87;
    private const int ERROR_INVALID_PASSWORD = 1216;
    private const int ERROR_MORE_DATA = 234;
    private const int ERROR_NO_MORE_ITEMS = 259;
    private const int ERROR_NO_NET_OR_BAD_PATH = 1203;
    private const int ERROR_NO_NETWORK = 1222;

    private const int ERROR_BAD_PROFILE = 1206;
    private const int ERROR_CANNOT_OPEN_PROFILE = 1205;
    private const int ERROR_DEVICE_IN_USE = 2404;
    private const int ERROR_NOT_CONNECTED = 2250;
    private const int ERROR_OPEN_FILES = 2401;

    #endregion

    #region PInvoke Signatures

    [DllImport("Mpr.dll")]
    private static extern int WNetUseConnection(
        IntPtr hwndOwner,
        NETRESOURCE lpNetResource,
        string lpPassword,
        string lpUserID,
        int dwFlags,
        string lpAccessName,
        string lpBufferSize,
        string lpResult
        );

    [DllImport("Mpr.dll")]
    private static extern int WNetCancelConnection2(
        string lpName,
        int dwFlags,
        bool fForce
        );

    [StructLayout(LayoutKind.Sequential)]
    private class NETRESOURCE
    {
        public int dwScope = 0;
        public int dwType = 0;
        public int dwDisplayType = 0;
        public int dwUsage = 0;
        public string lpLocalName = "";
        public string lpRemoteName = "";
        public string lpComment = "";
        public string lpProvider = "";
    }

    #endregion

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name. The user will be promted to enter credentials
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <returns></returns>
    public static NetworkShareAccesser Access(string remoteComputerName)
    {
        return new NetworkShareAccesser(remoteComputerName);
    }

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name using the given domain/computer name, username and password
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <param name="domainOrComuterName"></param>
    /// <param name="userName"></param>
    /// <param name="password"></param>
    public static NetworkShareAccesser Access(string remoteComputerName, string domainOrComuterName, string userName, string password)
    {
        return new NetworkShareAccesser(remoteComputerName,
                                        domainOrComuterName + @"\" + userName,
                                        password);
    }

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name using the given username (format: domainOrComputername\Username) and password
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <param name="userName"></param>
    /// <param name="password"></param>
    public static NetworkShareAccesser Access(string remoteComputerName, string userName, string password)
    {
        return new NetworkShareAccesser(remoteComputerName, 
                                        userName,
                                        password);
    }

    private NetworkShareAccesser(string remoteComputerName)
    {
        RemoteComputerName = remoteComputerName;               

        this.ConnectToShare(this._remoteUncName, null, null, true);
    }

    private NetworkShareAccesser(string remoteComputerName, string userName, string password)
    {
        RemoteComputerName = remoteComputerName;
        UserName = userName;
        Password = password;

        this.ConnectToShare(this._remoteUncName, this.UserName, this.Password, false);
    }

    private void ConnectToShare(string remoteUnc, string username, string password, bool promptUser)
    {
        NETRESOURCE nr = new NETRESOURCE
        {
            dwType = RESOURCETYPE_DISK,
            lpRemoteName = remoteUnc
        };

        int result;
        if (promptUser)
        {
            result = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
        }
        else
        {
            result = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);
        }

        if (result != NO_ERROR)
        {
            throw new Win32Exception(result);
        }
    }

    private void DisconnectFromShare(string remoteUnc)
    {
        int result = WNetCancelConnection2(remoteUnc, CONNECT_UPDATE_PROFILE, false);
        if (result != NO_ERROR)
        {
            throw new Win32Exception(result);
        }
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    /// <filterpriority>2</filterpriority>
    public void Dispose()
    {
        this.DisconnectFromShare(this._remoteUncName);
    }
}

 

No i ostatecznie wywołanie wrappera:

 

using (NetworkShareAccesser.Access(REMOTE_COMPUTER_NAME, DOMAIN, USER_NAME, PASSWORD))
{
    File.Copy(@"C:\Some\File\To\copy.txt", @"\\REMOTE-COMPUTER\My\Shared\Target\file.txt");
}

 

Słowo przestrogi: pamiętaj o zniszczeniu obiektu metodą Dispose w klauzuli finally, inaczej połączenie pozostanie aktywne np. gdy aplikacja się niespodziewanie zawiesi.

 

Odpowiedź opracowana na podstawie StackOwerflow

  • Odpowiedział
  • @ | 03.03.2015
  • TRENER ALTKOM AKADEMII