セキュリティ記述子(SecurityDescriptor)の出力の.NETクラス使用版の C# のコードです。
 Active Directoryデータのプロパティ出力のCOM対応版(C#)と比較しながら見ていただければと思います。
 コードはサンプルアプリに組み込む前提で書いてます。
 追加で System.Security.AccessControl 名前空間をインポートしてます。
  
 まずは呼出し側。出力部分はメソッド化しました。
 public static void OutputProperties(DirectoryEntry entry, string filePath)
 {
   var props = entry.Properties.PropertyNames.Cast<string>().OrderBy(s => s).ToList();   //プロパティ名のリスト
   using (var writer = new StreamWriter(filePath, false, Encoding.UTF8))
   {
     foreach (var pname in props)  //プロパティ数分
     {
       if (pname.Equals("nTSecurityDescriptor"))   //セキュリティ記述子の時
       {
         writer.WriteLine(pname);
         OutputSecurityDescriptor(entry.ObjectSecurity, writer);   //セキュリティ記述子を出力
         continue;
       }
  
       var val = entry.Properties[pname].Value;
       if (val is byte[])  //バイト配列の時
       {
         var pstr = GetByteValue(pname, (byte[])val);  //バイト値を取得
         writer.WriteLine("{0}:{1}", pname, pstr);
       }
       else if (val is IADsLargeInteger)   //大きい整数の時
       {
         var li = GetLargeIntegerValue(pname, (IADsLargeInteger)val);  //大きい整数値を取得
         writer.WriteLine("{0}:{1}", pname, li);
       }
       else  //それ以外の時
       {
         foreach (var pval in entry.Properties[pname])   //値数分
         {
           var value = GetValue(pval);  //値を取得
           writer.WriteLine("{0}:{1}", pname, value);
         }
       }
     }
   }
 }
  
 続いて出力処理側。DirectoryEntry.ObjectSecurityプロパティのとこに少し書きましたが、Trustee の名前部分(ユーザ名やグループ名)を取得する必要があるので、別途メソッド化しました。
 //セキュリティ記述子を出力
 private static void OutputSecurityDescriptor(ActiveDirectorySecurity security, StreamWriter writer)
 {
   var sd = new CommonSecurityDescriptor(true, true, security.GetSecurityDescriptorBinaryForm(), 0);
   var aceList = sd.DiscretionaryAcl.Cast<QualifiedAce>().ToList();
   var sidList = aceList.GroupBy(ace => ace.SecurityIdentifier).Select(
     group => group.First().SecurityIdentifier.Value).ToList();
   var sidNameDic = CreateSidNameDictionary(sidList);  //SID/アカウント名のコレクションを作成
   var aceGroups = aceList.OrderBy(ace => sidNameDic[ace.SecurityIdentifier.Value]).ThenBy(
     ace => ace.AccessMask).ThenBy(ace => ace.AceFlags).ThenBy(ace => ace.AceType).ThenBy(
     ace => (ace is ObjectAce) ? ((ObjectAce)ace).ObjectAceFlags : 0).GroupBy(
     ace => String.Format("{0}|{1}|{2}|{3}|{4}",
       sidNameDic[ace.SecurityIdentifier.Value], ace.AccessMask, ace.AceFlags, ace.AceType,
       (ace is ObjectAce) ? ((ObjectAce)ace).ObjectAceFlags : 0)).ToList();   //プロパティ値でグループ化したACE
   var ctr = 0;
  
   foreach (var aceGroup in aceGroups)   //ACE数分
   {
     var ace = aceGroup.First();
     ctr++;
     writer.WriteLine(" {0:D2}. Trustee   :{1}", ctr, sidNameDic[ace.SecurityIdentifier.Value]);
     writer.WriteLine(" {0:D2}. AccessMask:{1}", ctr, ToEnumValueText(ace.AccessMask, typeof(ActiveDirectoryRights)));
     writer.WriteLine(" {0:D2}. AceFlags  :{1}", ctr, ToEnumValueText((int)ace.AceFlags, typeof(AceFlags)));
     writer.WriteLine(" {0:D2}. AceType   :{1}", ctr, ToEnumValueText((int)ace.AceType, typeof(AceType)));
     if (ace is ObjectAce)   //ディレクトリ オブジェクトに関連付けられたACEの時
     {
       writer.WriteLine(" {0:D2}. Flags     :{1}", ctr,
         ToEnumValueText((int)((ObjectAce)ace).ObjectAceFlags, typeof(ObjectAceFlags)));
     }
     else  //CommonAce(ACE)の時
     {
       writer.WriteLine(" {0:D2}. Flags     :0", ctr);
     }
   }
 }
  
 //SID/アカウント名のコレクションを作成
 private static Dictionary<string, string> CreateSidNameDictionary(List<string> sidList)
 {
   var sidNameDic = new Dictionary<string, string>(sidList.Count);
   using (var root = GetRootEntry())   //ルートのDirectoryEntryを取得
   {
     using (var searcher = new DirectorySearcher(root))
     {
       foreach (var sid in sidList)  //SID数分
       {
         searcher.Filter = String.Format("(objectSid={0})", sid);
         var res = searcher.FindOne();   //検索
         if (res == null)  //見つからなかった時(SYSTEM、SELF、Everyoneなど)
         {
           var acc = new SecurityIdentifier(sid).Translate(typeof(NTAccount));   //アカウントに変換
           sidNameDic.Add(sid, System.IO.Path.GetFileName(acc.Value));
         }
         else  //見つかった時
         {
           using (var entry = res.GetDirectoryEntry())
           {
             var objectType = (CategoryType)Enum.Parse(typeof(CategoryType), entry.SchemaClassName, true);
             if (objectType == CategoryType.ForeignSecurityPrincipal)  //外部のセキュリティプリンシパルの時
             {
               var acc = new SecurityIdentifier(sid).Translate(typeof(NTAccount));   //アカウントに変換
               sidNameDic.Add(sid, System.IO.Path.GetFileName(acc.Value));
             }
             else  //外部のセキュリティプリンシパル以外の時
             {
               sidNameDic.Add(sid, entry.Properties["cn"].Value.ToString());
             }
           }
         }
       }
     }
   }
   return sidNameDic;
 }
  
 最後に既存のメソッド。内部実装を変更しました。
 //列挙体のプロパティ値をテキスト化
 private static string ToEnumValueText(int value, Type enumType)
 {
   if (enumType == typeof(AceType))  //AceTypeの時
   {
     return String.Format("{0}({1})", value, Enum.ToObject(enumType, value));
   }
  
   Func<int, string> selector = e => Enum.ToObject(enumType, e).ToString();
   var values = Enum.GetValues(enumType).Cast<object>().Select(e => Convert.ToInt32(e)).Where(
     e => (value & e) == e).OrderBy(selector).Select(selector).ToList();   //設定されている値の列挙体文字列
  
   values.Remove("None");
   if (values.Count == 0)   //設定されている値がない時
   {
     return value.ToString();
   }
   return String.Format("{0}({1})", value, String.Join(" | ", values));
 }