Autodesk Community Tips- ADNオープン
Autodesk Community Tipsではちょっとしたコツ、やり方、ショートカット、アドバイスやヒントを共有しています。
ソート順:
Issue グループやブロック化していない状態の特定オブジェクトを移動や回転する際に、 イベント処理を使用せずに、アプリ内で関係のあるオブジェクトを同じように移動や回転する方法はありますか?   Solution 特定のオブジェクト タイプ(クラス)の移動や回転、縮尺変更など編集は、TransformOverrule オーバールール(Overrule)プロトコルを使用して、既定の振る舞いを変更することが可能です。ただし、イベント ハンドラーでのオブジェクト編集で同一イベントが発生してしまい、無限ループに陥らないような配慮が必要です。   次の C# コードは、MOVE コマンドや ROTATE コマンド、SCALE コマンドで円弧オブジェクト(Arc)を選択すると、青色(インデックスカラー色:5)の線分オブジェクト(Line)に同じマトリックス変換を実施するものです。   public class TransformByOverrule : TransformOverrule { public override void TransformBy(Entity ent, Matrix3d mat) { Database db = HostApplicationServices.WorkingDatabase; Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor; ed.WriteMessage("\nTransformBy.TransformBy Overrule - matrix {0}", mat.ToString()); base.TransformBy(ent, mat); TypedValue[] typeval = new TypedValue[2]; typeval.SetValue(new TypedValue(62, 5), 0); typeval.SetValue(new TypedValue(0, "LINE"), 1); SelectionFilter filter = new SelectionFilter(typeval); PromptSelectionResult psr = ed.SelectAll(filter); ed.WriteMessage("\nGetSelection = {0}", psr.Status.ToString()); if (psr.Status == PromptStatus.OK) { using (Transaction tr = db.TransactionManager.StartTransaction()) { SelectionSet sset = psr.Value; foreach (ObjectId objId in sset.GetObjectIds()) { ed.WriteMessage("\nObjectTd:" + objId.ToString()); Entity slave = (Entity)tr.GetObject(objId, OpenMode.ForWrite); slave.TransformBy(mat); } tr.Commit(); } } } } static TransformByOverrule _toverrule = null; [CommandMethod("MyCommand")] static public void MyCommand() { Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor; if (_toverrule == null) { _toverrule = new TransformByOverrule(); ObjectOverrule.AddOverrule(RXObject.GetClass(typeof(Arc)), _toverrule, true); ObjectOverrule.Overruling = true; ed.WriteMessage("\nBegin modifying ARC overrule"); } else { ObjectOverrule.RemoveOverrule(RXObject.GetClass(typeof(Arc)), _toverrule); _toverrule.Dispose(); _toverrule = null; ed.WriteMessage("\nEnd modifying ARC overrule"); } }        
記事全体を表示
Issue 選択したオブジェクトが自動調整配列複写の要素かどうか判定して、自動調整配列複写であった場合に分解する実装するには、どのようなコードを記述すればいいでしょうか?       Solution 自動調整配列複写の対象となっているオブジェクト一式は、匿名ブロック(Anonymous Block)として自動調整フレームワークで管理、維持されています。   自動調整配列複写の判定には、AcDbAssocArrayActionBody::isAssociativeArray() メンバー関数を利用することが出来ます。同様に、分解には AcDbAssocArrayActionBody::explode() メンバー関数 を利用することが出来ます。   次のコードは、選択したオブジェクトが自動調整配列複写の構成要素だった場合に、トップレベルの調整配列複写を分解する例です。   ads_name ename; ads_point pt; int stat = acedEntSel(_T("\nオブジェクトを選択 : "), ename, pt); if (stat != RTNORM) return; AcDbEntity* pEnt; AcDbObjectId objId; acdbGetObjectId(objId, ename); Acad::ErrorStatus err = acdbOpenAcDbEntity(pEnt, objId, AcDb::kForRead); if (err != Acad::eOk) return; if (AcDbAssocArrayActionBody::isAssociativeArray(pEnt)) { acutPrintf(_T("\n自動調整配列です")); AcDbObjectId actId = AcDbAssocArrayActionBody::getControllingActionBody(pEnt, NULL); AcDbAssocArrayActionBody* pAryAct = NULL; err = acdbOpenAcDbObject((AcDbObject*&)pAryAct, actId, AcDb::kForWrite); if (err != Acad::eOk) { pEnt->close(); return; } if (AcDbAssocArrayPathParameters::cast(pAryAct->parameters())) acutPrintf(_T("\n - パス")); if (AcDbAssocArrayPolarParameters::cast(pAryAct->parameters())) acutPrintf(_T("\n - 円形状")); if (AcDbAssocArrayRectangularParameters::cast(pAryAct->parameters())) acutPrintf(_T("\n - 矩形状")); AcDbObjectIdArray newIds; pAryAct->explode(pEnt, newIds); pAryAct->close(); } else acutPrintf(_T("\n自動調整配列ではありません")); pEnt->close(); return;   EXPLODE コマンドで自動調整配列複写を分解する場合には、次のようなコードで AcDbAssocArrayActionBody::explode()  メンバー関数を代替することが出来ます。いずれの場合も、自動調整フレームワークが構成要素オブジェクトを保持しているため、すべてのオブジェクトを指定する必要はありません。   ads_name sset; acedSSAdd(NULL, NULL, sset); acedSSAdd(ename, sset, sset); acedCommandS(RTSTR, _T("EXPLODE"), RTPICKS, sset);      
記事全体を表示
Issue 選択したオブジェクトが自動調整配列複写の要素かどうか判定して、自動調整配列複写であった場合に分解する実装するには、どのようなコードを記述すればいいでしょうか?     Solution 自動調整配列複写の対象となっているオブジェクト一式は、匿名ブロック(Anonymous Block)として自動調整フレームワークで管理、維持されています。   自動調整配列複写の判定には、AssocArray.IsAssociativeArray メソッドを利用することが出来ます。 同様に、分解には AssocArray.Explode メソッドを利用することが出来ます。   次のコードは、選択したオブジェクトが自動調整配列複写の構成要素だった場合に、トップレベルの調整配列複写を分解する C# 例です。   Database db = HostApplicationServices.WorkingDatabase; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; PromptEntityResult pent = ed.GetEntity("\nオブジェクトを選択:"); if (pent.Status != PromptStatus.OK) { return; } using (Transaction tr = db.TransactionManager.StartTransaction()) { if (AssocArray.IsAssociativeArray(pent.ObjectId)) { ed.WriteMessage("\n自動調整配列です"); AssocArray ary = AssocArray.GetAssociativeArray(pent.ObjectId); var param = ary.GetParameters(); if (param as AssocArrayPathParameters != null) ed.WriteMessage("\n - パス"); if (param as AssocArrayPolarParameters != null) ed.WriteMessage("\n - 円形状"); if (param as AssocArrayRectangularParameters != null) ed.WriteMessage("\n - 矩形状"); AssocArray.Explode(pent.ObjectId); } else ed.WriteMessage("\n自動調整配列ではありません"); tr.Commit(); }   EXPLODE コマンドで自動調整配列複写を分解する場合には、次のようなコードで AssocArray.Explode メソッドを代替することが出来ます。いずれの場合も、自動調整フレームワークが構成要素オブジェクトを保持しているため、すべてのオブジェクトを指定する必要はありません。   var sset = SelectionSet.FromObjectIds(new ObjectId[] { pent.ObjectId }); ed.Command("EXPLODE", sset);    
記事全体を表示
AutoCAD .NET APIリファレンスに記載の様に、PromptSelectionOptions.SingleOnlyは、AutoCAD ObjectARXでのacedSSGet()での”:S”モードの設定に相当します。また、 PromptSelectionOptions.SinglePickInSpaceは、":A"モードに相当します。   ObjectARXでのacedSSGetでは、”:S"モードは、単一のエンティティの選択が許容される状態となりますが、窓選択については有効な状態のままとなります。   また、”:A"モードについては、aecdSSGetのリファレンスでは「This mode option causes acedSSGet() to perform single pick selection in space on entities that implement a subselection filter.」との説明がありますが、これは例えばテーブルの様な、エンティティ内にサブエンティティがあるようなエンティティにおいて、テーブルのセル内の文字のクリックを、テーブル自身の選択とするオプション設定となります。
記事全体を表示
現象 AutoCAD 2025のAccoreconsole.exe でSystem.Data.OleDbを参照するプラグインのカスタムコマンドを実行すると、Accoreconsole.exeが異常終了するAutoCAD 2025のAccoreconsole.exe でSystem.Data.OleDbを参照するプラグインのカスタムコマンドを実行すると、Accoreconsole.exeが異常終了してしまいます。   なお、プラグインはAutoCAD 2025に合わせて.net 8を利用するように移植済みです。 また、System.Data.OleDbはVisual Studio のNugetパッケージマネージャから 9.0.2をプロジェクトにインストールして使用しています。 診断 AutoCAD インストールフォルダ配下のSystem.Data.OleDb.dllのバージョンと、プラグインプロジェクトのNugetでインストールしているSystem.Data.OleDb.dllのバージョンの差異に起因してAccoreconsole.exeが異常終了している状況です。   AutoCAD 2024以前は、.NET FrameworkのAppDomain機能を用いてプラグインは分離された実行空間にロード・実行されていました。AutoCADが利用するアセンブリとは異なるバージョンのアセンブリをプラグインが参照している場合でも問題なく実行することが出来ておりました。 一方で、AutoCAD 2025では、従来の.NET Frameworkから、.NET Core(.NET 8)にサポートプラットフォームが変更されましたが、この.NET CoreではAppDomain機能がサポートされておらず、すべてのプラグインとその参照アセンブリは同じ実行空間にロードされます。このため、AutoCADとプラグイン、または異なるプラグイン間でバージョンの異なる同じアセンブリを参照している場合、競合が発生して異常終了等の予期せぬ事態が発生することがあります(参照アセンブリによっては異常終了せず動作する場合もあります)。   解決策 System.Data.OleDbに特化した解決方法としては、プラグインが参照するSystem.Data.OleDbのライブラリをAutoCADのインストールフォルダ配下の者とすることで、問題を解消することが可能です。 以下、手順となります。 1.VisualStudio のプロジェクトからSystem.Data.OleDbのNugetパッケージを削除する 2.代わりに、VisualStudio のプロジェクトの参照設定で、AutoCADのインストールフォルダ(acad.exeおよびaccoreconsole.exeがあるフォルダ)配下のSystem.Data.OleDb.dllを参照ファイルに追加をする。   また、System.Data.OleDbに特化しない一般的な解決方法としては、.NET Coreが提供するAssemblyLoadContext機能を用いて、カスタム処理を異なる空間で実行することにより、参照するアセンブリのバージョン違いによる干渉を防ぐ方法となります。 AssemblyLoadContext機能については、Microsoft社の提供するマテリアル「System.Runtime.Loader.AssemblyLoadContext について」をご参照ください。
記事全体を表示
質問 InventorのVBA環境で、VBAの「ツール」-「参照設定」から利用するライブラリの参照設定が行えます。 VBAを実行するクライアント端末が複数あり、各端末でライラリファイルの参照設定をすることが手間なので、ライブラリの参照設定を出力して配布することは可能でしょうか? 回答 Inventorの設定ファイルには、VBAでの参照設定を編集する機能はありません。また、InventorのAPI にも直接的に参照設定を編集する機能はありません。   一方で、InventorのAPIで、Microsoft社が提供するVBA開発環境のVBProjectオブジェクトを取得するAPIが公開されているため、VBProjectオブジェクトのReferences.AddFromFile()メソッド等を利用することで、プログラムコードから参照設定を行うことが可能です。   以下は、"Adbe Acrobat 10 Type Library”への参照を追加する場合のサンプルコードとなります。 Dim app As Application Set app = ThisApplication Dim VBProject As Object Set VBProject = app.VBAProjects.Item(1).VBProject Const RefFile As String = "<path to acrobat.tlb file>" VBProject.References.AddFromFile RefFile
記事全体を表示
質問 Vaultクライアントで、プロジェクトエクスプローラ($)配下の、あるフォルダに対してローカル作業フォルダを規定値から別のフォルダに変更するを設定しました。 その後、Vault APIで対象のフォルダのローカル作業フォルダのパスを取得すると、変更したフォルダパスではなく、規定値のフォルダパスが取得されている状況です。 Vault クライアントで変更したフォルダパスを取得する方法はありますか。 回答  Vaultのローカル作業フォルダの設定を規定値から変更した場合、変更した設定は以下のファイルに保存されます(*****部は、実行環境により変化します)。   C:\Users\<Windows ログインユーザ>\AppData\Roaming\Autodesk\VaultCommon\Servers\Services_Security_******\<接続先サーバ名>\Vaults\<ボルト名>\Objects\WorkingFolders.xml     ファイルパスに<Windowsログインユーザ>、<接続先サーバ名>、<ボルト名>が含まれているため、カスタムアプリケーションからVaultに接続する際に、これらのどれかが異なる場合には、異なるローカル作業フォルダの設定が読み込まれます(ファイルが存在しない場合は、既定のパスとなります)。   ローカル作業フォルダの設定を規定値から変更したVaultクライアントアプリケーションと、APIを実行しているアプリケーションで、この値のどれかが異なっている場合Vault クライアントで変更したフォルダパスを取得することが出来ません。これらの値を合わせたうえで、変更後のフォルダパスが取得できるかを確認してください。
記事全体を表示
現象 Inventorを、VBAや.NET FrameworkのCreateObject()等のCOM API経由で起動後、カスタマイズプログラムからQuit()を実行せずに、GUIで終了(xボタン押下当)した場合、バックグラウンドプロセスとしてInventorが残ってしまう。   診断 外部プロセスから、VBAや.NET Framework等のCreateObject()等を用いて外部アプリケーションを起動した場合、対象のアプリケーションはQuit()メソッドにより終了をさせる必要があります。Quit()を実行せずアプリケーションGUI側から終了した場合バックグラウンドにプロセスが残存してしまいます。 この挙動はInventorのみならずExcel等でも同様で、COMオブジェクトを公開するアプリケーションに共通の挙動となります。   解決策 CreateObject()により取得したアプリケーションオブジェクトをQuit()メソッドで終了せず利用したい場合は、CreateObject()ではなくProcess.Start()等によりプロセスを起動し、GetObject()等を用いて起動中のアプリケーションのCOMオブジェクトを取得することで回避が可能です。   「APIでInventorを起動する際にバージョン指定する方法」にProcess.Start()を用いてInventorを起動し起動したInventorアプリケーションのCOMオブジェクトを取得する方法のサンプルコードがありますので、ご参照ください。  
記事全体を表示
Issue AutoCAD .NET API でフォルダ内の DWG ファイルを順に開いて処理していますが、サイズの大きい図面があると相対的に処理時間が長くなってしまいます。高速化するアイデアはありますか?   Solution   AutoCAD 上に図面を開いて表示する際、描画に多く時間がかかってしまいます。 もし、ユーザインターフェース上に図面を表示する必要がないのであれば、Database.ReadDwgFile メソッドを使って、メモリ上に図面を展開する方法を採用することで、描画にかかかる時間を省いて処理全体を高速化させることが出来ます。   次のコードは、フォルダを指定後にフォルダ内の .dwg ファイルを順に Database.ReadDwgFile メソッドでメモリ展開し、モデル空間のオブジェクト タイプを表示するものです。 [CommandMethod("MyCommand", CommandFlags.Modal)] public void MyCommand() { Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; using (FolderBrowserDialog fd = new FolderBrowserDialog()) { if (fd.ShowDialog() == DialogResult.OK) { string folder = fd.SelectedPath; System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(folder); IEnumerable<System.IO.FileInfo> files = di.EnumerateFiles("*.dwg", System.IO.SearchOption.AllDirectories); if (files == null) return; ed.WriteMessage("\n\t{0}", folder); foreach (System.IO.FileInfo f in files) { string file = f.FullName; ed.WriteMessage("\n - {0}", file); try { using (Database db = new Database(false, true)) { db.ReadDwgFile(file, FileOpenMode.OpenForReadAndAllShare, false, null); using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTableRecord btr = (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead); foreach (ObjectId id in btr) { Entity ent = (Entity)tr.GetObject(id, OpenMode.ForRead); ed.WriteMessage("\n - {0}", ent.GetType().ToString()); } tr.Commit(); } } } catch (System.Exception ex) { ed.WriteMessage(ex.ToString()); } } } } }   なお、Database.ReadDwgFile メソッドの使用時には、次の点に注意が必要です。   Database.ReadDwgFile メソッドで読み込む Database インスタンスを new ステートメントで用意する必要があります。この際、最低限必要なシンボルテーブルとオーナーシップを持つ Database 構造は、読み込む DWG ファイルに定義されているため、コンストラクタには Database.Database([MarshalAs(UnmanagedType.U1)] bool, [MarshalAs(UnmanagedType.U1)] bool)  オーバーロードを使用して、第1パラメーターの値を false にして、読み込む DWG ファイルの Database 構造との競合を抑止する必要があります。 AutoCAD のユーザインターフェースに表示された Database インスタンスは、同図面を表示している MDI 子ウィンドウを閉じると、AutoCAD が Database 領域をメモリから削除します。ただし、Database.ReadDwgFile メソッドでメモリ上に展開した Database インスタンスの領域は、展開したアドイン アプリがメモリーから削除しなければなりません。削除には上、記コードのように using ステートメントを利用してスコープ内の処理後に削除させるか、using ステートメントでスコープ指定せずに Dispose メソッドを明示的に呼び出すことも出来ます。 Database.ReadDwgFile メソッドで読み込んだ Database インスタンス内の DBText オブジェクトや AttributeReference オブジェクトの位置合わせを変更する場合、Database インスタンスを HostApplicationServices.WorkingDatabase プロパティで設定していないと予期しないズレが発生する場合があります。これは、DBText クラス実装が、位置合わせ時に現在の図面データベース(Database)を参照している関係で発生してしまう現象です。Database インスタンスを HostApplicationServices.WorkingDatabase プロパティで設定して処理した場合、処理後にオリジナル(ユーザーインターフェースに開いている図面の Database インスタンスを HostApplicationServices.WorkingDatabase プロパティで再設定する必要があります。
記事全体を表示
長期にわたる Autodesk App Store 運営の知見を反映するかたちで、Windows 版 AutoCAD 2026 でパッケージ バンドルを利用した自動ローダーに重要な変更が加えられています。アドイン アプリ運用時のセキュリティを向上させるのが目的です。   従来のパッケージの検出では次の 3 か所のフォルダを利用することが出来ました。   一般的なインストール フォルダ Windows: %PROGRAMFILES%¥Autodesk¥ApplicationPlugins Mac OS: /Applications/Autodesk/ApplicationAddins すべてのユーザ プロファイル フォルダ Windows: %PROGRAMDATA%¥Autodesk¥ApplicationPlugins Mac OS: 該当なし ユーザ プロファイル フォルダ Windows: %APPDATA%¥Autodesk¥ApplicationPlugins Mac OS: ~/Library/Application Support/Autodesk/ApplicationAddins Windows 版 AutoCAD 2026 では、%ProgramData%\Autodesk\ApplicationPlugins(通常はC:\ProgramData\Autodesk\ApplicationPlugins)フォルダに配置したパッケージ バンドルからのアドイン アプリの自動ロードを抑止します。   このため、従来、%ProgramData%\Autodesk\ApplicationPlugins フォルダを利用していたアドイン アプリは、%PROGRAMFILES%¥Autodesk¥ApplicationPlugins フォルダ、または、%APPDATA%¥Autodesk¥ApplicationPlugins フォルダにパッケージ バンドルの配置を変更する必要があります。   %ProgramData%\Autodesk\ApplicationPlugins フォルダを利用する利点は、Windows 上のすべてのユーザー環境下で、AutoCAD にアドインを識別・ロードさせることが出来る点にありましたが、逆にすべてのユーザが書き込み可能な権限を持つことになり、潜在的なセキュリティの脆弱性を生む可能性があります。   一方、%PROGRAMFILES%¥Autodesk¥ApplicationPlugins フォルダ、または、%APPDATA%¥Autodesk¥ApplicationPlugins フォルダへの書き込みには管理者権限を必要とするため、この点で安全なインストールを保証することが出来ます。   %ProgramData%\Autodesk\ApplicationPlugins フォルダを利用する必要がある場合には、システム レジストリの  \HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R25.1\ACAD-9101\Applications\AcadAutoLoader に DWORD 値 1 を持つ LoadFromProgramData キーを追加することで、従来の動作を復元することも出来ます。ただし、前述のセキュリティ上の観点から非推奨となります。   一度、LoadFromProgramData キーを作成して AutoCAD がパッケージを識別すると、キーの値を 0 にしたり、キー自体を削除しても、パッケージを検出するようになりますのでご注意ください。   特に、デジタル署名されていないアドイン ファイルに対して、初回ロード時に [常にロードする] を選択してしまうと、無条件にパッケージ バンドル内のアドイン アプリをロードするようになってしまいます。     初期状態の動作に戻すには、AutoCAD 2026 の再インストールが必要になります。
記事全体を表示
Issue AutoCAD .NET API でアプリケーションのフォーカス イベントを処理することは出来ますか?   Solution AutoCAD の Application ウィンドウ レベルでは、COM API の Application.AppActivate イベントと Application.AppDeactivate イベントを COM Interop でお使いいただくことが出来ます。 ご参考:COM ベースのイベントを登録する(.NET)   Autodesk.AutoCAD.Interop.AcadApplication? acAppCom = null; [CommandMethod("AddCOMEvent")] public void AddCOMEvent() { acAppCom = (Autodesk.AutoCAD.Interop.AcadApplication)Autodesk.AutoCAD.ApplicationServices.Application.AcadApplication; acAppCom.AppActivate += new _DAcadApplicationEvents_AppActivateEventHandler(appComAppActivate); acAppCom.AppDeactivate += new _DAcadApplicationEvents_AppDeactivateEventHandler(appComAppDeactivate); } [CommandMethod("RemoveCOMEvent")] public void RemoveCOMEvent() { if (acAppCom != null) { acAppCom.AppActivate -= new _DAcadApplicationEvents_AppActivateEventHandler(appComAppActivate); acAppCom.AppDeactivate -= new _DAcadApplicationEvents_AppDeactivateEventHandler(appComAppDeactivate); acAppCom = null; } } public void appComAppActivate() { Autodesk.AutoCAD.ApplicationServices.Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; ed.WriteMessage("\n*** AppActivate ***\n"); } public void appComAppDeactivate() { Autodesk.AutoCAD.ApplicationServices.Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; ed.WriteMessage("\n*** AppDeactivate ***\n"); }  
記事全体を表示
Issue バルーン番号の番号部分を属性定義(タグ名 Index)したブロック定義(ブロック名 MyBlock)があります。     このブロック定義の挿入時にバルーン番号を加算しながら重複のないバルーン番号を持つブロック参照を配置することは可能でしょうか?   Solution 新たな MyBlock ブロック参照の挿入時に、モデル空間に配置されたすべての MyBlock を走査して Index タグ名を持つ属性値(文字列)を取得、最大値となり値をバルーン番号に設定することで実現することが出来るはずです。   次のサンプルは、MyBlock ブロック定義後に同処理を実装する VBA マクロの例です。属性値の妥当性チェックの処理はしていませんが、インデックス番号を保持するタグ名 Index を持つ属性には、半角整数の文字列が設定されているものとしています。IndexOnBlockRef プロシージャを実行してみてください。   ' 挿入時に属性値(インデックス番号)を順番に割り当てるメイン プロシージャ Public Sub IndexOnBlockRef() Dim strBlkName strBlkName = "MyBlock" CreateBlockDef (strBlkName) Dim insPt(0 To 2) As Double insPt(0) = 0: insPt(1) = 0: insPt(2) = 0 Dim retPt As Variant Dim index As Integer Dim blockRefObj As AcadBlockReference On Error Resume Next Do Err.Clear retPt = ThisDrawing.Utility.GetPoint(, vbCrLf & "ブロックの挿入点を指示 : ") If Err Then Exit Do End If Set blockRefObj = ThisDrawing.ModelSpace.InsertBlock(retPt, strBlkName, 1#, 1#, 1#, 0) index = FindMaxIndexAttribute() Call SetIndexAttribute(blockRefObj, index + 1) Loop End Sub ' 属性定義(タグ名 Index)を含むブロック定義 MyBlock を定義する関数 Public Function CreateBlockDef(strBlkName As String) Dim blockObj As AcadBlock Dim insPt(0 To 2) As Double insPt(0) = 0: insPt(1) = 0: insPt(2) = 0 On Error Resume Next Set blockObj = ThisDrawing.Blocks.Item(strBlkName) If Err Then Set blockObj = ThisDrawing.Blocks.Add(insPt, strBlkName) Dim circleObj As AcadCircle Set circleObj = blockObj.AddCircle(insPt, 100#) Dim attributeObj As AcadAttribute Set attributeObj = blockObj.AddAttribute(100#, acAttributeModePreset, "インデックス?", insPt, "Index", "0") attributeObj.Alignment = acAlignmentMiddleCenter End If End Function ' 与えられたブロック参照のタグ名 Index の属性値(整数)を返す関数 Public Function GetIndexAttribute(blockRefObj) As Integer Dim varAttributes As Variant varAttributes = blockRefObj.GetAttributes Dim strAttribute As String strAttribute = "-1" For Each blockAttr In varAttributes If blockAttr.TagString = "Index" Then strAttribute = blockAttr.TextString Exit For End If Next GetIndexAttribute = CInt(strAttribute) End Function ' モデル空間に配置されたすべての MyBlock ブロック参照を走査して割り当てられた最大の Index の属性値(整数)を返す関数う Public Function FindMaxIndexAttribute() As Integer Dim sset As AcadSelectionSet On Error Resume Next Set sset = ThisDrawing.SelectionSets.Item("ssblocks") If Not Err Then sset.Delete End If Set sset = ThisDrawing.SelectionSets.Add("ssblocks") Dim FilterType(1) As Integer Dim FilterData(1) As Variant FilterType(0) = 0 FilterData(0) = "Insert" FilterType(1) = 2 FilterData(1) = "MyBlock" sset.Select acSelectionSetAll, , , FilterType, FilterData Dim index As Integer index = -1 Dim maxIndex As Integer maxIndex = -1 If sset.Count > 0 Then Dim entityObj As AcadEntity For Each entityObj In sset Dim blockRefObj As AcadBlockReference Set blockRefObj = entityObj index = GetIndexAttribute(blockRefObj) If maxIndex < index Then maxIndex = index End If Next End If FindMaxIndexAttribute = maxIndex End Function ' 与えられたブロック参照1のタグ名 Index の属性値(整数)を設定する関数 Public Function SetIndexAttribute(blockRefObj, index) Dim varAttributes As Variant varAttributes = blockRefObj.GetAttributes For Each blockAttr In varAttributes If blockAttr.TagString = "Index" Then blockAttr.TextString = CStr(index) Exit For End If Next End Function     既に挿入配置済のブロック参照 My Block に新たにインデクス番号の属性値を割り当てる場合には、前述のコードに次の AssignIndexToBlockRef プロシージャを追加・実行することでブロック参照を順に選択して値を更新することも出来ます。   ' 配置済のブロック参照に指定値から属性値(インデックス番号)を順番に割り当てるメイン プロシージャ Public Sub AssignIndexToBlockRef() Dim strBlkName strBlkName = "MyBlock" Dim startIndex As Integer startIndex = FindMaxIndexAttribute() On Error Resume Next Err.Clear Dim returnInt As Integer ThisDrawing.Utility.InitializeUserInput (4) ' disallow negative returnInt = ThisDrawing.Utility.GetInteger(vbCrLf & "割り当てるインデクス開始値を入力<" & CStr(startIndex + 1) & "> : ") If Err = 0 Then If returnInt <= startIndex Then MsgBox ("指定した値と同じ、または、大きいインデックス値を持つブロック参照が既に存在します" & vbCrLf & _ "既に配置済のブロック参照とインデックス値が重複してしまう可能性があります") Exit Sub End If startIndex = returnInt - 1 End If Dim returnObj As AcadObject Dim basePnt As Variant Do Err.Clear ThisDrawing.Utility.GetEntity returnObj, basePnt, "ブロック参照を選択:" If Err = 0 Then If returnObj.EntityName = "AcDbBlockReference" Then Dim blockRefObj As AcadBlockReference Set blockRefObj = returnObj If blockRefObj.Name = strBlkName Then startIndex = startIndex + 1 Call SetIndexAttribute(blockRefObj, startIndex) End If End If Else Exit Do End If Loop End Sub    
記事全体を表示
Issue AutoCAD .NET API を利用してサーフェス同士を結合することは出来ますか?   Solution AutoCAD .NET API には、サーフェス同士を結合する ObjectARX の AcDbSurface::booleanUnion() メンバ関数ラッパーである サーフェス同士を結合する Surface.BooleanUnion メソッドが用意されています。   次の C# 例は、Surface.BooleanUnion メソッドでサーフェス同士を結合するものです。   [CommandMethod("MyCommand", CommandFlags.Modal)] public void MyCommand() // This method can have any name { Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; PromptEntityOptions peo = new PromptEntityOptions("\n結合するサーフェスを選択:"); peo.SetRejectMessage("\nサーフェスを選択してください..."); peo.AddAllowedClass(typeof(Autodesk.AutoCAD.DatabaseServices.Surface), false); PromptEntityResult per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; ObjectId objId1 = per.ObjectId; peo = new PromptEntityOptions("\n結合させたいサーフェスを選択:"); peo.SetRejectMessage("\nサーフェスを選択してください..."); peo.AddAllowedClass(typeof(Autodesk.AutoCAD.DatabaseServices.Surface), false); per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; ObjectId objId2 = per.ObjectId; try { using (Transaction tr = db.TransactionManager.StartTransaction()) { Autodesk.AutoCAD.DatabaseServices.Surface surf1 = tr.GetObject(objId1, OpenMode.ForWrite) as Autodesk.AutoCAD.DatabaseServices.Surface; Autodesk.AutoCAD.DatabaseServices.Surface surf2 = tr.GetObject(objId2, OpenMode.ForWrite) as Autodesk.AutoCAD.DatabaseServices.Surface; Autodesk.AutoCAD.DatabaseServices.Surface surf = surf1.BooleanUnion(surf2); if (surf == null) { if (surf1 != null) { surf1.ColorIndex = 5; surf1.LineWeight = LineWeight.LineWeight100; } if (surf2 != null) { surf2.Erase(); } } else { surf1.Erase(); surf2.Erase(); ObjectId objId = SymbolUtilityServices.GetBlockModelSpaceId(db); BlockTableRecord btr = tr.GetObject(objId, OpenMode.ForWrite) as BlockTableRecord; surf.ColorIndex = 5; surf.LineWeight = LineWeight.LineWeight100; btr.AppendEntity(surf); tr.AddNewlyCreatedDBObject(surf, true); } tr.Commit(); } } catch(Autodesk.AutoCAD.Runtime.Exception ex) { ed.WriteMessage("\n" + ex.Message); } }  
記事全体を表示
Issue サーフェスの外形エッジやパス形状をオブジェクトとして再作成することは出来ますか?   Solution サーフェスの外形エッジやパス形状はサーフェスのサブエンティティ(FullSubentityPath)から形状を模倣して再作成することが出来ます。  Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; PromptEntityOptions peo = new PromptEntityOptions("\nサーフェスを選択:"); peo.SetRejectMessage("\nサーフェスを選択してください..."); peo.AddAllowedClass(typeof(Autodesk.AutoCAD.DatabaseServices.Surface), false); PromptEntityResult per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; using (Transaction tr = db.TransactionManager.StartTransaction()) { ObjectId objId = per.ObjectId; Entity ent = tr.GetObject(objId, OpenMode.ForWrite) as Entity; ObjectId[] entId = new ObjectId[] { ent.ObjectId }; IntPtr pSubentityIdPE = ent.QueryX(AssocPersSubentityIdPE.GetClass(typeof(AssocPersSubentityIdPE))); if (pSubentityIdPE == IntPtr.Zero) return; AssocPersSubentityIdPE subentityIdPE = AssocPersSubentityIdPE.Create(pSubentityIdPE, false) as AssocPersSubentityIdPE; SubentityId[] edgeIds = subentityIdPE.GetAllSubentities(ent, SubentityType.Edge); objId = SymbolUtilityServices.GetBlockModelSpaceId(db); BlockTableRecord btr = tr.GetObject(objId, OpenMode.ForWrite) as BlockTableRecord; foreach (SubentityId subentId in edgeIds) { FullSubentityPath path = new FullSubentityPath(entId, subentId); Entity edge = ent.GetSubentity(path); if (edge != null) { edge.ColorIndex = 3; edge.LineWeight = LineWeight.LineWeight100; btr.AppendEntity(edge); tr.AddNewlyCreatedDBObject(edge, true); } } tr.Commit(); }  
記事全体を表示
Issue AutoLISP のリスト操作で拡張エンティティ データを付加・参照・削除するには、それぞれ、どのようなコードを作成すればいいでしょうか?   Solution 拡張エンティティ データは、AutoCAD 図面内の任意のオブジェクトに任意のカスタム データの付加、参照する手法です。AutoLISP でも、他の API 同様、拡張エンティティ データの付加・参照・削除を実装することが出来ます。   次のコードは、リスト操作で拡張エンティティ データを付加する AddXData コマンド、参照する GetXData コマンド、削除する RemoveXData コマンドの例です。   ;https://help.autodesk.com/view/OARX/2025/JPN/?guid=GUID-A94BC605-5517-437F-A6FE-D3EB8116A01A ; 拡張エンティティデータの追加例 (defun C:AddXData (/) (setq ename (car (entsel))) (if (= ename nil) (exit) ) (setq appname "Test_App") (if (= (tblsearch "APPID" appname) nil) (if (= (regapp appname) nil) (princ (strcat "\n" appname " アプリケーション名が登録出来ません... ")) ) ) (setq edata (entget ename (list "*"))) (setq exist (cdr (assoc -3 edata))) (if (= exist nil) (progn ; 他の拡張エンティティデータも付加されていない場合 (setq xdata (list (list -3 (list appname (cons 1002 "{") (cons 1000 "あいうえお") ; 文字列データ (cons 1040 999.99) ; 実数データ (cons 1070 1111) ; 整数データ (cons 1002 "}") ) ) ) ) (setq newdata (append edata xdata)) (entmod newdata) ) (progn ; 既になんらかの拡張エンティティデータが付加されている場合 (foreach xdatas exist (progn (if (= appname (car xdatas)) (progn (princ (strcat "\n既に " appname " データが付加されています...")) (exit) ; 処理中断 ) ) (setq xdata (list (list appname (cons 1002 "{") (cons 1000 "あいうえお") ; 文字列データ (cons 1040 999.99) ; 実数データ (cons 1070 1111) ; 整数データ (cons 1002 "}") ) ) ) (setq xdatas (append (list -3) (append exist xdata))) (setq newdata (subst xdatas (append (list -3) exist) edata)) (entmod newdata) (princ (strcat "\n" appname " データが付加しました...")) ) ) ) ) (princ) ) ; 拡張エンティティデータの取得例 (defun C:GetXData (/) (setq ename (car (entsel))) (if (= ename nil) (exit) ) (setq edata (entget ename (list "*"))) (setq exist (cdr (assoc -3 edata))) (if (/= exist nil) (progn (foreach xdatas exist (progn (princ (strcat "\n*** " (car xdatas))) (setq xdata (cdr xdatas)) (foreach element xdata (setq code (car element)) (setq value (cdr element)) (cond ((= code 1000) (princ (strcat "\n文字列値:" value)) ) ((= code 1040) (princ (strcat "\n実数値:" (rtos value))) ) ((= code 1070) (princ (strcat "\n整数値:" (itoa value))) ) ) ) ) ) ) (progn (princ "\n拡張エンティティデータが付加されていません...") ) ) (princ) ) ; 拡張エンティティデータの削除例 (defun C:RemXData (/) (setq ename (car (entsel))) (if (= ename nil) (exit) ) (setq appname "Test_App") (if (= (tblsearch "APPID" appname) nil) (princ (strcat "\n" appname " アプリケーション名が登録されていません... ")) ) (setq edata (entget ename (list "*"))) (setq exist (cdr (assoc -3 edata))) (if (/= exist nil) (progn (foreach xdatas exist (progn (if (= appname (car xdatas)) (progn (setq xdata (list (list appname) ) ) (setq xdatas (append (list -3) (append exist xdata))) (setq newdata (subst xdatas (append (list -3) exist) edata)) (entmod newdata) (princ (strcat "\n付加された " appname " データを削除しました...")) ) ) ) ) ) ) (princ) ) 他の 3rd party アプリケーションや AutoCAD 自身も拡張エンティティデータ利用していますので、既に付加されている拡張エンティティデータに影響を与えずに独自データを付加、削除するようご留意ください。
記事全体を表示
Issue AutoCAD .NET API で SURFEXTEND[延長サーフェス] コマンド と同じような処理は出来ますか?   Solution サーフェス エッジの延長は、Surface.ExtendEdges メソッド で実装することが出来ます。   次の C# コードは、選択した1つのサーフェス エッジを 100 ポイント延長するものです。 SURFEXTEND[延長サーフェス] コマンド と同様に、エッジの延長でサーフェスに自己交差すると延長に失敗する場合があります。   var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; try { PromptSelectionOptions pso = new PromptSelectionOptions(); pso.MessageForAdding = "\n延長するサーフェス エッジを選択:"; pso.SingleOnly = true; pso.SinglePickInSpace = true; pso.ForceSubSelections = true; PromptSelectionResult psr = ed.GetSelection(pso); if (psr.Status != PromptStatus.OK) return; SelectionSet ss = psr.Value; SelectedObject surfedge = ss[0]; ObjectId surfId = ss.GetObjectIds()[0]; if (!surfId.ObjectClass.IsDerivedFrom(RXClass.GetClass(typeof(Autodesk.AutoCAD.DatabaseServices.Surface)))) { ed.WriteMessage("\nサーフェス エッジを選択を選択してください ..."); return; } using (var tr = db.TransactionManager.StartTransaction()) { var surf = tr.GetObject(surfId, OpenMode.ForWrite) as Autodesk.AutoCAD.DatabaseServices.Surface; if (surf != null) { if (surfedge.GetSubentities()[0].FullSubentityPath.SubentId.Type != SubentityType.Edge) { ed.WriteMessage("\n{0} が選択されました ...\nサーフェス エッジを選択を選択してください ...", surfedge.GetSubentities()[0].FullSubentityPath.SubentId.Type); tr.Abort(); return; } FullSubentityPath[] edges = { surfedge.GetSubentities()[0].FullSubentityPath }; surf.ExtendEdges(edges, 100.0, Autodesk.AutoCAD.DatabaseServices.Surface.EdgeExtensionType.ExtendEdge, true); } tr.Commit(); } } catch (Autodesk.AutoCAD.BoundaryRepresentation.Exception ex) { ed.WriteMessage("\nエラー: {0}\r\n at {1}", ex.Message, ex.StackTrace); }   なお、SURFEXTEND[延長サーフェス] コマンド によるサーフェス延長とは異なり、Surface.ExtendEdges メソッド  による延長では、延長されたサーフェス部分は、無条件に元のサーフェスに結合された状態になります。 SURFEXTEND[延長サーフェス] コマンド では、延長されたサーフェスを元のサーフェスと結合(合成)するか、別のサーフェスとして追加するか(既定値)をコマンド プションで指定することが出来ます。  
記事全体を表示
Issue VBA では表を TABLE オブジェクトで作成することが出来ますが、データ値にオブジェクトのプロパティをフィールドとして利用、表の内容を最新に保つようなことは出来ますか?   Solution  TABLE オブジェクト のデータセルにフィールド文字を挿入することは可能です。どのようなフィールド文字列を値に設定するべきかは、FIELD[フィールド] コマンド 表示される [フィールド] ダイアログで、特定のオブジェクトと同オブジェクト タイプで利用可能なプロパティを選択すると、ダイアログ下部の「フィールド式」に表示される内容で取得することが出来ます。   このとき、特定のオブジェクトは AutoCAD API の識別子の 1つである ObjectId で関連付けされますので、VBA マクロでフィールド式をセルに指定する際に、適宜変更する必要があります。     次のマクロは、モデル空間に作図されているすべてのオブジェクトのハンドル番号とオブジェクト タイプ、オブジェクトの色を表に書き込むものです。色の情報がフィールド式で指定されているので、作図後に REGEN[再作図] コマンド を実行すると、値が更新されるようになります。   Option Explicit Public Sub FieldTable() ' 新しい表スタイルの作成 On Error Resume Next Dim oTblDict As AcadDictionary Set oTblDict = ThisDrawing.Dictionaries.Item("ACAD_TABLESTYLE") Dim oTblStyle As AcadTableStyle Set oTblStyle = oTblDict.Item("MyTableStyle") If Err Then ' 新しい表スタイル "MyTableStyle" を作成 Err.Clear Set oTblStyle = oTblDict.AddObject("MyTableStyle", "AcDbTableStyle") ' タイトル欄の設定(背景色:青、文字色:白、文字高さ:4.0) Dim oColor As AcadAcCmColor Set oColor = AcadApplication.GetInterfaceObject("AutoCAD.AcCmColor.25") oColor.ColorIndex = acWhite oTblStyle.SetColor acTitleRow, oColor oColor.ColorIndex = acBlue oTblStyle.SetBackgroundColorNone acTitleRow, False oTblStyle.SetBackgroundColor acTitleRow, oColor oTblStyle.SetTextHeight acTitleRow, 4# oTblStyle.TitleSuppressed = False ' 列見出し欄の色設定(背景色:白、文字色:緑、文字高さ:2.5) oColor.ColorIndex = acWhite oTblStyle.SetColor acHeaderRow, oColor oColor.ColorIndex = acGreen oTblStyle.SetBackgroundColorNone acHeaderRow, True oTblStyle.SetBackgroundColor acHeaderRow, oColor oTblStyle.SetTextHeight acHeaderRow, 2.5 oTblStyle.HeaderSuppressed = False ' データ欄の色設定(背景色:なし、文字色:白、文字高さ:2) oColor.ColorIndex = acWhite oTblStyle.SetColor acDataRow, oColor oTblStyle.SetBackgroundColorNone acDataRow, True oTblStyle.SetTextHeight acDataRow, 2 oTblStyle.SetAlignment acDataRow, acMiddleCenter End If ' 配置基点を指示 Dim ptBase As Variant ThisDrawing.Utility.InitializeUserInput 1 ptBase = ThisDrawing.Utility.GetPoint(, vbCrLf & "表の配置点を指定:") ' 表特性の算出 Dim nRow As Integer Dim nCol As Integer Dim dHeight As Double nRow = ThisDrawing.ModelSpace.Count + 2 ' タイトル+列見出し nCol = 3 dHeight = ThisDrawing.GetVariable("TEXTSIZE") ' 現在のレイアウトに表を配置 Dim oTbl As IAcadTable Set oTbl = ThisDrawing.ActiveLayout.Block.AddTable(ptBase, nRow, nCol, dHeight, dHeight * 10#) oTbl.RegenerateTableSuppressed = True oTbl.StyleName = "MyTableStyle" oTbl.GenerateLayout ' タイトルの設定 oTbl.SetText 0, 0, "モデル空間図形の色一覧" oTbl.SetRowHeight 0, 7# oTbl.SetText 1, 0, "ハンドル番号" oTbl.SetText 1, 1, "クラス名" oTbl.SetText 1, 2, "色" oTbl.SetRowHeight 1, 5# ' データの設定 Dim nRowCnt As Integer Dim strField As String Dim oEnt As AcadEntity nRowCnt = 2 For Each oEnt In ThisDrawing.ModelSpace ' ハンドル番号 oTbl.SetText nRowCnt, 0, oEnt.Handle ' クラス名 oTbl.SetText nRowCnt, 1, oEnt.ObjectName ' フィールド式(オブジェクトID で特定したオブジェクトの色) strField = "%<\AcObjProp Object(%<\_ObjId " & CStr(oEnt.ObjectID) & ">%).TrueColor>%" oTbl.SetText nRowCnt, 2, strField ' 行高さの設定 oTbl.SetRowHeight nRowCnt, 5# ' 行カウンタ nRowCnt = nRowCnt + 1 Next oTbl.RegenerateTableSuppressed = False End Sub   このマクロコードでは、表スタイルを登録して表に使用しています。表スタイルの内容と図面の尺度が適切でない場合がありますので、必要に応じて文字高さ等を変更してください。AutoCAD VBA では、表スタイルは TableStyle オブジェクト で作成・編集することが出来ます。この例では、椅子のブロック参照の色を水色(cyan)から青(blue)に変更しています。    
記事全体を表示
Issue VBA マクロで特定のブロック名パターンを持つブロックのブロック属性を得たいのですが、可能でしょうか? 例えば、図面内の A で始まるブロック名を持つすべてのブロックのみを対象にする、といった方法です。   Solution 特定のブロック名パターンに検索には、選択セットのフィルタ リストにアスタリスク記号でワイルドカード(*)を組み合わせて指定することが出来ます。A で始まるブロック名を持つブロックは、A* で指定可能です。   次のコードは、ワイルドカードを含めたブロック名パターンを指定して、対象ブロック参照を取得、ブロック属性の有無をチェックして、ブロック属性が含まれる場合に属性のタグと値を表示するものです。なお、*fix* や *tub など、ワイルドカードはブロック名中、どこに置いても指定することが出来ます。   Public Sub GetBlockAttribute2() On Error Resume Next Dim sset As AcadSelectionSet ThisDrawing.SelectionSets.Item("ssblocks").Delete Set sset = ThisDrawing.SelectionSets.Add("ssblocks") ThisDrawing.Utility.InitializeUserInput NoNull returnString = ThisDrawing.Utility.GetString(True, "対象のブロック名を入力 [すべて (*) ] : ") If returnString = "" Then returnString = "*" End If Dim FilterType(1) As Integer Dim FilterData(1) As Variant FilterType(0) = 0 FilterData(0) = "Insert" FilterType(1) = 2 FilterData(1) = returnString sset.Select acSelectionSetAll, , , FilterType, FilterData If sset.Count > 0 Then Dim ent As AcadEntity For Each ent In sset Dim blkRef As AcadBlockReference Set blkRef = ent ThisDrawing.Utility.Prompt vbCrLf & blkRef.Name & " ブロック参照が検出されました." Dim varAttributes As Variant varAttributes = blkRef.GetAttributes If UBound(varAttributes) < 0 Then ThisDrawing.Utility.Prompt vbCrLf & " ブロック参照に属性がありません..." Else Dim strAttributes As String For Each blkAttr In varAttributes strAttributes = "" strAttributes = strAttributes & " タグ名: " & blkAttr.TagString & " - 属性値 " & blkAttr.TextString & vbLf & " " ThisDrawing.Utility.Prompt vbCrLf & strAttributes Next End If Next Else ThisDrawing.Utility.Prompt vbCrLf & returnString & " ブロック名パターンに合致するブロック参照がありません..." End If End Sub   次の例では、ブロック名パターン * で全ブロックのブロック属性を、A* で A で始まるブロック名を持つブロック参照のブロック属性を表示しています。  
記事全体を表示
質問 AutoCADのActiveX APIを.NET アプリケーションから実行すると「呼び出し先が呼び出しを拒否しました。 (HRESULT からの例外:0x80010001 (RPC_E_CALL_REJECTED))」エラーが発生することがある。 毎回同じAutoCADのAPIの実行でエラーが発生するわけではなく、また発生頻度もまちまち(発生せずに実行できる場合もある)で規則性は見られない状況。   回答 AutoCADがビジー状態(=何らかの処理中)にあるために、Remote Procedure Call (ここでは、カスタムアプリーケーションからの、AutoCADのAPIの呼び出し)を受け付けることが出来ない状態であるため、APIの呼出しが拒否されている可能性が高い状況です。 外部プロセスからのAcitveX APIの実行は、Windowsの低レベルレイヤーではクライアントアプリケーション(この場合は外部プロセスのカスタムアプリ)が、サーバ側のアプリケーション(この場合はAutoCAD)のメインスレッドに対してWindows Messageを送信する形で、プロセス間でのAPIの実行が行われています。   このため、AutoCAD側のメインスレッドが何らかの別の処理を行っている状態にある場合、Windows Messageが処理されず呼び出し元のアプリケーションは待ちの状態となります。 呼び出し元のアプリケーションが無限に処理待ちの状態となってしまうことを防ぐ機構として、一定時間の経過後に例外を送信する仕組みがActiveXに備わっており、タイトルにあるようなエラーとして出現いたします。   通常は、AcitveXの実行系のデフオルトのリトライ処理で問題なくAPIを実行することが出来ますが、実行環境の状態や処理内容に依存して、エラーが発生するケースがあります。このような場合カスタムプログラム側でIMessageFilterのRetryRejectedCallで、ActiveX APIの呼び出しをリトライをする機構を実装することにより、状況が改善する可能性があります。   以下のブログ記事にて、C#でのIMessageFilterのリトライ処理を実装のサンプルコードが掲載されております。 https://www.keanw.com/2010/02/handling-com-calls-rejected-by-autocad-from-an-external-net-application.html   また、以下のサンプルコードは、上記ブログ記事の IMessageFilterのリトライ処理部をVB.NETに置き換えたものとなります。 Imports System.Runtime.InteropServices Imports Microsoft.Win32 Imports System Imports Microsoft.VisualBasic <ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000016-0000-0000-C000-000000000046")> Public Interface IMessageFilter <PreserveSig()> Function HandleInComingCall(ByVal dwCallType As Integer, ByVal hTaskCaller As IntPtr, ByVal dwTickCount As Integer, ByVal lpInterfaceInfo As IntPtr) As Integer <PreserveSig()> Function RetryRejectedCall(ByVal hTaskCallee As IntPtr, ByVal dwTickCount As Integer, ByVal dwRejectType As Integer) As Integer <PreserveSig()> Function MessagePending(ByVal hTaskCallee As IntPtr, ByVal dwTickCount As Integer, ByVal dwPendingType As Integer) As Integer End Interface Public Class Form1 Implements IMessageFilter Public Sub New() InitializeComponent() Dim oldFilter As IMessageFilter = Nothing CoRegisterMessageFilter(Me, oldFilter) End Sub Private Function IMessageFilter_HandleInComingCall(ByVal dwCallType As Integer, ByVal hTaskCaller As IntPtr, ByVal dwTickCount As Integer, ByVal lpInterfaceInfo As IntPtr) As Integer Implements IMessageFilter.HandleInComingCall Return 0 End Function Private Function IMessageFilter_RetryRejectedCall(ByVal hTaskCallee As IntPtr, ByVal dwTickCount As Integer, ByVal dwRejectType As Integer) As Integer Implements IMessageFilter.RetryRejectedCall 'retry in a second. Return 1000 End Function Private Function IMessageFilter_MessagePending(ByVal hTaskCallee As IntPtr, ByVal dwTickCount As Integer, ByVal dwPendingType As Integer) As Integer Implements IMessageFilter.MessagePending Return 1 End Function <DllImport("ole32.dll")> Private Shared Function CoRegisterMessageFilter(ByVal lpMessageFilter As IMessageFilter, ByRef lplpMessageFilter As IMessageFilter) As Integer End Function End Class    
記事全体を表示
現象 Inventor 2025で、.NET FrameworkをターゲットにしてビルドしたC#アプリケーションからiLogicを実行すると、System.Runtime.InteropServices.COMException HResult=0x80131165 Message=Typelib エクスポート: タイプ ライブラリが登録されていません。 (HRESULT からの例外:0x80131165) Source=mscorlibが発生する。 エラーはiLogicの実行だけではなく、iLogicのAPIを実行すると発生する。   なお、同じソースコードでInventor 2024以下のバージョンではエラーは発生せずにiLogicを実行することが出来る。   以下のサンプルコードの場合、 auto.RunRule(doc, "Rule1");でエラーとなる。 var type = Type.GetTypeFromProgID(ProgId) ?? throw new Exception($"Failed to get type from '{ProgId}'"); var app = (Inventor.Application)Activator.CreateInstance(type); app.Visible = true; var addin = app.ApplicationAddIns.get_ItemById(ILogicAddInClassId); try { if (!addin.Activated) { addin.Activate(); } } catch (Exception ex) { throw new Exception("Failed activating iLogic addin", ex); } var doc = app.Documents.Open(@"<path to ipt file>"); dynamic auto = addin.Automation; auto.RunRule(doc, "Rule1"); 診断 InventorのiLogic APIはActiveX/COM Automationを用いてAPIを公開しています。Inventor 2025の場合Inventor 本体が従来の.NET Frameworkから.NET(.NET 8)を使用するよう更新がされており、iLogic機能を提供するコンポーネントも.NET 8を使用しております。   一方で、.NETでは.NET系の言語からCOM/ActiveX を実行する際の利用されるCOM Callable WrapperにITypeInfoのサポートを削除する変更が加えられています(.NET FrameworkではITypeInfoのサポートされていた)。   このため、.NET FrameworkをターゲットにしてビルドしたC#アプリケーションから、iLogicのAPIを遅延バインディング(dynamicキーワドを用いる形で、iLogic APIを呼び出す)を用いて利用すると、.NET で実装されていないITypeInfo情報を取得する処理(GetITypeInfoFromIDispatch 関数)が実行され、エラーが発生します。 解決策 以下のような方法で、エラーの回避が可能です。   1.対象のカスタムアプリケーションを.NETを使用するようにマイグレーションを行う   2.何らかの事情で、.NET へのマイグレーションが出来ない場合、対象のアプリーションをVB.NETを用いて開発する。VB.NETの場合、上述の問題となっている関数GetITypeInfoFromIDispatchを利用しない形で、遅延バインディングによるAPI実行を行っているため、エラーとなりません。   3.Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateCallを利用する 2のVB.Netでアプリケーションを開発した場合、VB.NETが内部的に利用している、遅延バインディングでのAPI実行を行う、低レベルのAPIを使用することでC#からもiLogic APIの実行が可能となります。   NewLateBinding.LateCall(auto, ((object)auto).GetType(), "RunRule1", new object[] { doc, "Rule" }, null, null, null, false);   4.dynamic 変数をobject型にCastしたのちにGetType().InvokeMember()を行う ((object)auto).GetType().InvokeMember("RunRule", BindingFlags.Public | BindingFlags.InvokeMethod, null, auto, new object[] { doc, "Rule1" });   3及び4の方法は、ソースコードの記述が非常に煩雑でわかりにくくなるため、iLogic APIの利用箇所が少ない場合などの回避としてご利用ください。      
記事全体を表示