45

How can I check if a UNC Path is available? I have the problem that the check takes about half a minute if the share is not available :

var fi = new DirectoryInfo(@"\\hostname\samba-sharename\directory");

if (fi.Exists)
//...

Is there a faster way to check if a folder is available? I'm using Windows XP and C#.

dsh
  • 12,037
  • 3
  • 33
  • 51
thumbmunkeys
  • 20,606
  • 8
  • 62
  • 110

8 Answers8

26

How's this for a quick and dirty way to check - run the windows net use command and parse the output for the line with the network path of interest (e.g. \\vault2) and OK. Here's an example of the output:

C:\>net use
New connections will be remembered.

Status       Local     Remote                    Network

-------------------------------------------------------------------------------
OK           O:        \\smarty\Data       Microsoft Windows Network
Disconnected P:        \\dummy\Data       Microsoft Windows Network
OK                     \\vault2\vault2           Microsoft Windows Network
The command completed successfully.

It's not a very .netish solution, but it's very fast, and sometimes that matters more :-).

And here's the code to do it (and LINQPad tells me that it only takes 150ms, so that's nice)

void Main()
{
    bool available = QuickBestGuessAboutAccessibilityOfNetworkPath(@"\\vault2\vault2\dir1\dir2");
    Console.WriteLine(available);
}

public static bool QuickBestGuessAboutAccessibilityOfNetworkPath(string path)
{
    if (string.IsNullOrEmpty(path)) return false;
    string pathRoot = Path.GetPathRoot(path);
    if (string.IsNullOrEmpty(pathRoot)) return false;
    ProcessStartInfo pinfo = new ProcessStartInfo("net", "use");
    pinfo.CreateNoWindow = true;
    pinfo.RedirectStandardOutput = true;
    pinfo.UseShellExecute = false;
    string output;
    using (Process p = Process.Start(pinfo)) {
        output = p.StandardOutput.ReadToEnd();
    }
    foreach (string line in output.Split('\n'))
    {
        if (line.Contains(pathRoot) && line.Contains("OK"))
        {
            return true; // shareIsProbablyConnected
        }
    }
    return false;
}

Or you could probably go the route of using WMI, as alluded to in this answer to How to ensure network drives are connected for an application?

Adam Strobel
  • 63
  • 1
  • 13
Pat
  • 16,515
  • 15
  • 95
  • 114
  • 4
    Probably should change `line.Contains("OK")` to `line.StartsWith("OK")` in case the path contains 'OK' – sparkplug Nov 02 '15 at 10:01
  • 2
    This is an excellent solution, for mapped drives. Any idea how I could utilize this functionality for network paths which are shared but not necessarily mapped? – PJRobot Feb 12 '19 at 10:46
  • FYI: if this code will run as admin, while mapped drives are at the user level, you'll get `New connections will be remembered. There are no entries in the list.` – itsho May 15 '20 at 12:00
  • This requires to map every time network location with `net use` command, but for direct paths \\10.10.10.10\foo\bar which are not mapped this wouldn't work :( – shjeff Jun 15 '21 at 13:35
14

In my project I use the System.IO :

if (Directory.Exists(@"\\hostname\samba-sharename\directory")) { ...

and it's pretty fast so far...

Fabien
  • 458
  • 3
  • 17
  • 2
    Yes, this is to test the existence & availability... Perhaps there is a sort of time out to set from the network admin when not available and that would be why you have to wait 30 sec..? – Fabien Sep 28 '16 at 10:13
5

In a project of mine, i had to check whether a server connection was established or not. I used a TCP Socket to asynchronically check whether the server could be reached or not. I wonder if you could use this to check on a network share. The async TCP Socket connect goes so fast i tested for the connection 10 times under 60 miliseconds. Maybe you could play around with that a bit ?


EDIT: Here is the Asynchronous Socket i used for my project. Use this class to check for a certain IP or address. Hope it is of any use to you

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace Base.BaseObjects
{
    public class AsynchronousClient
    {
        #region Properties

        private int _port = 0000, currentTry = 0, _buffersize, _fastpingdelay = 80;
        private string _server = "localhost";
        private Socket client;
        private static IPEndPoint remoteEP;

        // Delegates & Events
        public delegate void SendMessageDelegate(string message);
        public event SendMessageDelegate SendMessageEvent;
        public delegate void ConnectionStatusDelegate(bool connected, bool reconnect);
        public event ConnectionStatusDelegate ConnectionStatusChanged;

        // ManualResetEvent instances signal completion.
        private static ManualResetEvent connectDone = new ManualResetEvent(false);
        private static ManualResetEvent sendDone = new ManualResetEvent(false);
        private static ManualResetEvent receiveDone = new ManualResetEvent(false);

        /// <summary>
        /// Port to monitor
        /// </summary>
        public int Port { get { return _port; } }

        /// <summary>
        /// Number of packages to buffer until system reports connection loss
        /// </summary>
        public int BufferSize { get { return _buffersize; }  }

        /// <summary>
        /// Time in milliseconds between two pings
        /// </summary>
        public int FastPingDelay { get { return _fastpingdelay; } }

        /// <summary>
        /// Servername to connect to
        /// </summary>
        public string Server
        {
            get { return _server; }
            set
            {
                _server = value;
                // Resolve the remote endpoint for the socket.
                try
                {
                    IPAddress ipAddress = (IPAddress)Dns.GetHostAddresses(value)[0];
                    remoteEP = new IPEndPoint(ipAddress, Port);
                }
                catch (SocketException ex)
                {
                    SendMessage(ex.Message);
                }
            }
        }

        #endregion

        #region Events & Delegates

        protected void SendMessage(string message)
        {
            if (SendMessageEvent != null)
                SendMessageEvent(message);
        }

        protected void UpdateConnectionStatus(bool connected, bool reconnect)
        {
            if (ConnectionStatusChanged != null)
                ConnectionStatusChanged(connected, reconnect);
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.
                Socket client = (Socket)ar.AsyncState;

                // Complete the connection.
                client.EndConnect(ar);

                SendMessage(String.Format("Socket connected to {0}", client.RemoteEndPoint.ToString()));
                //UpdateConnectionStatus(true, false);

                // Signal that the connection has been made.
                connectDone.Set();
            }
            catch (Exception e)
            {
                SendMessage(e.ToString());
                UpdateConnectionStatus(false, true);
            }
        }

        #endregion

        #region methods

        public AsynchronousClient(int port, string server)
        {
            _port = port;
            Server = server;
            _buffersize = 10;
            _fastpingdelay = 20;
        }

        public void CreateSocket()
        {
            try
            {
                StopClient();
            }
            catch (Exception ex)
            {
                SendMessage(ex.Message);
            }
            finally
            {
                client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            }
        }

        public bool FastPingSocket()
        {
            for (currentTry = 0; currentTry <= BufferSize; currentTry++)
            {
                try
                {
                    CreateSocket();
                    client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client);
                    connectDone.WaitOne();
                    System.Threading.Thread.Sleep(FastPingDelay);
                    client.Shutdown(SocketShutdown.Receive);
                    connectDone.WaitOne();
                    client.Close();
                    return true;
                }
                catch (SocketException ex)
                {
                    SendMessage(ex.Message);
                }
                catch (ObjectDisposedException ex)
                {
                    currentTry--;
                    SendMessage(ex.Message);
                    CreateSocket();
                }
                catch (NullReferenceException ex)
                {
                    currentTry--;
                    SendMessage(ex.Message);
                    CreateSocket();
                }
                catch (ArgumentNullException ex)
                {
                    SendMessage(ex.Message);
                    CreateSocket();
                }
                catch (InvalidOperationException ex)
                {
                    SendMessage(ex.Message);
                    CreateSocket();
                    currentTry--;
                }
                finally
                {
                    StopClient();
                }
            }
            UpdateConnectionStatus(false, true);
            return false;
        }

        public void StopClient()
        {
            // Release the socket.
            try
            {
                client.Shutdown(SocketShutdown.Both);
                client.Close();
            }
            catch (Exception) { }
            finally
            {
                UpdateConnectionStatus(false, false);
            }
        }

        #endregion

    }
}

Edit: Please don't just copy/paste it. Try to understand the code so you can use it for your benefit, and finetune it for your needs.

Steven Ryssaert
  • 1,989
  • 15
  • 25
4

I ended up "cheating" and just pinging the host, which is reasonable as that in reality is the case that I'm checking for.

private bool HostExists(string PCName)
{
    Ping pinger = new Ping();

    try
    {
        PingReply reply = pinger.Send(PCName);
        return reply.Status == IPStatus.Success;
    }
    catch
    {
        return false;
    }
    finally
    {
        pinger.Dispose();
    }

}

This way suits me best because of its speed, simplicity, and reliability.

Jack
  • 472
  • 6
  • 13
3

I used the ping method suggested above and it did not work for me since I am using OpenDNS Here is a function that worked well for me:

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/// <summary>
/// A quick method to test is the path exists 
/// </summary>
/// <param name="s"></param>
/// <param name="timeOutMs"></param>
/// <returns></returns>
public static bool CheckPathExists(string s, int timeOutMs = 120) {
    if (s.StartsWith(@"\\")) {
        Uri uri = new Uri(s);
        if (uri.Segments.Length == 0 || string.IsNullOrWhiteSpace(uri.Host))
            return false;
        if (uri.Host != Dns.GetHostName()) {
            WebRequest request;
            WebResponse response;
            request = WebRequest.Create(uri);
            request.Method = "HEAD";
            request.Timeout = timeOutMs;
            try {
                response = request.GetResponse();
            } catch (Exception ex) {
                return false;
            }
            return response.ContentLength > 0;

            // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
            // Do a Ping to see if the server is there
            // This method doesn't work well using OPenDNS since it always succeeds
            // regardless if the IP is a valid or not
            // OpenDns always maps every host to an IP. If the host is not valid the 
            // OpenDNS will map it to 67.215.65.132
            /* Example:
                C:\>ping xxx

                Pinging xxx.RT-AC66R [67.215.65.132] with 32 bytes of data:
                Reply from 67.215.65.132: bytes=32 time=24ms TTL=55
                */

            //Ping pingSender = new Ping();
            //PingOptions options = new PingOptions();
            // Use the default Ttl value which is 128,
            // but change the fragmentation behavior.
            //options.DontFragment = true;

            // Create a buffer of 32 bytes of data to be transmitted.
            //string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
            //byte[] buffer = Encoding.ASCII.GetBytes(data);
            //int timeout = 120;
            //PingReply reply = pingSender.Send(uri.Host, timeout, buffer, options);
            //if (reply == null || reply.Status != IPStatus.Success)
            //    return false;
        }
    }
    return File.Exists(s);
}
Michael Fitzpatrick
  • 682
  • 3
  • 8
  • 18
2

Thats probably the quickest way, the delay will be the general network speed/disk access etc.

If this is causing a delay for the user, you could try checking this asynchronously?

Mark Redman
  • 24,079
  • 20
  • 92
  • 147
  • I could make it async, but the user would still have to wait half a minute for the file. – thumbmunkeys Mar 01 '11 at 10:08
  • 1
    If you navigate to the same UNC path in Explorer, do you still see the same delay? – Mark Redman Mar 01 '11 at 10:39
  • @Mark: Yes it is the same delay, which tells me that win xp seems to have a huge timeout when accessing non existent samba shares. Also the explorer GUI hangs for half a minute when it tries to access the share (which is really bad). – thumbmunkeys Mar 01 '11 at 16:22
  • 1
    I think the delay in this case is a fact of life, and its just about how elegantly this is presented to the user. – Mark Redman Mar 01 '11 at 18:24
  • 1
    @pivot: Yes, this takes quite a while. It's not just the case in Windows XP; all versions of Windows have a similar timeout. There may be a way to change it, but it won't help your users any. Mark's proposal of covering it up with a pretty GUI is about your only choice. Explorer doesn't even bother to do that. If you distract the user sufficiently (say with a progress bar), they probably won't care. Especially if your app doesn't have to do this *repeatedly* (and it shouldn't). – Cody Gray - on strike Mar 02 '11 at 05:32
  • 2
    @pivot: As per your original question, no, you can't (and shouldn't) check in advance if the drive is available. That causes unavoidable race conditions, and for little point. As others have suggested, it's best to just attempt to use the drive, and detect failure. You have to write that code anyway. – Cody Gray - on strike Mar 02 '11 at 05:33
  • @Mark: could you replace your answer with your comment, I'll mark it as answered then – thumbmunkeys Mar 05 '11 at 20:45
  • @CodyGray I disagree about not checking if the drive is available in the situation where you can quickly check if it is *not* available immediately before accessing the drive. The reason is that we are talking about network shares and it is often the case that the share is just not connected - we should be able to inform the user quickly about that situation. Then, when we think it is available, there should still be a check on the validity of the network resource/path. – Pat Aug 03 '12 at 15:38
0

Maybe you should try to create the folder, if it does exist, it will return an error that you could catch. There should be no default timeout.

  • 3
    I tried that, creating a folder has the same long timeout if the share is not available (It would have been surprising if it was otherwise) – thumbmunkeys Mar 01 '11 at 10:21
0

The accepted answer didn't work in my case because the drive stays "Disconnected" even if it is available (when my VPN reconnects) until I access the drive.

Instead this works: do a single quick ping of the IP. For example:

ping 192.168.10.98 -n 1 -w 100