it has something to do with the userAccountControl but I
don't get it to work in C#
Johan.
Its a bit more complicated that. If you set the LockOutTime attribute to 0, then the account is unlocked. If the Lockouttime value is non zero, then the account is
locked. I'm working on a simple example from C#. I'll post the code sometime tomorrow, 1/23/03.
Sincerely,
Max Vaughn [MS]
Microsoft Developer Support
Disclaimer: This posting is provided "AS IS" with no warranties, and confers no rights. You assume all risk for your use.
I've been doing some testing on my locale domain. When an account is locked out, the LockoutTime is set to the time the account is locked. I think we all
know that one. The ADU&C will reset this value to 0 to unlock the account. Checking the bit is unreliable at best. The bit in user account control is not
automatically updated. This is a known issue, and most likely will not be fixed.
The next question is, how can you quickly tell if a user account is locked? Check the Lockouttime, any value greater than 0, means the account is locked.
Another question is, how can you find all the user's whose accounts are locked? If an account has never been locked, it could have a value of -1 or nothing
in the lockouttime attribute. If the account has ever been unlocked, it will have a value 0 ( that is if the account has been unlocked using ADU&C. If you use
code, you could easily remove the attribute from the user object, you would a "Not Set" for a value in ADSI Edit). How do can you setup an LDAP query to
locate all the user accounts that are locked? Well, this is were it gets interesting. You can do an exact match search on the interger8 value, but I have not
been able to find a way to a ">" search. You can do an existence search for Lockouttime values and get back all of the accounts were Lockouttime has a
value ( Lockouttimne=* ), as I stated earlier, this is not a guarantee that the account is actually locked. However, checking the value of the attribute will
definitively tell if the account is locked. Then you could reset all the accounts that have a value of < 0 or = 0 to be unset ( invoking IADs::PutEx on the user
object).
The final questions is, how can you test this attribute for a value in c#? Since the ADSI property cache returns the value as an IADsLargeInteger, you must
use a CCW to instantiated a LargeInteger object in C#. This can be done by including Activeds.dll in your C# project references. Below is a simple C#
example of how to read the value. The next logical step is to stuff the IADsLargeInteger into a c# long and use the time conversion routines to retrieve an
actual time ( I'm leaving that as an excursus to the reader)
Hope this helps.
Sincerely,
Max Vaughn [MS]
Microsoft Developer Support
Disclaimer: This posting is provided "AS IS" with no warranties, and confers no rights. You assume all risk for your use.
using System;
using System.DirectoryServices;
//
// Remember to include "Active DS Type Library" on the COm Tab of the
// project References
//
using ActiveDs;
namespace AccountLockoutTime
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
static public bool IsAccountLocked( DirectoryEntry de )
{
LargeInteger oLI;
try
{
oLI = (LargeInteger)de.Properties["lockouttime"][0];
//
// At this point, we have a value. Check the lower DWORD to see if its is
//is non zero and not = to -1. If both conditions are true, then
// the account is locked.
//
uint l = (uint)oLI.LowPart;
if( (l > 0) & (oLI.LowPart != -1)) return true;
else return false;
}
catch( System.Exception e )
{
//
// Just want to catch the eception.
// If there is one, the account is not locked, reutrn False
// We could get a property not found in cache error because
// the value is not set in the general case. So, no value,
// account is not locked.
//
return false;
}
}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
DirectoryEntry de = new DirectoryEntry("LDAP://RootDSE");
DirectoryEntry dfDNC = new DirectoryEntry("LDAP://" + de.Properties["defaultnamingcontext"][0]);
DirectorySearcher oDS = new DirectorySearcher(dfDNC);
oDS.PageSize = 1000;
oDS.SearchScope = System.DirectoryServices.SearchScope.Subtree;
oDS.Filter = "(&(objectclass=user)(objectcategory=person)(LockoutTime=*))";
oDS.PropertiesToLoad.Add("distinguishedname");
oDS.PropertiesToLoad.Add("lockouttime");
SearchResultCollection oResCol = oDS.FindAll();
//
// This search will find all the users who have a value in LockOutTime
// You would need to check the value of LockoutTime using the LargeInteger type delcared
// in ACTiveDs.dll to determine if the value is actually a time. Checking the LargeInteger.LowPart
// to see if it is > 0 would be sufficient. If the value is negative 1, the lowpart will contain
// all 1's.
//
foreach( SearchResult oRes in oResCol )
{
Console.Write( oRes.Properties["distinguishedname"][0] );
//
// A better way would be to check the LockOutTime directly, just wanted to illustrate
// how to check the value form a directory entry
//
DirectoryEntry oResDE = oRes.GetDirectoryEntry();
if( IsAccountLocked( oResDE ) )Console.WriteLine(" is Locked Out.");
else Console.WriteLine(" is not Locked out.");
}
//
// Must dispose of the results the collection or we will leak LDAP connnections
// and system resources.
//
oResCol.Dispose();
//
// This is the way to unlock a user account the same way the ADU&C does, by setting the Lockouttime
// to 0
//
DirectoryEntry oD = new DirectoryEntry("LDAP://cn=Ford Prefect,ou=Hitch Hikers,dc=br549,dc=nttest,dc=microsoft,dc=com");
ActiveDs.LargeInteger oVal;
oVal = (LargeInteger)oD.Properties["LockoutTime"][0];
oVal.HighPart = 0;
oVal.LowPart = 0;
oD.Properties["LockoutTime"][0] = oVal;
oD.CommitChanges();
}
}
}
Public Shared Function GetDateFromLargeInteger(ByVal largeInteger As Object)
As Date
Try
Return Date.FromFileTime(GetInt64FromLargeInteger(largeInteger))
Catch e As ArgumentOutOfRangeException
Throw New ArgumentException("IADsLargeInteger value was not a valid
date value.", e)
End Try
End Function
Public Shared Function GetInt64FromLargeInteger(ByVal largeInteger As
Object) As Long
Const highShift As Long = &H100000000
Dim lowPart As Integer
Dim highPart As Integer
Dim longVal As Long
Dim largeIntType As Type
largeIntType = largeInteger.GetType()
Try
highPart = CType(largeIntType.InvokeMember("HighPart",
BindingFlags.GetProperty Or BindingFlags.Public, Nothing, largeInteger,
Nothing), Integer)
lowPart = CType(largeIntType.InvokeMember("LowPart",
BindingFlags.GetProperty Or BindingFlags.Public, Nothing, largeInteger,
Nothing), Integer)
longVal = (highPart * highShift) + lowPart 'my kingdom for a shift
operator in VB...
Return longVal
Catch e As MissingMethodException
Throw New ArgumentException("Invalid COM object passed as parameter.
Object must be IADsLargeInteger.", e)
End Try
End Function
In C#, you could use the real shift operator in the code above. These
functions require you to import System.Reflection.
Another way you can get the Int64 value is to use the DirectorySearcher.
The ResultPropertyValueCollection converts LargeInteger to Int64. However,
I believe the current implementation has a bug in it where the HighPart
value is not properly shifted to the high bits and the two values are just
ORed together, yielding the incorrect number. Perhaps someone at MS could
verify this?
Cheers,
Joe K.
"Max L. Vaughn" <ma...@online.microsoft.com> wrote in message
news:L4eKsYuwCHA.3284@cpmsftngxa06...
Here is a new C# code:
using System;
using System.DirectoryServices;
//
// Remember to include "Active DS Type Library" on the COm Tab of the
// project References
//
using ActiveDs;
namespace AccountLockoutTime
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
//
// COnvert an IADsLargeInteger to a DateTime structure
//
static public DateTime LargeIntegerToDateTime( LargeInteger oLI )
{
long lTime;
lTime = (long)(oLI.HighPart);
lTime <<= 32;
lTime |=(uint)oLI.LowPart;
DateTime lockTime = System.DateTime.FromFileTime( lTime);
return lockTime;
SearchResultCollection oResCol = oDS.FindAll();
//
// This search will find all the users who have a value in LockOutTime
// You would need to check the value of LockoutTime using the LargeInteger type delcared
// in ACTiveDs.dll to determine if the value is actually a time. Checking the LargeInteger.LowPart
// to see if it is > 0 would be sufficient. If the value is negative 1, the lowpart will contain
// all 1's.
//
foreach( SearchResult oRes in oResCol )
{
Console.Write( oRes.Properties["distinguishedname"][0] );
//
// Use the Direcctory Entry object to retrive the LockOutTime.
//
if( IsAccountLocked( oRes.GetDirectoryEntry() ) )
{
Console.WriteLine(" was locked out at " + LargeIntegerToDateTime( (LargeInteger)oRes.GetDirectoryEntry().Properties["LockoutTime"]
[0]).ToString());
}
else Console.WriteLine(" is not Locked out.");
}
//
// Must dispose of the results the collection or we will leak LDAP connnections
// and system resources.
//
oResCol.Dispose();
//
// This is the way to unlock a user account the same way the ADU&C does, by setting the Lockouttime
// to 0
//
DirectoryEntry oD = new DirectoryEntry("LDAP://cn=Ford Prefect,ou=Hitch Hikers,dc=br549,dc=nttest,dc=microsoft,dc=com");
ActiveDs.LargeInteger oVal;
oVal = (LargeInteger)oD.Properties["LockoutTime"][0];
oVal.HighPart = 0;
oVal.LowPart = 0;
oD.Properties["LockoutTime"][0] = oVal;
oD.CommitChanges();
}
}
}