Feb 232010

I don’t really consider myself a C# programmer yet, but I recently had a need to figure out how to do this in C#.  As it turns out I couldn’t find a handy built in function so I Binged for a solution.  Unfortunately, I only found parts of solutions in different places.  So, I compiled all of them in to the following code.  Maybe some of this will help someone else with the same need.  This code is similar to what I do in C++ and I think it is pretty safe, but make sure you test it in your application.

using System.Runtime.InteropServices;
using System.Management;

        [DllImport("mpr.dll")]
        [return: MarshalAs(UnmanagedType.U4)]
        static extern int WNetGetUniversalName(
            string lpLocalPath,
            [MarshalAs(UnmanagedType.U4)] int dwInfoLevel,
            IntPtr lpBuffer,
            [MarshalAs(UnmanagedType.U4)] ref int lpBufferSize);
        const int UNIVERSAL_NAME_INFO_LEVEL = 0x00000001;
        const int REMOTE_NAME_INFO_LEVEL = 0x00000002;
        const int ERROR_MORE_DATA = 234;
        const int ERROR_NOT_CONNECTED = 2250;
        const int ERROR_NO_NETWORK = 1222;
        const int NOERROR = 0;

        static public String GetUniversalName(String _path)
        {
            // The return value.
            String UNCPath = null;

            // Already a UNC, no need to convert
            if (_path.StartsWith(@"\") || _path.StartsWith(@"//"))
                return _path;

            // WNetGetUniversalName does not allow a null buffer
            IntPtr buf = Marshal.AllocCoTaskMem(5);
            int size = 0;
            try
            {
                // First call to WNetGetUniversalName to get the buffer size
                int ret = WNetGetUniversalName(_path, UNIVERSAL_NAME_INFO_LEVEL, buf, ref size);
                Marshal.FreeCoTaskMem(buf);

                // Local Drive
                if (ret == ERROR_NOT_CONNECTED || ret == ERROR_NO_NETWORK)
                    return GetLocalUniversalName(_path);

                // If the return is not ERROR_MORE_DATA then something went wrong and the
                // conversion didn't work so return the original path
                if (ret != ERROR_MORE_DATA)
                    return _path;
            }
            catch (Exception  ex)
            {
                String msg = String.Format("{0}: {1}",ex.Message, _path);
                log.Error(msg);
                return _path; // Something blew so just return the original path
            }

            buf = Marshal.AllocCoTaskMem(size);
            try
            {
                // Look up the name of the share for the mapped drive
                int ret = WNetGetUniversalName(_path, UNIVERSAL_NAME_INFO_LEVEL, buf, ref size);

                // If it didn't convert just return the original path
                if (ret != NOERROR)
                {
                    // Release the temp buffer
                    Marshal.FreeCoTaskMem(buf);
                    return _path;
                }

                // Now convert the result to a string we can actually use
                // It's all in the same buffer, but the pointer is first,
                // so offset the pointer by IntPtr.Size and pass to PtrToStringAnsi.
                UNCPath = Marshal.PtrToStringAnsi(new IntPtr(buf.ToInt64() + IntPtr.Size), size);

                // Clean up stuff at the end of UNCPath
                int actualLength = UNCPath.IndexOf('\0');
                if (actualLength >= 0)
                {
                    UNCPath = UNCPath.Substring(0, actualLength);
                }

                // Release the temp buffer
                Marshal.FreeCoTaskMem(buf);
                return UNCPath;
            }
            catch (Exception ex)
            {
                String msg = String.Format("{0}: {1}", ex.Message, _path);
                log.Error(msg);
                return _path;
            }
        }

        // Get a UNC for a local share
        static public String GetLocalUniversalName(String _path)
        {
            // Already a UNC, no need to convert
            if (_path.StartsWith(@"\") || _path.StartsWith(@"//"))
                return _path;

            String UNCPath = _path;
            ManagementClass exportedShares = new ManagementClass("Win32_Share");
            ManagementObjectCollection shares = exportedShares.GetInstances();
            foreach (ManagementObject share in shares)
            {
                String name = share["Name"].ToString();
                String path = share["Path"].ToString();
                String caption = share["Caption"].ToString();
                if (!name.Contains("$") && _path.StartsWith(path))
                {
                    String rest = _path.Substring(path.Length);
                    UNCPath = String.Format(@"<a href="file://\\{0}\{1}\{2">\\{0}\{1}\{2</a>}", Environment.MachineName, name, rest);
                }
            }
            return UNCPath;
        }

Leave a Reply

(required)

(required)