Click here to Skip to main content
Click here to Skip to main content

Network Shares and UNC paths

By , 11 Nov 2003
 

Introduction

Two common requirements seem to have been missed from the .NET framework: enumerating network shares, and converting a local path to a UNC path. The shares could potentially be retrieved via WMI, but you can't guarantee that NT4 and 98 will have it installed. These classes provide a simple wrapper around the API calls necessary to retrieve this information.

Implementation

To retrieve the UNC path for a file on a mapped drive, call WNetGetUniversalName. This doesn't work on Windows 95, but neither does the .NET framework, so I don't care!

If the path is on a local shared folder, WNetGetUniversalName won't help. In this case, you need to call NetShareEnum and look for a matching path. The function behaves completely differently across NT/2K/XP and 9x/ME, so there are two versions of this code.

  • In the NT version, you may not have permissions to access the SHARE_INFO_2 information, so the code will revert to the SHARE_INFO_1 structure. In this case, the path will not be available.
  • On Windows 9x/ME/NT4, the server name (if any) must be prefixed with "\\". The code will check this, so you don't need to worry.
  • On Windows 9x/ME, there is no way to resume the enumeration, so the code will return a maximum of 20 shares.

Classes

Share

Encapsulates information about a single network share, including the server name, share name, share type, local path and comment. Also has some utility methods to determine if it is a file-system share, whether it matches part of a local path, and returns the root directory.

ShareCollection

A strongly-typed read-only collection of Share objects. Shares can be retrieved by index or by path, in which case the best match will be returned. Includes factory methods to return the shares for a specified computer, and static methods to return the UNC path and local share for a local path.

Interesting Note

The Windows 98 structure SHARE_INFO_50 is declared in the file SrvApi.h. Towards the top of the file, there is a line which reads #pragma pack(1). Although this looks like complete gobbledegook, it is actually important. The number in brackets specifies the packing for all structures in the file. According to MSDN, the alignment of a member will be on a boundary that is either a multiple of n or a multiple of the size of the member, whichever is smaller.

In the case of the SHARE_INFO_50 structure, the default packing, pads the structure to align the members on 8 byte boundaries. This meant that the size was calculated as 44 bytes, when it should be 42. Two extra padding bytes are added to the end of the structure to make the length a multiple of 4, the size of the native integer.

After tearing my hair out trying to work out why the size was wrong, and why taking two bytes off the end reduced the size by four bytes, I finally noticed the Pack property of the StructLayoutAttribute class. Adding Pack=1 to the attribute fixed the problems, and the code now works on Windows 98.

Demonstration

The testShares.cs file contains a sample console application which demonstrates the main functionality. The testShares.exe file is the compiled version of this sample.

//
// Enumerate shares on local computer
//
Console.WriteLine("\nShares on local computer:");
ShareCollection shi = ShareCollection.LocalShares;
if (shi != null) 
{
    foreach(Share si in shi) 
    {
        Console.WriteLine("{0}: {1} [{2}]", 
            si.ShareType, si, si.Path);

        // If this is a file-system share, try to
        // list the first five subfolders.
        // NB: If the share is on a removable device,
        // you could get "Not ready" or "Access denied"
        // exceptions.
        if (si.IsFileSystem) 
        {
            try 
            {
                System.IO.DirectoryInfo d = si.Root;
                System.IO.DirectoryInfo[] Flds = d.GetDirectories();
                for (int i=0; i < Flds.Length && i < 5; i++)
                    Console.WriteLine("\t{0} - {1}", i, Flds[i].FullName);

                Console.WriteLine();
            }
            catch (Exception ex) 
            {
                Console.WriteLine("\tError listing {0}:\n\t{1}\n", 
                    si, ex.Message);
            }
        }
    }
}
else
    Console.WriteLine("Unable to enumerate the local shares.");

//
// Resolve local paths to UNC paths.
//
Console.WriteLine("{0} = {1}", 
    fileName, ShareCollection.PathToUnc(fileName));

Updates

  • 25 September 2002 - Original Release
  • 13 March 2003 -
    • Changed the ShareCollection to inherit from System.Collections.ReadOnlyCollection.
    • Moved the P/Invoke code inside the ShareCollection class, and removed the internal Interop class.
    • Fixed the packing bug on Windows 9x. Note to self: When the C++ header file says #pragma pack(1), don't ignore it!
    • Added support for level 1 share enumeration on Windows 98, which is required to list shares on remote machines, but isn't documented in MSDN.
  • 12 November 2003 - Bug fix

Credits

This code is (very) loosely based on a VB6 solution by Karl E. Peterson [http://www.mvps.org/vb].

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Richard Deeming
Software Developer Nevalee Business Solutions
United Kingdom United Kingdom
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5mvpJohn Simmons / outlaw programmer17 Nov '12 - 10:44 
Brilliant code and exactly what I needed. My only change was to make the ShareCollection a List<Share> so I could remove shares that were found that I wasn't interested in (default shares, non-disk shares, and stuff like that.
 
I'm using your code in my media center manager software to control where the user can search for media. Since the path is stored in a database that can be accessed from any PC on the LAN, I need to force the user to select from available UNC paths. Your code helps find the shares after I've found all the machines on the LAN.
 
Great stuff. Smile | :)
GeneralMy vote of 5memberRomulus-Tours28 Feb '11 - 5:14 
Very nice and helpfull
GeneralFail of ShareCollection.GetShares("10.0.255.13"); and ShareCollection.GetShares(@"\\10.0.255.13"); in VistamemberJosep Maria Roy7 Jan '10 - 3:59 
Hi,
 
good article! congratulations.   Wink | ;)
 
Just I'm using it from my web app with Framework 3.5 and Vista and I'm getting this errors:
 
ShareCollection shi = ShareCollection.GetShares("computer_name");   = works fine: 8 shared resources!
ShareCollection shi = ShareCollection.GetShares("10.0.255.13");      = fail
ShareCollection shi = ShareCollection.GetShares(@"\\10.0.255.13"); = fail
 
computer_name has 10.0.255.13 ip   Shucks | :->
 
The issue is in this lines:
 
                        nRet = NetShareEnum(server, level, out pBuffer, -1,
                              out entriesRead, out totalEntries, ref hResume);
 
                        if (ERROR_ACCESS_DENIED == nRet)
                        {
                              //Need admin for level 2, drop to level 1
                              level = 1;
                              nRet = NetShareEnum(server, level, out pBuffer, -1,
                                    out entriesRead, out totalEntries, ref hResume);
                        }
 
in 2nd nRet = NetShareEnum, nRet is still returning ERROR_ACCESS_DENIED. I have tested in some computers of my network and my conclusion is when you are using IP NetShareEnum seems that is not working fine in Vista (not tested neither XP nor Windows 7).
 
Talking of Windows 7: is this class compatible? Confused | :confused:
 
Yours sincerely,
 
Josep Balague
GeneralRe: Fail of ShareCollection.GetShares("10.0.255.13"); and ShareCollection.GetShares(@"\\10.0.255.13"); in VistamemberRichard Deeming7 Jan '10 - 4:32 
Strange - it works for me with either the computer name or IP address, from a console app or a web app. (I'm running Windows 7, so I guess that answers your last question.)
 
The documentation[^] says it requires "the DNS or NetBIOS name of the remote server", but the IP address (without the "\\" prefix) also seems to work.
 
Have you tried using WMI / System.Management[^]? A "Select * From Win32_Share[^]" query should return the list of shares.
 



"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer


Questionsam thing in java??membergaurav_nanda21 Mar '08 - 4:50 
hey !! can u suggest me how can i do same job in java???
its very important,,, please any one
GeneralLicence issues ... UNC classesmembervin kerai18 Dec '07 - 15:42 
Hi,
 
Can anyone please advise of any licence issues with regards to using these classes.
 
Regards
ViN.
GeneralRe: Licence issues ... UNC classesmemberThunderWeasel26 Jul '11 - 7:54 
This might be about 3 years too late, but you can find the licensing information here[^]. (There is a link at the top of the current page as well.)
Mark

GeneralFunctionality is too robust for needsmembervachaun19 Apr '07 - 8:45 
This works fine, but it retrieves too much data for what I need. Is there a way to get only the items that would be listed if you browsed to the location using the UNC path of the server...
 
IE: Start -> Run -> \\server_name -> enter
 
and list only items that are shown here.
GeneralRe: Functionality is too robust for needsmemberRichard Deeming19 Apr '07 - 8:52 
You can filter out any Share instances where the IsFileSystem property is false, which will leave you with the file-system shares.
 
If you want to hide the administrative shares as well, you can filter out any items where the ShareType property is equal to ShareType.Special.
 

"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

GeneralRe: Functionality is too robust for needsmembervachaun19 Apr '07 - 9:17 
That's exactly what I needed, with just a bit more fine tuning it gave me exactly what I wanted. Great Code!!!
QuestionAccessDenied flag wrong?memberToby_9 Mar '07 - 2:36 
First of all I want to thank you very much for this great peace of code! You saved me much work.
 
In the method EnumerateSharesNT(), however, I suppose there's a small bug regarding the accessDenied flag:
nRet = NetShareEnum(server, level, out pBuffer, -1, 
    out entriesRead, out totalEntries, ref hResume);
 
if (ERROR_ACCESS_DENIED == nRet) 
{
    //Need admin for level 2, drop to level 1
    level = 1;
    nRet = NetShareEnum(server, level, out pBuffer, -1, 
        out entriesRead, out totalEntries, ref hResume);
    accessDenied = true;
}
In my case the first NetShareEnum() call with level 2 returned ERROR_ACCESS_DENIED, then it correctly dropped to level 1 and called NetShareEnum() anew, which resulted in nRet == NO_ERROR. Nevertheless, accessDenied is set to true.
 
So I'd propose to write the following instead:
nRet = NetShareEnum(server, level, out pBuffer, -1, 
    out entriesRead, out totalEntries, ref hResume);
 
if (ERROR_ACCESS_DENIED == nRet) 
{
    //Need admin for level 2, drop to level 1
    level = 1;
    nRet = NetShareEnum(server, level, out pBuffer, -1, 
        out entriesRead, out totalEntries, ref hResume);
    if (ERROR_ACCESS_DENIED == nRet)
        accessDenied = true;
}
Toby
AnswerRe: AccessDenied flag wrong?memberRichard Deeming9 Mar '07 - 2:45 
I'm not sure what code you're looking at - there isn't a variable or field called accessDenied anywhere in the source. Confused | :confused:
 
The entire EnumerateSharesNT method reads:
protected static void EnumerateSharesNT(string server, ShareCollection shares)
{
    int level = 2;
    int entriesRead, totalEntries, nRet, hResume = 0;
    IntPtr pBuffer = IntPtr.Zero;
 
    try 
    {
        nRet = NetShareEnum(server, level, out pBuffer, -1, 
            out entriesRead, out totalEntries, ref hResume);
 
        if (ERROR_ACCESS_DENIED == nRet) 
        {
            //Need admin for level 2, drop to level 1
            level = 1;
            nRet = NetShareEnum(server, level, out pBuffer, -1, 
                out entriesRead, out totalEntries, ref hResume);
        }
 
        if (NO_ERROR == nRet && entriesRead > 0) 
        {
            Type t = (2 == level) ? typeof(SHARE_INFO_2) : typeof(SHARE_INFO_1);
            int offset = Marshal.SizeOf(t);
 
            for (int i=0, lpItem=pBuffer.ToInt32(); i<entriesRead; i++, lpItem+=offset) 
            {
                IntPtr pItem = new IntPtr(lpItem);
                if (1 == level) 
                {
                    SHARE_INFO_1 si = (SHARE_INFO_1)Marshal.PtrToStructure(pItem, t);
                    shares.Add(si.NetName, string.Empty, si.ShareType, si.Remark);
                }
                else 
                {
                    SHARE_INFO_2 si = (SHARE_INFO_2)Marshal.PtrToStructure(pItem, t);
                    shares.Add(si.NetName, si.Path, si.ShareType, si.Remark);
                }
            }
        }
 
    }
    finally 
    {
        // Clean up buffer allocated by system
        if (IntPtr.Zero != pBuffer) 
            NetApiBufferFree(pBuffer);
    }
}

 

"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

GeneralRe: AccessDenied flag wrong?memberToby_12 Mar '07 - 0:58 
Oops, I'm sorry, I used the code from the Network Browsing Control, which itself uses your code, but slightly adapted. I didn't realize that the sources differ in exactly that flag.
QuestionHow to retrive Shared Files(not folder) using WMImemberi_razi23 Feb '07 - 9:56 
I had searched a lot but unable to get help on reading shared Open Files using WMI. Please help on this. I can retrive till folder using this sample code. But this one is not working for Shared Files. I would like to read Computer Managemnt-Shared Folder-Open Files
 
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT *
FROM Win32_ConnectionShare");
foreach (ManagementObject connectionShare in searcher.Get())
{
// Win32_Share
string antecedent = connectionShare["Antecedent"].ToString();
Console.WriteLine("Antecedent: " + antecedent);
ManagementObject share = new ManagementObject(antecedent);
 
// Win32_ServerConnection
string dependant = connectionShare["Dependent"].ToString();
Console.WriteLine("Dependant: " + dependant);
ManagementObject connection = new ManagementObject(dependant);
Console.WriteLine(share["Name"].ToString());
 
if (connection != null && connection["Name"] != null)
Console.WriteLine(connection["Name"].ToString());
 
Console.WriteLine("\n");
}

 
adfg

AnswerRe: How to retrive Shared Files(not folder) using WMImemberRichard Deeming26 Feb '07 - 3:32 
I don't think you can do this with WMI - you'll need to use DirectoryServices and ADSI instead:
 
http://groups.google.com/group/microsoft.public.dotnet.framework/...[^]
 

"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

GeneralRe: How to retrive Shared Files(not folder) using WMImemberi_razi26 Feb '07 - 9:53 
Thanks a lot. We can achieve same by using either of two functions:
 

Private Sub FillOpenFile()
Dim objServerObject
Dim objresource
Dim lvData(2) As String
Dim lvItem As ListViewItem
InitializeListViewOpenFiles()
'Try
objServerObject = GetObject("WinNT://" & strOpenFileServer & "/LanmanServer")
If (IsNothing(objServerObject) = False) Then
For Each objresource In objServerObject.resources
If (Not objresource.User = "") Then
Dim strUser As String
strUser = objresource.User
strUser = strUser.Substring(strUser.Length - 1)
If (Not objresource.User = "") And (Not strUser = "$") Then
lvData(0) = objresource.name
lvData(1) = objresource.path
lvData(2) = objresource.user
 
On Error Resume Next
lvItem = New ListViewItem(lvData, 0)
lvwOpenFiles.Items.Add(lvItem)
End If
End If
Next
End If
'Catch ex As Exception
' 'MessageBox.Show(ex.Message)
'End Try
End Sub
 
'this also do the same as above-Iqubal
Private Sub FillOpenFile1()
Dim p As New Process
Dim pi As New ProcessStartInfo
pi.UseShellExecute = False
pi.RedirectStandardOutput = True
pi.Arguments = " /query /S " & strOpenFileServer & " /U AdminId /P !My!Password"
pi.WorkingDirectory = "C:\\windows\\system32"
'this for nt* computers
pi.FileName = "openfiles "
p.StartInfo = pi
p.StartInfo = pi
p.Start()
Dim sr As IO.StreamReader = p.StandardOutput
Dim sb As New System.Text.StringBuilder("")
Dim input As Integer = sr.Read
Do Until input = -1
sb.Append(ChrW(input))
input = sr.Read
Loop
MessageBox.Show(sb.ToString)
 
End Sub
 

 
adfg

GeneralWTF [modified]memberlexodus22 Feb '07 - 21:34 
The functionality is really good.
But there are some little changes you could do in relation to redundant Type Casts and CLS-Compliance.
 
@Edit; First post was much to excessive
GeneralRe: WTFmemberRichard Deeming23 Feb '07 - 0:31 
Well then, oh mighty coding genius, how would you have written this structure? Suspicious | :suss:
 
The Win9x version is defined as:
typedef struct _share_info_1 {
  char shi1_netname[LM20_NNLEN+1];
  char shi1_pad1;
  unsigned short shi1_type;
  char FAR* shi1_remark;
} _share_info_1;
 
The WinNT version is defined as:
typedef struct _SHARE_INFO_1 {
  LPWSTR shi1_netname;
  DWORD shi1_type;
  LPWSTR shi1_remark;
}
 
If you're so clever, let's see you do better.
 

"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

GeneralRe: WTFmemberlexodus23 Feb '07 - 3:30 
Uh, oh.
 
First; It wasnt my opinion to attack you, if i did this, sorry for that! I will change my comment to a not be that excessive. (Or kill it complete).
 
I was relating to the following code;
/// Share information level 1, Win9x
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=1)]
protected struct SHARE_INFO_1_9x
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=13)]
public string NetName;
public byte Padding;
public ushort bShareType;
 
[MarshalAs(UnmanagedType.LPTStr)]
public string Remark;
 
public ShareType ShareType
{
get { return (ShareType)((int)bShareType & 0x7FFF); }
}
}
 
There are some little things which could be done in a other way;
 
1. the type UShort ist not CLS compliance. I marked it as internal and tested it on Windows2000 case with all members as internal, works fine. --> There is no warranty that every .NET language can handle this type properly. (Visual Basic .NET for example)
http://dotnet.mvps.org/dotnet/articles/integeroperators/
 
2. There is no need to cast "bShareType & 0x7FFF" to an int and then cast it to ShareType (as i know an enum derives from int if you dont specify a other type).
I would advise you a plugin such as Resharper for VS2003, because the naked VS2003IDE is crap. (This plugin tells you that you have redundant Type casts) and does codeanalysis without starting the debugger. And it has a billion other cool features.
 
My code looks as follows (and all other Structs).
 
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=1)]
protected struct SHARE_INFO_1_9x
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=13)]
public string NetName;
public byte Padding;
internal ushort bShareType;
 
[MarshalAs(UnmanagedType.LPTStr)]
public string Remark;
public ShareType ShareType
{
get { return (ShareType)(bShareType & 0x7FFF); }
}
}
 
As you see, the code hasnt got many changes (my previous comment had to be a joke... it was a bad (joke sorry)).
 
Greetings
GeneralThanks a lot!memberlexodus22 Feb '07 - 5:42 
You are a genious, thank you for this perfect piece of source-code!
You just safed my life.
 
Where to donate @? Wink | ;-)
GeneralRe: Thanks a lot!membermrjmwcom3 Apr '07 - 7:53 
I want to say thank you too!! I hope there are no problems with it. I really just needed to convert a path to a unc path...
 
You were right on when you said "Two common requirements seem to have been missed from the .NET framework"
GeneralBug with IsValidFilePathmemberMonkeyget226 Jan '07 - 4:35 
I think there is a bug in the IsValidFilePath method. If the fileName is of length 1 or 2 it may throw an exception.
The precondition should check for such length instead of checking only for a length of zero.
GeneralRe: Bug with IsValidFilePathmemberRichard Deeming26 Jan '07 - 4:46 
Well spotted. D'Oh! | :doh:
 
Line 524 should be changed to read:
if (null == fileName || 3 > fileName.Length) return false;

 

"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

QuestionShow file list under UNC pathmemberjhtang22 Jan '07 - 9:18 
I try to show file information under UNC path on my web application, but I get " no network path was found." message.
Would you help me on this issue? Thank you very much.
 
DirectoryInfo CurrentRoot = new DirectoryInfo(@"\\servername\userdirectory") ;
FileSystemInfo[] files = CurrentRoot.GetFileSystemInfos() ;
FileSystemInfosExtend FileInfosEx = new FileSystemInfosExtend(files) ;

 
jtang
AnswerRe: Show file list under UNC pathmemberRichard Deeming26 Jan '07 - 4:55 
If the servername and share exist, the most likely problem is that the ASP.NET application doesn't have permission to access the share. You either need to grant the user account access, or make ASP.NET impersonate an account which does have access.
 
http://support.microsoft.com/kb/891031/[^]
 

"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 12 Nov 2003
Article Copyright 2002 by Richard Deeming
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid