Von C# aus auf ein Netzwerklaufwerk (SMB) verbinden

Manchmal muss man in einem C#-Projekt einen bestimmten Code-Block als einen anderen Benutzer laufen lassen.

Das nennt sich „Identitätswechsel“ („Impersonation“ auf Englisch). Dazu habe ich seit vielen Jahren eine Klasse erstellt:

(Diese funktioniert so nicht in .NET Core, nur in .NET Framework).

Manchmal hilft diese Klasse nichts, z. B. wenn man auf ein entferntes Laufwerk/UNC-Pfad zugreifen muss.

Für diesen Fall gibt es einen Wrapper für die NET USE-Funktionalität:

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

    /// <summary>
    /// Wrapper for the "NET USE" functionality.
    /// </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
    }
}

Damit kann dann mit einem bestimmten Benutzer auf den Pfad verbunden werden.