Determining object type from C# at runtime

M

Martin Robins

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.
 
N

Nicholas Paldino [.NET/C# MVP]

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]
- (e-mail address removed)
"Martin Robins" <martin dot robins at technicaldirect dot co dot uk> wrote in message 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.
 
M

Martin Robins

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];

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.

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]
- (e-mail address removed)
"Martin Robins" <martin dot robins at technicaldirect dot co dot uk> wrote in message 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.
 
J

Joe Kaplan \(MVP - ADSI\)

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 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];

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.

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]
- (e-mail address removed)
"Martin Robins" <martin dot robins at technicaldirect dot co dot uk> wrote in message 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.
 
M

Martin Robins

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.

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 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];

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.

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]
- (e-mail address removed)
"Martin Robins" <martin dot robins at technicaldirect dot co dot uk> wrote in message 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.
 
M

Martin Robins

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];
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 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.

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 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];

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.

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]
- (e-mail address removed)
"Martin Robins" <martin dot robins at technicaldirect dot co dot uk> wrote in message 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.
 
M

Martin Robins

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];
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.


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 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];

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.

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]
- (e-mail address removed)
"Martin Robins" <martin dot robins at technicaldirect dot co dot uk> wrote in message 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.
 
J

Joe Kaplan \(MVP - ADSI\)

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 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];
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 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.

in message 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 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];

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.

message 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]
- (e-mail address removed)
"Martin Robins" <martin dot robins at technicaldirect dot co dot uk> wrote
in message 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.
 

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