Building A String Array Of Local User Names

At some point, you may want to build a string array that contains all your relevant machine local user account names. I came up against this problem this evening while I was working on some code for the MSPress OBA book. I thought it might save someone else some time if I demonstrated the solution.

I needed to implement this because it is helpful to use this type of approach when you are offering your users a customized interaction with Windows Services setup which can harvest the available account names on the machine, and allow the user something like a WinForm drop-down control of all the available accounts that are local. There are, of course, properties that you can build within the relevant Windows Services classes to put these types of credentials into the Windows Services without any method of user interaction. To be honest, I find that when developing something that a client might at a later date move from machine to machine in disparate environments, it is best to build the simplest interfaces possible in order for them to hit the ground running.

It is best to put this type of functionality in an internal sealed class, so that the class cannot be inherited (can’t be used as a base class throughout other sections of the application).

Let’s get the easy stuff over first. When we are working with something like the machine usernames, it is helpful to have a formatting method already out there and ready to go, just a small helper method. This is obviously just going to take the name that needs to be formatted as a parameter to the method, and is just going to use a simple if loop to test whether the username does actually require formatting.

  1. internal static string FormatUserName(string name)
  2. {
  3. if (name.IndexOf(@"\") >= 0)
  4. {
  5. return name;
  6. }
  7. return string.Format(@"{0}\{1}", Environment.MachineName, name);
  8. }

This is really nothing that is exceptionally fancy :-), however it does get the job done, and can be called later if, and when it is necessary to format returned user names appropriately.

Moving along, within our class, there are some DLLimports that have to be done in order to invoke some functionality. The DLLimport is going to allow us to call the relevant unmanaged code that is neccesary.

The primary assembly we require a reference to that contains the relevant unmanaged code is netapi32.dll, also known as the Microsoft Net API Library, is responsible for processing networking objects for Microsoft LAN Manager.

  1. [DllImport("netapi32.dll")]
  2. internal static extern uint NetUserEnum(IntPtr serverName, uint level, uint filter, ref IntPtr buffer, uint prefMaxLen, ref uint entriesRead, ref uint totalEntries, IntPtr resumeHandle);

If you would like, you can decorate this call much more throughly as well, if you have certain marshaling code standards that require adherence to.

  1. [DllImportAttribute("netapi32.dll", EntryPoint = "NetUserEnum", CharSet = CharSet.None, ExactSpelling = false, SetLastError = false, PreserveSig = true, CallingConvention = CallingConvention.Winapi, BestFitMapping = false, ThrowOnUnmappableChar = false)]
  2. [PreserveSigAttribute()]
  3. internal static extern uint NetUserEnum (IntPtr serverName, uint level, uint filter, ref IntPtr buffer, uint prefMaxLen, ref uint entriesRead, ref uint totalEntries, IntPtr resumeHandle);

We are also going to use this assembly again, in order to test the state of the buffer.

  1. [DllImport("netapi32.dll")]
  2. internal static extern void NetApiBufferFree(IntPtr bufptr);

As before, you can decorate this call much more exhaustively as well.

  1. [DllImportAttribute("netapi32.dll", EntryPoint = "NetApiBufferFree", CharSet = CharSet.None, ExactSpelling = false, SetLastError = false, PreserveSig = true, CallingConvention = CallingConvention.Winapi, BestFitMapping = false, ThrowOnUnmappableChar = false)]
  2. [PreserveSigAttribute()]
  3. internal static extern void NetApiBufferFree (IntPtr bufptr);

Lastly, there have to be a small struct, a nested type for the username.

  1. [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Auto)]
  2. internal struct USER_INFO_0
  3.  
  4. {
  5. public IntPtr lpszUserName;
  6. }

Lastly now, you can actually harvest the local user names, which was the subject of this post in the first place.

  1. internal static string[] LocalUserNames
  2. {
  3. get
  4. {
  5. USER_INFO_0_SIZE = sizeof(USER_INFO_0);
  6. IntPtr buffer = IntPtr.Zero;
  7. uint level = 0;
  8. uint filter = 0;
  9. uint entriesRead = 0;
  10. uint totalEntries = 0;
  11. uint prefMaxLen = uint.MaxValue;
  12. if (NetUserEnum(IntPtr.Zero, level, filter, ref buffer, prefMaxLen, ref entriesRead, ref totalEntries, IntPtr.Zero) != 0)
  13. {
  14. return new string[0];
  15. }
  16. string[] textArray = new string[totalEntries];
  17. for (int i = 0; i < totalEntries; i++)
  18. {
  19. int cCount = buffer.ToInt32() + (USER_INFO_0_SIZE * i);
  20. USER_INFO_0 user_info_ = (USER_INFO_0) Marshal.PtrToStructure(new IntPtr(cCount), typeof(USER_INFO_0));
  21. textArray[i] = Marshal.PtrToStringAuto(user_info_.lpszUserName);
  22. }
  23. NetApiBufferFree(buffer);
  24. return textArray;
  25. }
  26. }

And you’re done. Now you can make your Windows Service installers not suck so much, and a lot more intuitive to users than just guessing what account they should running under, or assuming the installing user knows how to use the services MMC snap-in.

share save 171 16 Building A String Array Of Local User Names

2 Comments

  1. Rushikesh says:

    Hi,
    I am facing some problem using the net api for resolving well-known port numbers to the service name.

    It would be great if you could send me some information on the same.

    Regards,
    Rushikesh

  2. Adam Buenz says:

    Wouldn’t you have to use methods like:

    GetTcpTable()

    AllocateAndGetTcpExTableFromStack()

    GetExtendedTcpTable()

    which would allow you to display the relevant ports consumed by a process?

Trackbacks/Pingbacks

  1. Links (6/12/2007) « Steve’s SharePoint Stuff - [...] Building A String Array Of Local User Names [...]

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>