Via Code auf eine Netzwerkfreigabe zugreifen (Berechtigungen)

Wenn Ihr auf ein Netzwerklaufwerk in Eurem .NET-Programm zugreifen wollt, z.B. um eine Datei zu lesen oder zu schreiben, so ist es wichtig, dass Ihr korrekte Berechtigungen verwendet.

Ihr könntet dann z.B. via Identitätswechsel („Impersonation“) einen Teil des Codes unter einem anderen Benutzerkonto laufen lassen, oder Ihr simuliert einen NET USE in Eurem Programm.

Das könnt Ihr mit dieser Klasse hier machen:

namespace Tools
{
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Runtime.InteropServices;

    /// <summary>
    /// Wrapper für die "NET USE"-Funktionalität.
    /// </summary>
    public class NetworkConnection :
        IDisposable
    {
        private string _networkName;

        public NetworkConnection(
            string networkName,
            bool activate,
            string userName,
            string password)
        {
            if (activate)
            {
                doNetUse(networkName, userName, password);
            }
        }

        private void doNetUse(string networkName, string userName, string password)
        {
            var netResource = new NetResource
            {
                Scope = ResourceScope.GlobalNetwork,
                ResourceType = ResourceType.Disk,
                DisplayType = ResourceDisplaytype.Share,
                RemoteName = networkName
            };

            var result = WNetAddConnection2(
                netResource,
                password,
                userName,
                0);

            if (result != 0)
            {
                throw new IOException(string.Format("Error connecting to remote share '{0}': {1}", networkName, result), result);
            }

            _networkName = networkName;
        }

        ~NetworkConnection()
        {
            doDispose();
        }

        public void Dispose()
        {
            doDispose();
            GC.SuppressFinalize(this);
        }

        private void doDispose()
        {
            if (!string.IsNullOrEmpty(_networkName))
            {
                var result = WNetCancelConnection2(_networkName, 0, true);
                Trace.TraceInformation("Result for canceling network connection: {0}.", result);
            }
        }

        [DllImport(@"mpr.dll")]
        private static extern int WNetAddConnection2(
            NetResource netResource,
            string password,
            string username,
            int flags);

        [DllImport(@"mpr.dll")]
        private static extern int WNetCancelConnection2(
            string name, 
            int flags, 
            bool force);
    }

    [StructLayout(LayoutKind.Sequential)]
    public class NetResource
    {
        public ResourceScope Scope;
        public ResourceType ResourceType;
        public ResourceDisplaytype DisplayType;
        public int Usage;
        public string LocalName;
        public string RemoteName;
        public string Comment;
        public string Provider;
    }

    public enum ResourceScope : int
    {
        Connected = 1,
        GlobalNetwork,
        Remembered,
        Recent,
        Context
    };

    public enum ResourceType : int
    {
        Any = 0,
        Disk = 1,
        Print = 2,
        Reserved = 8,
    }

    public enum ResourceDisplaytype : int
    {
        Generic = 0x0,
        Domain = 0x01,
        Server = 0x02,
        Share = 0x03,
        File = 0x04,
        Group = 0x05,
        Network = 0x06,
        Root = 0x07,
        Shareadmin = 0x08,
        Directory = 0x09,
        Tree = 0x0a,
        Ndscontainer = 0x0b
    }
}

Ihr verwendet diese Klasse dann innerhalb eines using-Blocks z.B. so:

using (new NetworkConnection(
    @"\\myserver\myshare",
    true,
    "myuser",
    "mypassword"))
{
    // ... Dateizugriff ganz normal über File-Klassen ...
}

Innerhalb dieses Blocks wird dann der Zugriff mit den angegebenen Zugangsdaten ausgeführt, am Ende des Blocks wieder zurückgesetzt.

Die Dokumentation der einzelnen Funktionen:

Eine alternative Verwendung der NetworkConnection aus der Praxis:

private static void foo()
{
    using (new NetworkConnection(
        makeShare(destinationFilePath),
        !string.IsNullOrEmpty(userName) && isShare(destinationFilePath),
        userName,
        password))
    {
        // ... Dateizugriff ...
    }
}

mit den beiden Hilfsfunktionen:

private static bool isShare(string filePath)
{
    return filePath.StartsWith(@"\\") && filePath.LastIndexOf('\\') > 1 && 
        !filePath.EndsWith(@"\\");
}

und

private static string makeShare(string filePath)
{
    if (!isShare(filePath))
    {
        return filePath;
    }
    else
    {
        var parts = filePath.Trim('\\').Split('\\');
        return string.Format(@"\\{0}\{1}", parts[0], parts[1]);
    }
}