Interop.activeDS headache with Sharepoint v2 webpart

F

Fowler

I've been working on a web part that will remind a user how many days
they have until their password expires. I strong named it and created a
custom security policy to allow it to live in the bin rather than the
GAC. After many painful iterations with configuring code access levels
and active directory and such, I was able to retrieve some public AD
info and I was ready to add the magic code that crunches the numbers. I
added a COM reference to the interop.activeds*. Upon compiling, it said
that com object (not the web part) wasn't strong named.

How do I strong name an external com object that is included as a
reference in a web part that's already strong named? I tried adding the
com object dll to the GAC but it won't allow it without a stong name
either.

*ActiveDs needed to handle 'LargeInteger' type

Using .Net 2003 and Sharepoint v2
 
J

Joe Kaplan \(MVP - ADSI\)

If you just need the large integer, you can get around this in three ways:
Use the DirectorySearcher to get the value instead of the DirectoryEntry as
it returns Int64
Use reflection to get the value (sample below)
Implement your own interop types in your assembly that just do
IADsLargeInteger

The first is the easiest (I think).

The second is done like this (VB.NET sample; you can translate to C# if need
be)

Public Shared Function GetInt64FromLargeInteger(ByVal largeInteger
As Object) As Long
Dim lowPart As Integer
Dim lBytes() As Byte
Dim highPart As Integer
Dim hBytes() As Byte
Dim valBytes(7) As Byte

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)

'this works
lBytes = BitConverter.GetBytes(lowPart)
hBytes = BitConverter.GetBytes(highPart)

lBytes.CopyTo(valBytes, 0)
hBytes.CopyTo(valBytes, 4)

longVal = BitConverter.ToInt64(valBytes, 0)

Return longVal

Catch e As MissingMethodException
Throw New ArgumentException("Invalid COM object passed as
parameter. Object must be IADsLargeInteger.", e)
End Try
End Function


For your own interop type, you can do this:

<ComImport(), Guid("9068270b-0939-11D1-8be1-00c04fd8d503"),
TypeLibType(4160), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)>
_
public interface IADsLargeInteger
<DispId(2)> _
property HighPart as int32
<DispId(3)> _
property LowPart as int32
end interface

Note that this doesn't give you a CoClass for creation, but you can read
from it.

HTH,

Joe K.
 
F

Fowler

Thanks for your reply. To give you a little more info for the purpose of
asking a follow up, i am trying to take the big value that represents
the password last set value (127433834335468750) and convert it to
something workable.

I understand that this active directory property is an 8 byte integer
representing seconds since Jan 1, 1970.

How would your code example change for an 8 byte integer rather than a
large integer?
 
J

Joe Kaplan \(MVP - ADSI\)

8 byte integer = Int64 = Large Integer in this case. They are all the same,
just different words for the same thing.

The documentation is incorrect in many places in that the value is actually
the number of 100 nanosecond intervals from 1/1/1600. It is also a FILETIME
structure.

In .NET, to convert to a date, you just do DateTime.FromFileTime(....) or
DateTime.FromFileTimeUtc(....) if you want the time in UTC.

Any of those code samples will get you the Int64. The reflection approach
uses late binding and the interop approach is just taking that one type out
of what is in Interop.ActiveDs and putting it in your own code so you don't
need to drag the extra assembly around.

The easiest way is to just use the DirectorySearcher and
ResultPropertyValueCollection. That looks like:

Int64 dateVal = (Int64) result.Properties["pwdLastSet"][0];
DateTime date;
if (dateVal > 0)
date = DateTime.FromFileTime(dateVal);
else
//it is either -1 or 0 which isn't a date value, but means that the
user's password never expires or they need to change password at next logon

HTH,

Joe K.
 
F

Fowler

That works! Thanks for helping me out. I now have a web part that
queries active directory (passing in the logon_user credential as the
search filter) and returns the password last set value that I convert to
a date using the solution described here. Then I can do some date
arithmetic to let the user know how long they have until they need to
change their password.

We have an extranet portal solution in place and our external users
occasionally run into a hassle if they allow their password to expire.
 
J

Joe Kaplan \(MVP - ADSI\)

Spiffy.

We have similar stuff as well. One thing that we do is have a batch process
that runs on a server that sends out email messages at 15, 5, 2 and 1 days
before expiration telling people they need to update their password on the
web site. Now that you know the "magic" for doing the calculations, you
might consider putting something like that together as well. For that case,
you just need to figure out the proper date values and then do a subtree
search in your AD for all users with pwdLastSet within that range.

Best of luck to you.

Joe K.
 

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

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top