20210129のC#に関する記事は2件です。

Revit APIでダイアログの値を次開く時まで保持する方法

はじめに

Revitでアドイン開発をする際、ダイアログに入力した値を保持しておきたいがやり方が分からない、というときの手順です。
調べるとProperties.Settings.Defaultなど色々出てきたのですが、これは「拡張ストレージ」というやつにXML形式で読み書きするのが便利だそうです。
「拡張ストレージ」については調べても上級者向けの解説しかなかったため、次に使うときのために使い方のメモを残します。
使い方のメモなので、詳しい定義や意味までは追いません。
問題がある場合はコメントください。

環境

OS: Windows8.1
Revit: 2019.2

実装

ダイアログの中身をXML形式で保存

ここはおのおの楽な方法でデータを作成します。
XML形式でといっても保存時には文字列型にToString()するため、XML形式には限りません。
XMLが楽だと思われますが、他の形式の方が使いやすいという方は別の方法で文字列作成していいと思います。

拡張ストレージへの保存

トランザクションの開始と終了

拡張ストレージに保存するにはTransactionを開始する必要があります。(読み込み時には不要です)
TransactionはDocumentから取得することができ、DocumentはApplicationから取得することができます。→参考
以下参考サイトのDocument取得部分のコピペ。

UIApplication uiapp = this.ActiveUIDocument.Application;
UIDocument uidoc = uiapp.ActiveUIDocument;
Autodesk.Revit.ApplicationServices.Application app = uiapp.Application as Autodesk.Revit.ApplicationServices.Application;
Autodesk.Revit.DB.Document doc = uidoc.Document;

拡張ストレージへ保存する際のトランザクションの開始と終了の流れは以下の形になります。
/* ここで書き込み実行 */の部分で書き込み処理を行います(後述)。見辛いので書き込みは関数にした方が良いと思います。
エラー表示の部分は必要に応じてエラーを表示したりログに出力したりしてください。

// 拡張ストレージに保存
using (Transaction transaction = new Transaction(doc)) {
  if (transaction.Start("ExtensibleStorage") == TransactionStatus.Started) {
    try {
      /* ここで書き込み実行 */

      if (transaction.Commit() != TransactionStatus.Committed) {
        // コミットに失敗した時のエラー(Rollbackが必要)
        transaction.RollBack();
      }
    }
    catch (Exception ex) {
     // 途中で失敗した際のエラー(Rollbackが必要)
     transaction.RollBack();
    }
  }
  else {
    // トランザクションの開始に失敗した時のエラー
  }
}

拡張ストレージの読み書き

読み書きにあたっての公式のDocはここ
参考URLはここ等。

書き込み

書込みにあたっての大まかな流れは以下です。
1. SchemaBuilder を宣言する。(ある場合4へ)
2. SchemaBuilder に権限とスキーマ名を設定する。
3. SchemaBuilder にフィールド名を追加する。
4. Schema を取得。
4. docの持つProjectInformationに対して、このSchemaでEntityを取得する(無い場合作成)
5. Entityに登録したいXMLを文字列型としてセットする。
6. ProjectInformationに5のEntityをセットする。

// まず、スキーマがあるかどうかを判定
Schema sch = Schema.Lookup(new Guid("適当なGUID"));
  if(sch == null){
  SchemaBuilder sb = new SchemaBuilder(new Guid("適当なGUID"));
  // 権限等
  sb.SetReadAccessLevel(AccessLevel.Public); // Public/Vendor/Applicationの3種類あります。とりあえずPublicで。
  sb.SetWriteAccessLevel(AccessLevel.Public);
  // スキーマ名
  sb.SetSchemaName("適当なスキーマ名");
  // フィールド名設定
  sb.AddSimpleField("適当なフィールド名", typeof(string));
  // スキーマを取得
  sch = sb.Finish();
}
// Entityを取得
Entity ent = doc.ProjectInformation.GetEntity(sch);
// XMLを設定
ent.Set<FieldType>("適当なフィールド名", xmldata.ToString());
// Docに設定
doc.ProjectInformation.SetEntity(ent);

これで、ダイアログのOKボタンを押した時にこれが実行されるようにすればOKです。

読み込み

読み込みの方はいたってシンプルで、
1. Schema を取得
2. あればProjectInformationに対してSchemaの中のフィールド名からEntityをゲット
です。

ここではジェネリッククラスTを使います。
引数のElementdoc.ProjectInformationstrGUIDは書き込み時の「適当なGUI」、strFieldNameは「適当なフィールド名」にそれぞれ読み替えてください。

public static T GetData<T>(this Element elm, string strGUID, string strFieldName) {
  Schema sch = Schema.Lookup(new Guid(strGUID));
  // そもそも無い時
  if (sch == null) {
    return default(T);
  }
  // 有効な値が無い時
  if (!elem.GetEntity(sch).IsValid()) {
    return default(T);
  }
  T entity = elem.GetEntity(sch).Get<T>(strFieldName);
  return entity;
}

処理としてはこれだけです。
stringで保存したので、呼び出すときは以下のように<string>とします。

string strXMLdata = GetData<string>(...);

これで、ダイアログを開くときに読み込んだstrXMLdataを反映させていけばOKです。

感想

Revit関係はググっても英語のページばかりだなあと思いました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LDAP でユーザー認証

.NET Framework の LdapConnection を使って、LDAP でユーザー認証するサンプルコード。
このままだと、指定 DN 直下にいるユーザーでのみ認証できる。
DN を固定せず認証したい場合は、一旦 LDAP を検索可能かつ DN が判明しているアカウントでバインドしてユーザー名を検索したのち、そうして得た認証対象ユーザーの DN とパスワードを使ってバインドしてみる、って流れになる。

using System;
using System.DirectoryServices.Protocols;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;
using System.Net;

namespace LdapTest
{
    public static class LdapAuthnTest
    {
        /// <summary>
        /// LDAP で認証する。
        /// </summary>
        /// <param name="userId">ユーザー名</param>
        /// <param name="userPassword">ユーザーのパスワード</param>
        /// <param name="userAttr">DN に指定されている属性 (多くの場合、CN)</param>
        /// <param name="baseDN">ベース DN</param>
        /// <param name="ldapServer">LDAP サーバーのホスト名または IP アドレス</param>
        /// <param name="isTls">LDAPS にする場合 true。LDAP のままにする場合 false。</param>
        /// <returns>認証成功でそのユーザーの <see cref="SearchResultEntry" />、認証失敗は null。</returns>
        /// <exception cref="LdapException">LDAP 例外</exception>
        public static SearchResultEntry LdapAuthn(string userId, string userPassword, string userAttr, string baseDN, string ldapServer, bool isTls)
        {
            // 認証情報が空の場合は即認証失敗
            if (userId.Length < 1 || userPassword.Length < 1)
            {
                return null;
            }

            ldapServer = ServerPortSpecify(ldapServer, isTls);

            LdapConnection ldapConnection = new LdapConnection(ldapServer) {
                Credential = new NetworkCredential(userAttr + "=" + LdapEscape(userId) + "," + baseDN, userPassword),
                AuthType = AuthType.Basic,
                Timeout = new TimeSpan(0, 0, 10)
            };
            ldapConnection.SessionOptions.ProtocolVersion = 3;
            ldapConnection.SessionOptions.SecureSocketLayer = isTls;

            SearchResultEntry searchResultEntry = null;
            try
            {

                // 認証したいユーザーでバインドしてみる
                ldapConnection.Bind();

                // バインドが通ったらユーザー情報を得る
                SearchRequest searchRequest = new SearchRequest()
                {
                    DistinguishedName = baseDN,
                    Filter = "(" + userAttr + "=" + userId + ")"
                };
                SearchResponse searchResponse = ldapConnection.SendRequest(searchRequest) as SearchResponse;

                if (searchResponse.Entries.Count == 1)
                {
                    foreach (SearchResultEntry item in searchResponse.Entries)
                    {
                        searchResultEntry = item;
                        break;
                    }
                }

                if (searchResultEntry == null)
                {
                    // 念のため。この例外がスローされることはない。
                    throw new ArgumentOutOfRangeException(searchResponse.Entries.Count.ToString());
                }
            }
            catch (LdapException e)
            {
                if (e.ErrorCode != 49) // エラー コード 49 は「認証失敗」
                {
                    throw e;
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine(e.Message);
                    System.Diagnostics.Debug.WriteLine(e.ServerErrorMessage);
                }
            }
            finally
            {
                if (ldapConnection != null)
                {
                    try
                    {
                        ldapConnection.Dispose();
                    }
                    catch (Exception e)
                    {
                        System.Diagnostics.Debug.WriteLine(e);
                    }
                }
            }

            return searchResultEntry;
        }

        /// <summary>
        /// TCP ポート指定なし、かつ、LDAPS のときはポート指定「:636」を追加する。
        /// </summary>
        /// <param name="ldapServer">LDAP サーバー指定</param>
        private static string ServerPortSpecify(string ldapServer, bool isTls)
        {
            if (isTls && !Regex.IsMatch(ldapServer, ":[0-9]*$")) return ldapServer + ":636";
            else return ldapServer;
        }

        /// <summary>
        /// 正常に処理できるよう、特殊文字をエスケープする。(RFC2253)
        /// </summary>
        /// <param name="ldapValue">エスケープ処理前の文字列</param>
        /// <returns>エスケープ処理済みの文字列</returns>
        static string LdapEscape(string ldapValue)
        {
            return "\"" + ldapValue.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"";
        }
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む