How do you retrieve PasswordLastChanged using C#?

J

John Smith

I am converting my old ASP domain account manager to .Net / C# and having
problems returning some of the properties such as "PasswordLastChanged" and
"AccountExpirationDate". These properties are provided by the NT provider
and not LDAP, if I'm not mistaken. In my ASP code, I simply use the
following code to return the values:

Set objUser = GetObject("WinNT://domain/joe_user")
lastChanged = objUser.PasswordLastChanged
acctExpire = objUser.AccountExpirationDate
Set objUser=Nothing

So, in my C# code, I'm calling the WinNT provider as follows:

DirectoryEntry deMain = new DirectoryEntry();
deMain.Path = "WinNT://domain/"+Request.QueryString["userid"];

and I'm trying to retrive the properties with:

strLastChanged = deMain.Properties["PasswordLastChanged"][0].ToString();
strAcctExpire = deMain.Properties["PasswordLastChanged"][0].ToString();

but it's returning this error:

System.ArgumentOutOfRangeException: Specified argument was out of the range
of valid values. Parameter name: Index was out of range. Must be
non-negative and less than the size of the collection

The ASP code works fine. Any help is greatly appreciated.

Thanks
 
W

Willy Denoyette [MVP]

PasswordLastChanged is an ADSI property, and is NOT supported by the WinNT
provider (so your asp code cannot work).
You can use the LDAP provider and read the "pwdLastSet" attribute, or you
can use reflection to late bind to the provider and use the
PasswordLastChanged property (Note: VB.NET has late binding support in the
language, so you don't need to import activeds.tlb and you can use the
property syntax xxx = user.PasswordLastChanged).

Next illustrates the first option....

using ActivedsNamespaceHere; //imported namespace from activeds.tlb
(You need to set a reference to activeds.tlb)
....

DirectoryEntry user = new DirectoryEntry("LDAP://xxxxx/...............
LargeInteger li; //ILargeInteger COOM interface - import
activeds.tlb
li = user.Properties["pwdLastSet"].Value as LargeInteger;
long date = (((long)(li.HighPart) << 32) + (long) li.LowPart);
string dt = DateTime.FromFileTime(date).ToString();
Console.WriteLine("DATE = {0}" ,dt)


Willy.
 
J

John Smith

Thanks Willy.

Just dumping code in:

lblPasswordLastChanged.Text =
deUser.Properties["pwdLastSet"].Value.ToString();

That gives me "System.__ComObject " as a result. At least it didn't fail!
:) I look into this.

What about for the AccountExpirationDate property? Do you know how I can
retrieve that? If I import this activeds.tlb into my c# app, will that
allow me to retrieve this propterty?

Thanks again.


Willy Denoyette said:
PasswordLastChanged is an ADSI property, and is NOT supported by the WinNT
provider (so your asp code cannot work).
You can use the LDAP provider and read the "pwdLastSet" attribute, or you
can use reflection to late bind to the provider and use the
PasswordLastChanged property (Note: VB.NET has late binding support in the
language, so you don't need to import activeds.tlb and you can use the
property syntax xxx = user.PasswordLastChanged).

Next illustrates the first option....

using ActivedsNamespaceHere; //imported namespace from activeds.tlb
(You need to set a reference to activeds.tlb)
...

DirectoryEntry user = new DirectoryEntry("LDAP://xxxxx/...............
LargeInteger li; //ILargeInteger COOM interface - import
activeds.tlb
li = user.Properties["pwdLastSet"].Value as LargeInteger;
long date = (((long)(li.HighPart) << 32) + (long) li.LowPart);
string dt = DateTime.FromFileTime(date).ToString();
Console.WriteLine("DATE = {0}" ,dt)


Willy.

John Smith said:
I am converting my old ASP domain account manager to .Net / C# and having
problems returning some of the properties such as "PasswordLastChanged"
and
"AccountExpirationDate". These properties are provided by the NT provider
and not LDAP, if I'm not mistaken. In my ASP code, I simply use the
following code to return the values:

Set objUser = GetObject("WinNT://domain/joe_user")
lastChanged = objUser.PasswordLastChanged
acctExpire = objUser.AccountExpirationDate
Set objUser=Nothing

So, in my C# code, I'm calling the WinNT provider as follows:

DirectoryEntry deMain = new DirectoryEntry();
deMain.Path = "WinNT://domain/"+Request.QueryString["userid"];

and I'm trying to retrive the properties with:

strLastChanged = deMain.Properties["PasswordLastChanged"][0].ToString();
strAcctExpire = deMain.Properties["PasswordLastChanged"][0].ToString();

but it's returning this error:

System.ArgumentOutOfRangeException: Specified argument was out of the
range
of valid values. Parameter name: Index was out of range. Must be
non-negative and less than the size of the collection

The ASP code works fine. Any help is greatly appreciated.

Thanks
 
W

Willy Denoyette [MVP]

John,
Inline ***
Willy.

John Smith said:
Thanks Willy.

Just dumping code in:

lblPasswordLastChanged.Text =
deUser.Properties["pwdLastSet"].Value.ToString();

That gives me "System.__ComObject " as a result. At least it didn't
fail!
:) I look into this.

*** That's why you need to set a reference to activeds.tlb (or run
tlbimp.exe to create the interop assembly), the __ComObject type is a
Dispatch COM interface, you need to cast this to the LargeInteger class
available through the interop assembly.

LargeInteger li = user.Properties["pwdLastSet"].Value as LargeInteger;
Convert the highorder/loworder parts to a long
long date = (((long)(li.HighPart) << 32) + (long) li.LowPart);
and convert it from FileTime to DateTime
string dt = DateTime.FromFileTime(date).ToString();

What about for the AccountExpirationDate property? Do you know how I can
retrieve that? If I import this activeds.tlb into my c# app, will that
allow me to retrieve this propterty?
***
Yes, using same code as above, but now retrieving the "accountExpires"
property.
 
J

John Smith

Cool. I got it working with your help. Below is the function that returns
the values if anyone is interested. If there are better ways, please let me
know.

Thanks again.

///////////////// BEGIN ////////////////
public string getProviderProps(string strProperty,string strADsPath) {

const int MAXPASSWDAGE = 90;

DirectoryEntry deMain = new DirectoryEntry();
deMain.Path = "LDAP://SERVERPATH/" + strADsPath;
deMain.Username = "username";
deMain.Password = "password";

//get some properties and do things with them

//PasswordLastChanged
LargeInteger liPasswdLastSet = deMain.Properties["pwdLastSet"].Value as
LargeInteger;

//Convert the highorder/loworder parts to a long
long datePasswdLastSet = (((long)(liPasswdLastSet.HighPart) << 32) + (long)
liPasswdLastSet.LowPart);

//and convert it from FileTime to DateTime string
DateTime datePasswdLastSetVal = DateTime.FromFileTime(datePasswdLastSet);
///////////////////////////////////////////////////////////

//AccountExpiration
//return the expiration date if account is set to expire.
//accounts not set to expire will have 12/31/1600 7:00:00 PM

LargeInteger liAcctExpiration = deMain.Properties["accountExpires"].Value as
LargeInteger;

//Convert the highorder/loworder parts to a long
long dateAcctExpiration = (((long)(liAcctExpiration.HighPart) << 32) +
(long) liAcctExpiration.LowPart);

//and convert it from FileTime to DateTime
string strAcctExpiration =
DateTime.FromFileTime(dateAcctExpiration).ToString();
///////////////////////////////////////////////////////////

switch (strProperty) {

case "passwdexpirdate":
return datePasswdLastSetVal.AddDays(MAXPASSWDAGE).ToString();
break;
case "passwdlastchanged":
return DateTime.FromFileTime(datePasswdLastSet).ToString();
break;
case "passwdage":
return ((int)((DateTime.Now.Ticks/1000 - datePasswdLastSetVal.Ticks/1000) /
(1000 * 3600 * 24))/10 ).ToString();
break;
case "accountexpiration":
if(strAcctExpiration.IndexOf("1600") > 0)
return "Account expiration not set";
else
return strAcctExpiration;
break;
default:
return "Internal System Failure";
break;

}

}





///////////////END ///////////////////







Willy Denoyette said:
John,
Inline ***
Willy.

John Smith said:
Thanks Willy.

Just dumping code in:

lblPasswordLastChanged.Text =
deUser.Properties["pwdLastSet"].Value.ToString();

That gives me "System.__ComObject " as a result. At least it didn't
fail!
:) I look into this.

*** That's why you need to set a reference to activeds.tlb (or run
tlbimp.exe to create the interop assembly), the __ComObject type is a
Dispatch COM interface, you need to cast this to the LargeInteger class
available through the interop assembly.

LargeInteger li = user.Properties["pwdLastSet"].Value as LargeInteger;
Convert the highorder/loworder parts to a long
long date = (((long)(li.HighPart) << 32) + (long) li.LowPart);
and convert it from FileTime to DateTime
string dt = DateTime.FromFileTime(date).ToString();

What about for the AccountExpirationDate property? Do you know how I can
retrieve that? If I import this activeds.tlb into my c# app, will that
allow me to retrieve this propterty?
***
Yes, using same code as above, but now retrieving the "accountExpires"
property.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top