AutoCAD .NET API では ObjectARX の Open/Close モデルが廃止され(*1)、トランザクションを使用してエンティティの登録・修正を行う形式に一本化されました。
本稿では、このトランザクションを用いて線分エンティティを作成する方法をご紹介します。

サンプルコードは線分に限らず、他のエンティティを作成する場合にもテンプレートとしてご利用いただけると思います。
また、コマンドの作り方は本サイト内の「.NET API でコマンドを作る」をご覧ください。

なおサンプルコードは VisualStudio2005 の C# で作成し、AutoCAD2007 / 2008 / 2009 で動作確認を行いました。

エンティティを作成する流れ

  1. トランザクションを開始する。
  2. エンティティを作成する。
  3. ブロックテーブルを読み込みモードで取得する。
  4. モデルスペースを書き込みモードで取得する。
  5. エンティティを登録する。
  6. トランザクションをコミットする。
  7. トランザクションを破棄する。

サンプルコード

[CommandMethod( "CREATE_LINE" )]
public void CreateLine()
{
    Transaction trans = null
    try
    {
        Document active_doc = Application.DocumentManager.MdiActiveDocument;
        Database active_db = active_doc.Database;

        // 1. トランザクションを開始する
        trans = active_db.TransactionManager.StartTransaction();

        // 2. エンティティを作成する
        Point3d from_pos = Point3d.Origin;
        Point3d to_pos = new Point3d( 100, 100, 0 );
        Line line = new Line( from_pos, to_pos );

        // 3. ブロックテーブルを読み込みモードで取得する
        ObjectId blk_tbl_id = active_db.BlockTableId;
        BlockTable blk_tbl = (BlockTable)trans.GetObject( blk_tbl_id, OpenMode.ForRead );

        // 4. モデルスペースを書き込みモードで取得する
        ObjectId model_space_id = blk_tbl[ BlockTableRecord.ModelSpace ];
        BlockTableRecord model_space =
            (BlockTableRecord)trans.GetObject( model_space_id, OpenMode.ForWrite );

        // 5. エンティティを登録する
        model_space.AppendEntity( line );
        trans.AddNewlyCreatedDBObject( line, true );

        // 6. トランザクションをコミットする
        trans.Commit();
    }
    catch( System.Exception ex )
    {
        Editor edit = Application.DocumentManager.MdiActiveDocument.Editor;
        edit.WriteMessage( "\n{0}", ex.Message );
    }
    finally
    {
        // 7. トランザクションを破棄する
        if( trans != null )
        {
            trans.Dispose();
        }
    }
}

サンプルプロジェクト (4 KB)

環境によっては、コンパイル時に "この参照を解決できませんでした" という警告が出ます。この場合 "acdbmgd.dll" と "acmgd.dll" を削除し、 AutoCAD のインストールディレクトリから参照しなおしてください。

出力結果

出力結果(原点から(100,100)までの線分)

上記出力結果のうち、コマンド実行時に出力されるのは白の線分のみです。シアンの寸法線はコマンド実行後に別途描画しました。

ソースコードの解説

トランザクション

先述の通り、.NET API ではエンティティの生成/登録にトランザクションを使用します(*2)。トランザクションモデルではエンティティを複数回開いても問題がない(*3)など、ObjectARX で使われていた Open/Close モデルに比べて安全性が飛躍的に向上しています。

オブジェクトの取得

オブジェクトを取得するには、Transaction.GetObject() メソッドにオブジェクトIDを与えます。同時に OpenMode を指定しますが、ForRead で取得したエンティティに対して変更を加えると eNotOpenForWrite 例外(*4)が発生しますので気をつけてください。
サンプルコードでは、ブロックテーブルはモデルスペースのオブジェクトIDを取得するだけですので ForRead で取得しています。それに対してモデルスペースは生成した線分エンティティを登録しなければなりませんので ForWrite で取得しています。

コミット

オブジェクトを編集したら、トランザクションをコミットしなければその変更が図面に反映されません。逆に言えばオブジェクトを変更しない場合、たとえばエラーが発生した場合ではコミットせずにそのまま終了すればトランザクション開始前の状態に戻ります。

トランザクションの破棄

Transaction はマネージドクラスですが、インスタンスを破棄するには Dispose() メソッドを明示的に呼び出さなければなりません。
using 構文を使う手もありますが、例外処理を考えると、少なくともコマンドメソッドでは上記のように try-catch 構文を使用し、 finally ブロックで Dispose() した方がいいでしょう。

エンティティの生成と登録

エンティティを生成するには、目的のクラスのインスタンスを new します。
サンプルコードでは始点と終点を与えて線分オブジェクトを作成しましたが、この部分を別のクラスに差し替えれば、他のエンティティを生成することができます。

生成したエンティティは、まず AppendEntity() メソッドを使ってモデルスペースに登録します。これで図面に表示されるようになります。
次に、AddNewlyCreatedDBObject() メソッドを使ってトランザクションの管理下に入れます。

生成したエンティティは、必ずこの順序で登録してください。この順番を間違えると eNotInDatabase 例外が発生します。

(MINERVA 深津貴成)
  1. ^ 実は .NET API でも Open/Close モデルを使用することは可能です。しかし Open/Close モデルに関連するメソッドには非推奨属性が付いていますからコンパイル時に警告が出ますし、安全性の面から考えてもトランザクションモデルを使用することを強く推奨します。とはいえ、アンドゥ/リドゥが絡んできた場合などのように Open/Close モデルを使わざるをえないケースもあります。
  2. ^ ObjectARX でもトランザクションモデルは提供されていました。しかし、少なくとも日本語の資料ではほとんどが Open/Close モデルで書かれていることを考えると、ObjectARX のトランザクションモデルを使ったことのある方は少ないのではないでしょうか。
  3. ^ トランザクションは入れ子にすることもできます。この場合、もっとも外側のトランザクションをコミットした時点で図面に変更が反映されます。状態によって部分的に変更したりしなかったりといった複雑な処理では、特に恩恵が大きいでしょう。しかしトランザクションの開始と破棄はコストのかかる処理ですので、入れ子が深すぎたりループの中でトランザクションを使ったりすると動作が非常に重くなります。
  4. ^ このサンプルコードでは eNotOpenForWrite 例外のようなインターナルエラーをトラップすることはできません。ためしに C++/CLI を使ってさらに強力なエラートラップができるようなコードを書いてみましたが、それでもトラップできませんでした。したがって .NET API でインターナルエラーを処理することはあきらめるよりほかになさそうです。しかしほとんどの例外は try-catch 構文できちんと処理できますので、必ずエラー対応をするようにしてください。