How can I get an active UNC Path in DFS programatically
Asked Answered
R

3

7

Given a DFS path how would I know what is the active path it is currently on programatically.

For exmaple I have 2 Servers shares as "\\Server1\Folder\" and "\\Server2\Folder\" and it has DFS turned on so it can be accessed on "\\DFS_Server\Folder\", how would I know what is the active path currently "\\DFS_Server\Folder\" is on, whether it is "\\Server1\Folder\" or "\\Server2\Folder\".

Resile answered 15/10, 2010 at 0:44 Comment(0)
C
4

try this where sDFSPath is the path you want to query and sHostServer is the Server you want to query your WMI, this can be any of the two servers you mentioned above. You can even make a more elegant code when it fails on first server then query WMI on the next servers

public static ArrayList GetActiveServers(string sDFSPath, string sHostServer)
{
    ArrayList sHostNames = new ArrayList(); 

    ManagementPath oManagementPath = new ManagementPath();
    oManagementPath.Server = sHostServer;
    oManagementPath.NamespacePath = @"root\cimv2";

    oManagementScope = new ManagementScope(oManagementPath);
    oManagementScope.Connect();

    SelectQuery oSelectQuery = new SelectQuery();
    oSelectQuery.QueryString = @"SELECT * FROM Win32_DfsTarget WHERE LinkName LIKE '%" + sDFSPath.Replace("\\", "\\\\") + "%' and State = 1";

    ManagementObjectSearcher oObjectSearcher = new ManagementObjectSearcher(oManagementScope, oSelectQuery);
    ManagementObjectCollection oObjectCollection = oObjectSearcher.Get();

    if (oObjectCollection.Count != 0)
    {
        foreach (ManagementObject oItem in oObjectCollection)
        {
            sHostNames.Add(oItem.Properties["ServerName"].Value.ToString());
        }
    }

    return sHostNames;
}

Hope it makes sense

Cheater answered 15/10, 2010 at 3:11 Comment(2)
How would the above look in VBS using WMI? Any guidance would be appreciated.Overword
@Cheater I see that you are passing in the host. How would you get a list of dfs links. Also what is the shostserver? is that a dfs server? Also, what type of permissions are needed for thatVentriloquize
G
8

If I understand your requirement correctly, there's also an API that seems to do what you need:

// mscorlib (no additional assemblies needed)
using System.Runtime.InteropServices;

public static class Dfs
{
    private enum NetDfsInfoLevel
    {
        DfsInfo1 = 1,
        DfsInfo2 = 2,
        DfsInfo3 = 3,
        DfsInfo4 = 4,
        DfsInfo5 = 5,
        DfsInfo6 = 6,
        DfsInfo7 = 7,
        DfsInfo8 = 8,
        DfsInfo9 = 9,
        DfsInfo50 = 50,
        DfsInfo100 = 100,
        DfsInfo150 = 150,
    }

    [DllImport("netapi32.dll", SetLastError = true)]
    private static extern int NetApiBufferFree(IntPtr buffer);

    [DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern int NetDfsGetInfo(
        [MarshalAs(UnmanagedType.LPWStr)] string DfsEntryPath, // DFS entry path for the volume
        [MarshalAs(UnmanagedType.LPWStr)] string ServerName,   // This parameter is currently ignored and should be NULL
        [MarshalAs(UnmanagedType.LPWStr)] string ShareName,    // This parameter is currently ignored and should be NULL.
        NetDfsInfoLevel Level,                                 // Level of information requested
        out IntPtr Buffer                                      // API allocates and returns buffer with requested info
        );

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private struct DFS_INFO_3
    {
        [MarshalAs(UnmanagedType.LPWStr)]
        public string EntryPath;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string Comment;
        public int State;
        public int NumberOfStorages;
        public IntPtr Storage;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private struct DFS_STORAGE_INFO
    {
        public int State;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string ServerName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string ShareName;
    }

    private static T GetStruct<T>(IntPtr buffer, int offset=0)where T:struct
    {
        T r = new T();
        r = (T) Marshal.PtrToStructure(buffer + offset * Marshal.SizeOf(r), typeof(T));
        return r;
    }

    public static string GetDfsInfo(string server)
    {
        string rval = null;
        IntPtr b;
        int r = NetDfsGetInfo(server, null, null, NetDfsInfoLevel.DfsInfo3, out b);
        if(r != 0)
        {
            NetApiBufferFree(b);

            // return passed string if not DFS
            return rval;
        }

        DFS_INFO_3 sRes = GetStruct<DFS_INFO_3>(b);
        if(sRes.NumberOfStorages > 0)
        {
            DFS_STORAGE_INFO sResInfo = GetStruct<DFS_STORAGE_INFO>(sRes.Storage);
            rval = string.Concat(@"\\", sResInfo.ServerName, @"\", sResInfo.ShareName, @"\");
        }

        NetApiBufferFree(b);

        return rval;
    }
}

Use it like this:

string dfsPath = @"\\DFS_Server\Folder\";
string share = Dfs.GetDfsInfo(dfsPath)

For an API reference, check msdn on NetDfsGetInfo, DFS_INFO_3, DFS_STORAGE_INFO and NetApiBufferFree.

Glassware answered 20/11, 2012 at 9:46 Comment(1)
Great solution, could you please add using System.Runtime.InteropServices; to the top?Cordi
C
4

try this where sDFSPath is the path you want to query and sHostServer is the Server you want to query your WMI, this can be any of the two servers you mentioned above. You can even make a more elegant code when it fails on first server then query WMI on the next servers

public static ArrayList GetActiveServers(string sDFSPath, string sHostServer)
{
    ArrayList sHostNames = new ArrayList(); 

    ManagementPath oManagementPath = new ManagementPath();
    oManagementPath.Server = sHostServer;
    oManagementPath.NamespacePath = @"root\cimv2";

    oManagementScope = new ManagementScope(oManagementPath);
    oManagementScope.Connect();

    SelectQuery oSelectQuery = new SelectQuery();
    oSelectQuery.QueryString = @"SELECT * FROM Win32_DfsTarget WHERE LinkName LIKE '%" + sDFSPath.Replace("\\", "\\\\") + "%' and State = 1";

    ManagementObjectSearcher oObjectSearcher = new ManagementObjectSearcher(oManagementScope, oSelectQuery);
    ManagementObjectCollection oObjectCollection = oObjectSearcher.Get();

    if (oObjectCollection.Count != 0)
    {
        foreach (ManagementObject oItem in oObjectCollection)
        {
            sHostNames.Add(oItem.Properties["ServerName"].Value.ToString());
        }
    }

    return sHostNames;
}

Hope it makes sense

Cheater answered 15/10, 2010 at 3:11 Comment(2)
How would the above look in VBS using WMI? Any guidance would be appreciated.Overword
@Cheater I see that you are passing in the host. How would you get a list of dfs links. Also what is the shostserver? is that a dfs server? Also, what type of permissions are needed for thatVentriloquize
S
4

Thank you, your hints were useful. However I was more successfull with NetDfsGetClientInfo. Also realized that resolving process may be recursive. I ended up with at least 2 recursive calls to get the actual physical UNC share and here is my example.

I don't know, how

public static class DFS
{
    #region Import

    [DllImport("Netapi32.dll", EntryPoint = "NetApiBufferFree")]
    public static extern uint NetApiBufferFree(IntPtr Buffer);

    [DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int NetDfsGetInfo(
        [MarshalAs(UnmanagedType.LPWStr)] string EntryPath,
        [MarshalAs(UnmanagedType.LPWStr)] string ServerName,
        [MarshalAs(UnmanagedType.LPWStr)] string ShareName,
        int Level,
        out IntPtr Buffer);

    [DllImport("Netapi32.dll")]
    public static extern int NetDfsGetClientInfo(
        [MarshalAs(UnmanagedType.LPWStr)] string EntryPath,
        [MarshalAs(UnmanagedType.LPWStr)] string ServerName,
        [MarshalAs(UnmanagedType.LPWStr)] string ShareName,
        int Level,
        out IntPtr Buffer);

    #endregion

    #region Structures

    public struct DFS_INFO_3
    {
        [MarshalAs(UnmanagedType.LPWStr)]
        public string EntryPath;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string Comment;
        public UInt32 State;
        public UInt32 NumberOfStorages;
        public IntPtr Storages;
    }

    public struct DFS_STORAGE_INFO
    {
        public Int32 State;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string ServerName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string ShareName;
    }

    #endregion

    const int DFS_VOLUME_STATE_OK = 0x00000001;
    const int DFS_VOLUME_STATE_ONLINE = 0x00000004;
    const int DFS_STORAGE_STATE_ONLINE = 0x00000002;
    const int DFS_STORAGE_STATE_ACTIVE = 0x00000004;

    public static String GetSharePath(String DFSPath)
    {
        if (!String.IsNullOrEmpty(DFSPath))
        {
            IntPtr Buffer = IntPtr.Zero;
            try
            {
                int Error = NetDfsGetClientInfo(DFSPath, null, null, 3, out Buffer);
                if (Error == 0)
                {
                    DFS_INFO_3 DFSInfo = (DFS_INFO_3)Marshal.PtrToStructure(Buffer, typeof(DFS_INFO_3));
                    if ((DFSInfo.State & DFS_VOLUME_STATE_OK) > 0)
                    {
                        String SubPath = DFSPath.Remove(0, 1 + DFSInfo.EntryPath.Length).TrimStart(new Char[] { '\\' });
                        for (int i = 0; i < DFSInfo.NumberOfStorages; i++)
                        {
                            IntPtr Storage = new IntPtr(DFSInfo.Storages.ToInt64() + i * Marshal.SizeOf(typeof(DFS_STORAGE_INFO)));
                            DFS_STORAGE_INFO StorageInfo = (DFS_STORAGE_INFO)Marshal.PtrToStructure(Storage, typeof(DFS_STORAGE_INFO));
                            if ((StorageInfo.State & DFS_STORAGE_STATE_ACTIVE) > 0)
                            {
                                if (String.IsNullOrEmpty(SubPath))
                                {
                                    return String.Format(@"\\{0}\{1}", StorageInfo.ServerName, StorageInfo.ShareName);
                                }
                                else
                                {
                                    return GetSharePath(String.Format(@"\\{0}\{1}\{2}", StorageInfo.ServerName, StorageInfo.ShareName, SubPath));
                                }
                            }
                        }
                    }
                }
                else if (Error == 2662)
                    return DFSPath;
            }
            finally
            {
                NetApiBufferFree(Buffer);
            }
        }
        return null;
    }

    public static String GetShareName(String SharePath)
    {
        if (!String.IsNullOrEmpty(SharePath))
        {
            String[] Tokens = SharePath.Trim(new Char[] { '\\' }).Split(new Char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
            if (2 <= Tokens.Length)
                return Tokens[1];
        }
        return null;
    }
}
Strophic answered 24/1, 2014 at 13:29 Comment(4)
I found out little mode. So dfs resolution is recuresive. Assuming youre looking for unc a. Each iteration you need to remove EntryPath from the beginning of a, and rebuild it (prefix with ServerName and ShareName, which you get from StorageInfo) receiving unc b. If a == b resolution is complete.Strophic
NetDfsGetInfo was triggering error 1168 (not found), which is wired, so i used NetDfsGetClientInfo. This one returns data only for active storage. For non active storage it returns nothing. Workaround-hack is to use System.IO.Directory.Exists(...) on your unc path before calling NetDfsGetClientInfo.Strophic
Can NetDfsGetClientInfo be used in VBS, perhaps with WMI?Overword
getting 2662 code from int Error = NetDfsGetClientInfo(DFSPath, null, null, 3, out Buffer); any pointers to resolve it,Indivisible

© 2022 - 2024 — McMap. All rights reserved.