Reputation: 3539
I've been bumping my head over this for a while. I've found many questions asked regarding this on the internet but none had an actual answer.
My goal is to read & copy certain properties from one object in LDAP to another.
I can do it for all the properties without a problem, except for one property. Trying to access that specific property from a DirectoryEntry object results in the following error & exception: Unknown error (0x8000500c)
& 'P' threw an exception of type 'System.Runtime.InteropServices.COMException
.
I noticed that the SearchResult object holds a byte[] object as a value of that property, while the other properties are simply strings. So, reading the SearchResult object is no problem, I can get the byte[] and convert it to a string if I want to. The problem is that I need to get this value, and copy it into another object in LDAP, and that is done using the DirectoryEntry object of the SearchResult object (I get it using the GetDirectoryEntry
method).
Let me try to explain the problem:
In LDAP, I have two objects - 'A' and 'B'.
I need to copy a property 'P' from A into B.
Property 'P' comes as byte[] object (even though in JXplorer I read & update it manually as a string).
Trying to access a property 'P' from the DirectoryEntry object results in a System.Runtime.InteropServices.COMException
exception; I think I read somewhere that it's related to the data type, that it doesn't know how to read/convert it (byte[]).
The property 'P' of the SearchResult object holds a byte[] (instead of simply a string, ideally, like the other properties: )
I'm unable to access the property 'P' from the DirectoryEntry object; I've shown in above SS that the property exists (I can also see the property name in the DirectoryEntry itself, but not the property value):
I'm able to access any other property from the same DirectoryEntry object:
Help would be appreciated.
Upvotes: 0
Views: 195
Reputation: 3539
I was able to work around this issue on the same day I posted this question, by writing a custom class that handles the operations on properties that the current direct methods were unable to do. The methods can also be defined as extension methods, but I decided not to do it.
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;
using System.Web;
namespace Bell.ca.Services.UserServices.Ldap
{
/// <summary>
/// Based on <see href="https://learn.microsoft.com/en-us/windows/win32/api/iads/ne-iads-ads_property_operation_enum"/>
/// </summary>
public enum ADSPropertyOperationsEnum
{
CLEAR = 1,
UPDATE = 2,
APPEND = 3,
DELETE = 4
}
public static class ADSPropertyOperations
{
/// <summary>
/// Clears the <paramref name="propertyName" /> of the <paramref name="directoryEntry" />
/// </summary>
/// <param name="directoryEntry">The <typeparamref name="DirectoryEntry"/> to be modified</param>
/// <param name="propertyName">The name of the property to be cleared</param>
/// <returns><see langword="true"/> if operation was done successfully</returns>
public static bool Clear(DirectoryEntry directoryEntry, string propertyName)
{
try
{
directoryEntry.Invoke("PutEx", new object[] { ADSPropertyOperationsEnum.CLEAR, propertyName, new object[] { } });
directoryEntry.CommitChanges();
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Sets the <paramref name="directoryEntry" />'s <paramref name="propertyName" />'s value with <paramref name="value"/>
/// </summary>
/// <param name="directoryEntry">The <typeparamref name="DirectoryEntry"/> to be modified</param>
/// <param name="propertyName">The name of the property</param>
/// <param name="value">The value to set into <paramref name="propertyName"/></param>
/// <returns><see langword="true"/> if operation was done successfully</returns>
public static bool Set(DirectoryEntry directoryEntry, string propertyName, string value)
{
try
{
directoryEntry.Invoke("PutEx", new object[] { ADSPropertyOperationsEnum.UPDATE, propertyName, new object[] { value } });
directoryEntry.CommitChanges();
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Appends <paramref name="value"/> to the <paramref name="propertyName" /> of the <paramref name="directoryEntry" />
/// </summary>
/// <param name="directoryEntry">The <typeparamref name="DirectoryEntry"/> to be modified</param>
/// <param name="propertyName">The name of the property</param>
/// <param name="value">The value to be appended to <paramref name="propertyName"/></param>
/// <returns><see langword="true"/> if operation was done successfully</returns>
public static bool Append(DirectoryEntry directoryEntry, string propertyName, string value)
{
try
{
directoryEntry.Invoke("PutEx", new object[] { ADSPropertyOperationsEnum.APPEND, propertyName, new object[] { value } });
directoryEntry.CommitChanges();
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Deletes the <paramref name="directoryEntry" />'s <paramref name="propertyName" />'s specific <paramref name="value"/>
/// </summary>
/// <param name="directoryEntry">The <typeparamref name="DirectoryEntry"/> to be modified</param>
/// <param name="propertyName">The name of the property</param>
/// <param name="value">The value to be deleted from <paramref name="propertyName"/></param>
/// <returns><see langword="true"/> if operation was done successfully</returns>
public static bool Delete(DirectoryEntry directoryEntry, string propertyName, string value)
{
try
{
directoryEntry.Invoke("PutEx", new object[] { ADSPropertyOperationsEnum.DELETE, propertyName, new object[] { value } });
directoryEntry.CommitChanges();
return true;
}
catch
{
return false;
}
}
}
}
This is the method I used to convert the property that holds an array of TV PINs as an array of byte[] into a list of strings:
private List<string> GetTvPinValue(SearchResult searchResult)
{
var result = new List<string>();
var tvPinProperty = searchResult.Properties["ssotvpin"];
foreach (var item in tvPinProperty)
{
var tvPinBytes = (byte[])item;
var tvPin = Encoding.UTF8.GetString(tvPinBytes);
result. Add(tvPin);
}
return result;
}
Upvotes: 0