|
No problem.
BTW: Love the signature...
|
|
|
|
|
Thanks,
I just have a quick qeustion, any idea how I can retrieve this when im logged in but offline?
I'm using this on a tablet and when I'm offsite I can still log in with no network connection. I know the info is somewhere as windows displays my full name on the start menu.
All of the biggest technological inventions created by man - the airplane, the automobile, the computer - says little about his intelligence, but speaks volumes about his laziness. ~Mark Kennedy
|
|
|
|
|
I would recommend caching the display name somehow when you are connected. Perhaps you could add and application setting for the display name and store the last found display name in it. Then if you get an exception (because you're offline) you can use the cached name. If you've never been able to get the display name you can default back to the System.Environment.UserName.
IF you can't cache the name:
There are other things you could try as well. For example, in another comment on my article, andre12345 gives a (longer) alternative method using WMI. You can also hook in to the "GetUserNameEx" method in the secur32.dll Win32 DLL. It gets a bit hairy wrapping the Win32 method in .NET if you've never done it before but here's an example:
[DllImport("secur32.dll", CharSet = CharSet.Auto)]
public static extern int GetUserNameEx(int nameFormat, StringBuilder userName, ref int userNameSize);
private void Form1_Load(object sender, EventArgs e)
{
label1.Text = GetUserDisplayName();
}
private static string GetUserDisplayName()
{
string userName = Environment.UserName;
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
StringBuilder buffer = new StringBuilder(1024);
int bufferSize = buffer.Capacity;
int returnValue = GetUserNameEx((int)EXTENDED_NAME_FORMAT.NameDisplay, buffer, ref bufferSize);
if (returnValue != 0)
{
userName = buffer.ToString();
}
}
return userName;
}
The (int)EXTENDED_NAME_FORMAT.NameDisplay is an Enum that looks like this:
enum EXTENDED_NAME_FORMAT
{
NameUnknown = 0,
NameFullyQualifiedDN = 1,
NameSamCompatible = 2,
NameDisplay = 3,
NameUniqueId = 6,
NameCanonical = 7,
NameUserPrincipal = 8,
NameCanonicalEx = 9,
NameServicePrincipal = 10,
NameDnsDomain = 12
}
The enum makes the code more self documenting but you could just as easily do:
int returnValue = GetUserNameEx(3, buffer, ref bufferSize);
So if you absolutely need to get the name offline and can't cache it anywhere you can use this method. It turns the nice clean 1-liner into probably 10 or more lines of code but it works offline whether or not you're joined to a domain.
|
|
|
|
|
One more thing, I forgot to mention that you need an Imports or using statement for System.Runtime.InteropServices to use the DllImport attribute on the method.
|
|
|
|
|
Thanks for the help, I really appreciate it!
That solved my problem; I recoded it to VB.net and stuck it in a module.
Module User
<DllImport("secur32.dll")> _
Public Function GetUserNameEx(ByVal nameFormat As Integer, ByVal userName As StringBuilder, ByRef userNameSize As Integer) As Integer
End Function
Enum EXTENDED_NAME_FORMAT
NameUnknown = 0
NameFullyQualifiedDN = 1
NameSamCompatible = 2
NameDisplay = 3
NameUniqueId = 6
NameCanonical = 7
NameUserPrincipal = 8
NameCanonicalEx = 9
NameServicePrincipal = 10
NameDnsDomain = 12
End Enum
Function GetUserDisplayName() As String
Dim userName As String = Environment.UserName
If Environment.OSVersion.Platform = PlatformID.Win32NT Then
Dim buffer As StringBuilder = New StringBuilder(1024)
Dim bufferSize As Integer = buffer.Capacity
Dim returnValue As Integer = GetUserNameEx(CInt(EXTENDED_NAME_FORMAT.NameDisplay), buffer, bufferSize)
If returnValue <> 0 Then
' GetUserNameEx was successful
userName = buffer.ToString()
End If
End If
Return userName
End Function
End Module
All of the biggest technological inventions created by man - the airplane, the automobile, the computer - says little about his intelligence, but speaks volumes about his laziness. ~Mark Kennedy
|
|
|
|
|
I am no expert, but I use the following:
Based on the following link:
Simple Active Directory Browser Dialog
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
private bool SearchFullName(string strUserName, ref string strFullName)
{
const string connectionPrefix = "LDAP://";
const string userName = null;
const string password = null;
bool bFlag = false;
string OuDn = "balleysoft.com";
try
{
DirectoryEntry directoryObject = new DirectoryEntry(
connectionPrefix + OuDn,
userName,
password);
foreach (DirectoryEntry child in directoryObject.Children)
{
DirectorySearcher Searcher = new DirectorySearcher(child);
Searcher.PropertiesToLoad.AddRange(new string[]
{ "cn", "mail", "Name", "displayName", "samaccountname" });
Searcher.Filter = "(&(objectClass=person)(sAMAccountName=" +
strUserName + "))";
SearchResultCollection Results = Searcher.FindAll();
foreach (SearchResult result in Results)
{
try
{
strFullName = result.Properties["cn"][0].ToString();
bFlag = true;
}
catch
{
strFullName = "<not found>";
}
}
}
}
catch
{
strFullName = "<not found>";
}
return bFlag;
}
|
|
|
|
|
Wow, see how complicated that is! It's so much easier to just call "Sytstem.DirectoryServices.AccountManagement.UserPrinciple.Current.DisplayName". That's all you have to do and you're done. Why write 20 lines of code when you can do it with 1 and wrap it in a Try..Catch block??
|
|
|
|
|
Thanks for saving me from doing this legwork. I, also, am needing this type of information for one of my projects. Article was short and to the point.
Programming is an art form that fights back.
|
|
|
|
|
Be aware that you can get a PrincipalServerDown exception if you're not connected to your network. This can also happen if the user is not properly configured in the Active Directory (only pre-windows 2000 compatible name set)
Friedrich
|
|
|
|
|
Thanks for the first constructive reply I've had. I did some testing and you will not receive the PrincipleServerDown exception if you're not logged in as a domain user but will if you are. I also found that I should be using UserPrinciple.Current.DisplayName instead of UserPrinciple.Current.Name.
Can you elaborate on "only pre-windows 2000 compatible name set"? Are you talking about the domain functional level? NT server?
I really doesn't matter though, the takeaway is to make sure and wrap this in a Try...Catch block and if you get an exception, just get the username instead (System.Environment.UserName or My.User.Name in VB).
I'll update the article to clarify the exception handling. Thanks!
|
|
|
|
|
Hi,
what happens is that you get this exception when your machine is part of a domain, you're logged in with a domain user account, and you have disabled your network connection (either via software (disabled network adapter), or by plugging out the network cable). In this case, LoginUser() will still authenticate against cached credentials, but you won't get the user name. What we do in our application is to cache the full user name in a config file, and use the cached copy when the DomainController is not available. In a past era, I had written C++ code using the Net* API to get the full user name: (this old way of doing things may be more reliable)
BOOL CShowActiveDirUsers::GetFullUserName(LPTSTR szUser, LPTSTR szDomain,
LPTSTR szFullUserName, LPTSTR szDescription)
{
BOOL bResult = FALSE;
LPWSTR pServer = NULL;
BOOL bLocal = FALSE;
TCHAR szComputerName[_MAX_PATH];
TCHAR szDomainName[_MAX_PATH];
TCHAR szServer[_MAX_PATH];
TCHAR szPDC[_MAX_PATH];
DWORD dwLevel = 10;
LPUSER_INFO_10 pUserInfoBuffer = NULL;
ZeroMemory(szComputerName, _MAX_PATH);
ZeroMemory(szDomainName, _MAX_PATH);
ZeroMemory(szServer, _MAX_PATH);
ZeroMemory(szPDC, _MAX_PATH);
GetLocalComputerName(szComputerName);
if (_tcsicmp(szComputerName, szDomain) == 0)
bLocal = TRUE;
if (!bLocal)
{
GetDCName(szPDC, szDomain);
if (_tcschr(szUser, AT) )
{
GetUserAndDomainNameFromUPN(szUser, szFullUserName, szDomainName);
_tcscpy(szUser, szFullUserName);
_tcscpy(szDomain, szDomainName);
}
}
else
{
NetApiBufferAllocate(_MAX_PATH, (LPVOID*)&pServer);
ZeroMemory(pServer, _MAX_PATH);
_tcscpy(pServer, DOUBLE_BACKSLASH);
_tcscat(pServer, szComputerName);
}
if (NERR_Success == NetUserGetInfo(szPDC, szUser, dwLevel,
(LPBYTE*)&pUserInfoBuffer))
{
if (pUserInfoBuffer)
{
_tcscpy(szFullUserName, pUserInfoBuffer->usri10_full_name);
_tcscpy(szDescription, pUserInfoBuffer->usri10_comment);
bResult = TRUE;
}
}
if (pUserInfoBuffer)
NetApiBufferFree(pUserInfoBuffer);
if (pServer)
NetApiBufferFree(pServer);
return bResult;
}
Having said all of this, I noticed that our friend UserPrinciple.Current.Name cam also fail if the domain user is not setup properly:
To see this, you need domain admin priviliges on the domain under question, and you need to use Active Directory Users and Computers control panel applet (under Administration) to edit the user. The Active directory management tools can be gotten from Microsoft, installed, then they appear in the control panel.
You then go to the user properties User Properties->Account page. Here you get two entries
Editbox (for username) Combobox (containing @yourdomain.com)
as well as
User Logon Name Pre Windows 2000
(Grayed out edit box containg YOURDOMAIN\) and Edit box for the username.
It is possible to clear out the top entry user@yourdomain.com and have only the bottom entry set YOURDOMAIN\user. In this scenario, you do get the exception.
You also get the exception if your primary domain controller runs out of disk space. How do I know? It happenend on our Primary Domain controller, and we got the exception.
Testing this is pretty complicated, because you really don't know what will happen in any particular network setup and configuration. I have found that Active Directory in Native mode (no backwards compatibility to NT4 style domain controllers) can behave differently than Native Mode Active directory installations. This setting (at least used to be) available only when you installed the Active Directory server.
Sorry for the long post - Anyways- I do think its good that you wrote, it does get indexed by Google, and this helps people looking for answers.
Friedrich Brunzema
|
|
|
|
|
I like the idea of caching the name. You could very simply store the name in an application setting. That way if it does fail, you catch it and just use the application setting instead and maybe silently log the failed query somewhere.
Out of disk space causes this error huh? Have fun trying to track that one down. I suppose running out of disk space on a DC would cause a lot of unexpected errors (they must not test that scenario very well at M$).
Thanks for the info.
|
|
|
|
|
Hey great article...nice, clear, short and simple. Always amusing to watch people criticize decent articles like this when you see all the crap and broken english that spams up here these days...
I'm not sure what the difference is between using this approach rather than System.Environment.UserName and System.Environment.UserDomainName - can anyone explain?
|
|
|
|
|
Thanks for the reply. The .UserDomainName gives you the domain name for the currently logged in user and .UserName gives you the same thing as VB's My.User.Name (MyDomain\Username). This approach actually gets the user's name so instead of "MyDom\jdoe" you get "John Doe"!
|
|
|
|
|
I'm the kind of guy who doesn't always need a tutorial. The hint was enough for me to move forward! Thanks for taking the time!
|
|
|
|
|
|
only link's to MSDN ... where is the important info ?
|
|
|
|
|
As I've explained, this namespace isn't automatically referenced for new projects and finding this gem took me some time. If you would've took the time to read my replies to the other posts you'd see that I'm trying to make it easier to find. You didn't find it useful because you weren't looking for a way to get a user's full name. Don't vote an article as useless just because YOU have no use for it.
|
|
|
|
|
Country Man[^] is a troll/uni-voter and it has been requested in Bugs/Sugs to remove/deactivate their account See here.[^]
|
|
|
|
|
There is so much more possible from this API than just "Get a User's Full Name". You should consider expanding it.
only two letters away from being an asset
|
|
|
|
|
If people open an article named "Get a User's Full Name", then they've found what they're looking for. Obviously you can use the namespace for so much more. I've been thinking about creating a blog so maybe that's a better medium for this kind of stuff. Thanks for the comment though.
|
|
|
|
|
That's the point, if someone is loking for information about System.DirectoryServices.AccountManagement, and article titled "Get a User's Full Name" will be ignored, not opened to see what else is in it.
only two letters away from being an asset
|
|
|
|
|
Well, ignore it then lol. There's nothing in there about anything but getting a user's name since that's all I needed it for. I didn't know people here would be opposed to posting a simple article on a particular feature of the framework.
There's already a whole article on the namespace here: Using System.DirectoryServices.AccountManagement[^]
But it doesn't mention anything about the .Current.Name property there either. My whole point is that this information is not extremely easy to find so I wanted to put it out there.
So anyways, in the future I guess I'll reserve this kind of stuff for a blog...
|
|
|
|
|
jabit wrote: I didn't know people here would be opposed to posting a simple article on a particular feature of the framework.
I'm not opposing, I'm trying to encourage more from you because it has more potential. However, yes the other article does cover more than just the one method you were interested in.
only two letters away from being an asset
|
|
|
|
|
Not much here; no code involved; better as a blog entry.
|
|
|
|
|