In this case, it is a raw binary security descriptor, so you cannot cast it
directly to the ADSI interface. You would need a way to marshal the raw
binary data into a security descriptor. If you are using .NET 2.0, there is
good SD support built in, but otherwise, something else is needed (perhaps
IADsSecurityUtility or something).
To answer your previous question, security descriptors and binary attributes
will be marshaled as byte[], so you can't assume than anything returned as
byte[] is an SD. It could also be a GUID, a SID, an X509 certificate, a
jpeg, etc. Without knowing the semantics of the attribute in question, you
can't really interpret it as anything other than raw binary.
The same goes for large integer. MOST of the attributes that use that
syntax store Windows FileTime, but some of them are just intervals
(maxPwdAge for example). Once again, without the semantics, you cannot make
assumptions.
The way tools like ldp.exe is by having a big hard-coded list of the
attributes and what the data means. This is how LDAP can take numeric
attributes and convert them back into enumerated constants such as for
userAccountControl. However, doing this with the entire AD schema
(thousands of attributes) is a daunting task.
If you just want raw strings and can use .NET 2.0, you might consider using
System.DirectoryServices.Protocols. It is an LDAP API wrapper, not ADSI, so
all of the marshaling of LDAP data to ADSI and COM datatypes is not done.
You get the raw LDAP data which is all strings.
HTH,
Joe K.
"Martin Robins" <martin dot robins at technicaldirect dot co dot uk> wrote
in message news:e6**************@TK2MSFTNGP14.phx.gbl...
I have just tried casting the byte[] value of the
"msexchmailboxsecuritydescriptor" property from the SearchResult object
returned by the DirectorySearcher search below to IADsSecurityDescriptor and
it gives me an error!
object value = searchResult.Properties[propertyName][i];
if (value is byte[]) {
IADsSecurityDescriptor securityDescriptor =
(IADsSecurityDescriptor)value;
...
}
It would appear that I did not understand you as well as I thought!
I was originally using "x = y as z" to cast the properties (which obviously
would not generate an exception), but the result was always null so the cast
was always failing; I chose to try this one because of the name of the
property which suggested to me that it might indeed be an
IADsSecurityDescriptor.
Can you provide any more pointers?
Cheers.
"Martin Robins" <martin dot robins at technicaldirect dot co dot uk> wrote
in message news:e3***************@TK2MSFTNGP12.phx.gbl...
This is starting to sound better!
Seeing as my warehouse will take all values as string and them convert them
later, it sounds like 2 of the four are covered automatically simply by
using the SearchResult.Properties[...].ToString() instead of the
DirectoryEntry.Properties. Similarly, if I encounter a data type of byte[]
in that property collection I can assume it to be IADsSecurityDescriptor?
As for the IADsLargeInteger, is it used for anything other than representing
dates? If not then I can simply DateTime.FromFileTime() with a try/catch for
duff values - if it is used for other things is there a way to determine the
usage further.
The code I presented was purely experimental to work out how I will be doing
this; I would prefer to avoid any checking of property names etc in order to
determine the result type if I can.
Your help is very much appreciated.
"Joe Kaplan (MVP - ADSI)" <jo*************@removethis.accenture.com> wrote
in message news:uD*************@TK2MSFTNGP15.phx.gbl...
There are essentially for AD attribute syntaxes that are marhaled by the
DirectoryEntry as System.__ComObject: large integer (IADsLargeInteger),
security descriptor (IADsSecurityDescriptor), DN with binary
(IADsDNWithBinary) and DN with string (IADsDNWithString). Note that the
last 2 are more rare, with DN with string being incredibly rare since the
default 2003 and Exchange schemas don't use that syntax at all.
That said, you can either "know" what syntax the attribute in question is
and convert it or you can do type comparisons are runtime. Your code below
is using the approach of expecting attributes with certain names to have
certain syntaxes. This doesn't necessarily scale well, but you can cover
the special cases you are interested in this way pretty easily.
Also, if you use the DirectorySearcher to retrieve the attribute value, you
will not have this marshaling issue. The DirectorySeacher marshals large
integer as Int64, SD and byte[] and the other two as string. This is
because it converts data directly from the low level ADSI type system
instead of using the default COM automation level marshaling that the
DirectoryEntry uses.
There really isn't any need to use the NativeObject property here at all.
You would primarily do that if you wanted to use additional interfaces on
the object itself such as IADsUser for a user or IADsGroup for a group.
However, all of the method on those objects are available "late bound" via
the Invoke method and the properties are now available as well in .NET 2.0
via InvokeGet and InvokeSet.
HTH,
Joe K.
"Martin Robins" <martin dot robins at technicaldirect dot co dot uk> wrote
in message news:%2****************@TK2MSFTNGP09.phx.gbl...
Thanks for a quick answer, however I am not sure how to incorporate this
correctly with what I am doing.
My current code looks something like this ...
DirectoryEntry searchRoot = new DirectoryEntry();
searchRoot.Path = @"LDAP://DC=MyDomain,DC=local";
searchRoot.AuthenticationType = AuthenticationTypes.Secure;
// Search criteria; all user objects ...
DirectorySearcher directorySearcher = new DirectorySearcher(searchRoot,
@"(&(objectCategory=Person)(objectClass=User))" );
foreach (SearchResult searchResult in directorySearcher.FindAll())
{
DirectoryEntry directoryEntry = new
DirectoryEntry(searchResult.Path);
Trace.WriteLine(string.Format(@"{0}", directoryEntry.Name,
directoryEntry.Path));
foreach (string propertyName in
directoryEntry.Properties.PropertyNames)
{
string propertyValue = string.Empty;
for (int i = 0; i <
directoryEntry.Properties[propertyName].Count; i++)
{
object value = directoryEntry.Properties[propertyName][i];
if (value.GetType().IsCOMObject && propertyName ==
"lastLogon")
{
IADsLargeInteger largeInteger;
largeInteger = value as IADsLargeInteger;
if (largeInteger != null)
{
value =
DateTime.FromFileTime((long)largeInteger.HighPart << 32 |
(uint)largeInteger.LowPart);
}
}
propertyValue = string.Format(@"{0}{1}{2}", propertyValue,
propertyValue == string.Empty ? string.Empty : @";" ,value);
}
Trace.WriteLine(string.Format(@"{0}{1} = [{2}]", '\t',
propertyName, propertyValue));
}
directoryEntry.Close();
}
If I am understanding you correctly, you are suggesting that I cast the
directoryEntry.NativeEntry to the IADs* object whereas what I am trying to
do here is access the property values of the DirectoryEntry class (or is
this the bit I am missing?)
Intrestingly, I can cast all System.__ComObject property values to the
IADsLargeInteger interface which is why my current code checks the property
name as well as (for example) the "accountExpires" property is a
System.__ComObject, casts to IADsLargeInteger and is returning a value of
9223372036854775807 to the DateTime.FromFileTime() method which is in fact
an invalid number of ticks and therfore indicates to me that this property
value is probably not actually being stored as an IADsLargeInteger!
Cheers.
"Nicholas Paldino [.NET/C# MVP]" <mv*@spam.guard.caspershouse.com> wrote in
message news:uO**************@TK2MSFTNGP12.phx.gbl...
Martin,
I don't know that you will be able to determine what types you can cast
to. This was always a problem with COM (not so with .NET because of
reflection). Because the DirectoryEntry class ultimately relies on the COM
class, you are limited in this respect as well.
You will have to do this the way that you do it in COM, basically
calling QueryInterface on the entry, checking for the interface that you
want to use, and proceeding from there.
You can get the COM object itself through the NativeObject property, and
then try casting it to the appropriate IAD* interface. .NET makes this
somewhat easier, as you can do this:
// Get the native object.
object nativeObject = entry.NativeObject;
// Try and cast to IADsLargeInteger.
IADsLargeInteger largeInteger = (nativeObject as IADsLargeInteger);
If largeInteger is non-null, the cast succeeded.
Hope this helps.
--
- Nicholas Paldino [.NET/C# MVP]
-
mv*@spam.guard.caspershouse.com
"Martin Robins" <martin dot robins at technicaldirect dot co dot uk> wrote
in message news:ud**************@TK2MSFTNGP10.phx.gbl...
I am currently looking to be able to read information from Active Directory
into a data warehouse using a C# solution. I have been able to access the
active directory, and I have been able to return "DirectoryEntry" objects
within the path that I specify (either using the DirectoryEnrtry.Children or
using the DirectorySearcher class) and all started well and dandy!
Now the problem; some of the properties of the DirectoryEntry objects being
returned show up as "System.__ComObject" - I have google'd this and found
some of the properties and the type of object from the ActiveDS COM type
library (which I have referenced already in my project) that these
properties are stored as (for example the "lastLogon" property value of the
"User" object class can be cast to IADsLargeInteger which can then be
converted to a DateTime), however there are so many possible properties and
the various object types these can represent.
Is there any way to programatically determine the type of object to be used
to retrieve these values and cast the value to that object type, or am I
stuck with having some humungous switch statement(s) to try and determine
the type of object to use based upon the object class and property name?
Furthermore, if I am stuck with the latter, does anybody have any additional
resources I can look at to determine the correct combinations?
Cheers.