473,386 Members | 1,720 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

Determining object type from C# at runtime

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.
Dec 2 '05 #1
7 11905
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.
Dec 2 '05 #2
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.
Dec 2 '05 #3
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.
Dec 2 '05 #4
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.
Dec 2 '05 #5
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.
Dec 2 '05 #6
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.
"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.
Dec 2 '05 #7
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.
Dec 2 '05 #8

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
by: Bijay Kumar | last post by:
Hi Guys, I was going through the source code of Object.cs in rotor. What I found is Equals() implemented as follows: public extern virtual bool Equals(Object obj); What I don't...
2
by: Clinton Pierce | last post by:
I've filled a DataTable with columns that have custom type (a class that I'm using to keep track of other things, not just a value). When the .Select method goes to sort this column, how do I let...
5
by: John Hardin | last post by:
All: Is it possible at runtime to determine the names of all classes derived from a given class? -- John Hardin KA7OHZ <johnh@aproposretail.com> Internal Systems...
0
by: CTDev Team | last post by:
Hi, We are using Exchange Server 5.5, and have applications written in VB6 and C# that read and process emails. We are experiencing intermittent errors similar to C# Application ...
2
by: Garrett | last post by:
Need any help in determining which groups have been given security access to a folder. Searched DirectoryServices to no avail... Any Help?
9
by: Clay_Culver | last post by:
I need to find out if an object is a class. Using new style classes this is very easy: class Test(object): pass obj = Test or obj = Test() if type(obj) == type:
5
by: Doug Stiers | last post by:
C# newbie question: I'm trying to determine the type of a datareader value. Why cant I do it like this? if (rdr.GetValue(0).GetType() is Guid || rdr.GetValue(0).GetType() is Int32) { // do...
4
by: lord trousers | last post by:
I'm implementing a garbage collector in C++ for a fun new language (don't ask), and I've found that it spends a good amount of time calling "finalize" on objects that are freed/not relocated. The...
53
by: Aaron Gray | last post by:
Due to M$'s stupidity in not making DOMElements first class citizens the following will not work :- function isElement( o) { return o instanceof Element } It works for FF, Opera and Safari.
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.