|
Get user account SID and domain name
In our earlier article, How to get user SID using
DirectoryServices classes, we showed you how to use DirectoryServices
classes to do it. Here is another technique but this time it will not use DirectoryServices,
but it uses PInvoke/Interop to call LookupAccountName Win32
API. Following code shows the prototype that you will need to define to call
the API.
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace PardesiServices.SecurityInterop
{
[ StructLayout( LayoutKind.Sequential )]
public struct SID_IDENTIFIER_AUTHORITY
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=6)]
public byte[] Value;
};
public class SecurityApiWin32
{
[DllImport("Advapi32.dll", SetLastError=true)]
public static extern int LookupAccountName(
string ServerName,
string AccountName,
IntPtr Sid,
ref int SidSize,
StringBuilder DomainName,
ref int DomainNameSize,
ref short SidUse);
}
}
Then you can call this prototype to get the user's domain, SID and type of
account. Remeber that every account created under windows in User account.
It can be of different type i.e. User or Group. There is one thing that we
would like to point out. The buffer for variables Sid and DomainName
are to be supplied by the caller and they should be of size to accomodate all
the information that API needs to return. If you pass the values for SidSize
and DomainNameSize variables as ZERO initially, then the API fills
these variables with the actual required sizes. And then in next call you can
allocate the memory buffer both the variables and get the required information.
The SID value is returned as IntPtr. To convert this into
string representation. You can either use Win32 ConvertSidToString
API using PInvoke or the utility function ConvertByteToStringSid that we
wrote that converts Byte array into text string. We prefer to use
ConvertByteToStringSid because it is guranteed to work on every
platform whereas ConvertSidToString is only available on Windows
2000 and higher. And the other advantage is that you will not incur another
PInvoke context switch overhead for second call.
public bool LookupAccountName(string strServer, string strAccountName,
out string accountSid, out string strDomainName,
out short AccountType)
{
bool bRet = false;
int lSidSize = 256;
int lDomainNameSize = 256;
accountSid = "";
strDomainName = "";
AccountType = 0;
StringBuilder strName;
lSidSize = 0;
IntPtr Sid = IntPtr.Zero;
// First get the required buffer sizes for SID and domain name.
int nRet = SecurityApiWin32.LookupAccountName(
strServer,
strAccountName,
Sid,
ref lSidSize,
null,
ref lDomainNameSize,
ref AccountType);
bRet = (0 != nRet);
if (!bRet)
{
int nErr = Marshal.GetLastWin32Error();
if (122 == nErr) // Buffer too small
{
// Allocate the buffers with actual sizes that are required
// for SID and domain name.
strName = new StringBuilder(lDomainNameSize);
Sid = Marshal.AllocHGlobal(lSidSize);
nRet = SecurityApiWin32.LookupAccountName(
strServer,
strAccountName,
Sid,
ref lSidSize,
strName,
ref lDomainNameSize,
ref AccountType);
bRet = (0 != nRet);
if (bRet)
{
byte [] sidArray = new byte[lSidSize];
strDomainName = strName.ToString();
Marshal.Copy(Sid, sidArray, 0, lSidSize);
accountSid = ConvertByteToStringSid(sidArray);
Console.WriteLine("Domain Name: {0}", strDomainName);
Console.WriteLine("Account Sid: {0}", accountSid);
}
}
else
{
Console.WriteLine(nErr);
}
}
Marshal.FreeHGlobal(Sid);
return bRet;
}
|