WMI is really helpful specially when you dont have a DLL’s or Assembly that you can reference to or even if its hard to understand how a dll works and how you to define those structures that the assembly is outputting, one of this is finding out what are the active UNC given a DFS Link. To use WMI on .NET is as easy as referencing to:
using System.Management;
then query the WMI objects you need, Now to get which are the active UNC paths given a link or folder location you usually right click a properties of the folder to find out which is active
but programatically you need to query Win32_DfsTarget in the rootcimv2 namespace, by fitlering the results to State = 1 will show all current active connections. Now below is the codes to achieve that on .Net.
Determine active UNC path in DFC using WMI
public static ArrayList GetActiveServers(string sPath) { ArrayList aHostNames = new ArrayList(); ArrayList aDFSServers = new ArrayList(); aDFSServers.Add("Server1"); aDFSServers.Add("Server2"); foreach (string sHostServer in aDFSServers) { ManagementObjectCollection oObjectCollection = null; if (IsHostOnline(sHostServer, "LinkName", sPath, ref oObjectCollection)) { if (oObjectCollection.Count != 0) { foreach (ManagementObject oItem in oObjectCollection) { aHostNames.Add(oItem.Properties["ServerName"].Value.ToString()); } } break; } } return aHostNames; } public static bool IsHostOnline(string sHostServer, string sSearchField, string sSearchString, ref ManagementObjectCollection oManagementObjectCollection) { try { ArrayList sHostNames = new ArrayList(); ManagementPath oManagementPath = new ManagementPath(); oManagementPath.Server = sHostServer; oManagementPath.NamespacePath = @"rootcimv2"; oManagementScope = new ManagementScope(oManagementPath); oManagementScope.Connect(); SelectQuery oSelectQuery = new SelectQuery(); oSelectQuery.QueryString = @"SELECT * FROM Win32_DfsTarget WHERE " + sSearchField + " LIKE '%" + sSearchString.Replace("\", "\\") + "%' and State = 1"; ManagementObjectSearcher oObjectSearcher = new ManagementObjectSearcher(oManagementScope, oSelectQuery); ManagementObjectCollection oObjectCollection = oObjectSearcher.Get(); if (oObjectCollection.Count != 0) { oManagementObjectCollection = oObjectCollection; return true; } return false; } catch { return false; } }
To explain a bit on what happens on the code, I had separated them on two Functions, one is to check whether the Host is online and another is to place that query results in a more useable type to be consumed by your application which is an ArrayList Type. Now the Function which checks for the Availability of the host you are querying is also outputting the result set you need so it executes one action for the whole process saving time and resources rather than pinging the server first using WMI then do your stuff, they both go to WMI anyways.
Now if you notice we are checking this for all servers thats why you have that ArrayList aDFSServers on GetActiveServers which ennumerates all your DFS Host, were doing that so in an event that one is down you can still check the other servers on roster thats the reason why you also have DFS anyways, for redundancy. All servers on your DFS Environment will have the same contents on the Win32_DfsTarget anyways.
Now you notice WMI is a bit slow as you are querying a big set of extension data of the Windows Driver Model, if you opt to choose for another option then using Netapi32.dll will be the better option as it is faster but requires more understanding on how it works and the data model that it outputs. Netapi32.dll is a process belonging to the Microsoft Network Program that contains the Windows NET API used so that applications gain access to Microsoft network like DFS. Now to achive the same results as above you need the following codes
Determine active UNC path in DFC using Netapi32.dll
[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, ref IntPtr Buffer ); 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; } public static ArrayList GetActiveServersPKI(string sDFSPath) { ArrayList sServers = new ArrayList(); IntPtr pBuffer = new IntPtr(); int iResult = NetDfsGetInfo(sDFSPath, null, null, 3, ref pBuffer); if (iResult == 0) { DFS_INFO_3 oDFSInfo = (DFS_INFO_3)Marshal.PtrToStructure(pBuffer, typeof(DFS_INFO_3)); for (int i = 0; i < oDFSInfo.NumberOfStorages; i++) { IntPtr pStorage = new IntPtr(oDFSInfo.Storages.ToInt64() + i * Marshal.SizeOf(typeof(DFS_STORAGE_INFO))); DFS_STORAGE_INFO oStorageInfo = (DFS_STORAGE_INFO)Marshal.PtrToStructure(pStorage, typeof(DFS_STORAGE_INFO)); //Get Only Active Hosts if (oStorageInfo.State == 2) { sServers.Add(oStorageInfo.ServerName); } } } return sServers; }
If you noticed we dont have to ennumerate the servers to query are we are directly communicating with the dll, delivering the result will be fast as well. Now for a bit of an explanation on what the piece of code does.
On the method NetDfsGetInfo you are just importing the Dll called “Netapi32.dll” and exposing thaty to your application, now since the dll methods outputs a buffer you need to understand how that buffer is structured which is defined here. That is why if you notice we defined stuctures DFS_INFO_3 and DFS_STORAGE_INFO to handle that output into a more usable type.
Amazing work, Raymund. Thanks for posting!
I have two questions about it:
1) The script seems to require input of the DFS servers (Server1, Server2). Perhaps I missed something.. Could you please advise?
2) Assuming one only needs to provide the DFS path to determine its UNC source(s), what would be the equivalent in VBScript?
We only have VBS available on all hosts, and can’t assume utilities like DFSUtil.exe are available, so we have to stick with programmatic solutions. I’m not a professional scripter, though I do try hard to do my own work where possible. 🙂 Thanks in advance for any help you can offer!