CでAutocad甚のプラグむンを䜜成した最初の経隓

背景


私は初心者開発者であり、「孊校」レベルのC ++知識、Cでのプログラミング経隓がわずか2幎、AutoCADでの経隓がれロです。
最近、圌らは境界蚈画を䜜成/倉曎し、察応するMS Word / XMLドキュメントを準備するように蚭蚈されたLISP auto-cadプログラムを倉曎するように頌みたした。バグを修正し、新しい機胜を远加したす。
Lispでのプログラムの読みやすさ少なくずも私にずっおはが望たれおいるので、私はこれをより理解しやすい蚀語で曞き盎すこずにしたした。
なぜなら ミリ秒単䜍で速床を䞊げる必芁はありたせんでした。C++をスキップし、Cで解決したした



この蚘事を曞いおいるのは


1. Autocadに぀いお孊んだこずを棚に頭の䞭に入れたす。
2.私のように、ごくわずかなドキュメントを読む人を助けるため。
3.「あなたは間違っおいたす。そうする方が簡単で良い」などのコメント情報を入手しおください。

始めたしょう。 プラグむンを䜜成したす。


ClassLibraryテンプレヌトを䜿甚しおCプロゞェクトを䜜成する
プログラムフォルダにある管理されたAutocad APIラむブラリぞのリンクを远加したす。
私の堎合、これは次のずおりです。
C\ Program Files \ AutoCAD 2007 \ acdbmgd.dll
C\ Program Files \ AutoCAD 2007 \ acmgd.dll

䜕かをするクラスを䜜成したす。
using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.ApplicationServices; namespace AutocadPlugin { public class test : IExtensionApplication { [CommandMethod("hello")] public void Helloworld() { var editor = Application.DocumentManager.MdiActiveDocument.Editor; editor.WriteMessage("  Autocad "); } public void Initialize() { var editor = Application.DocumentManager.MdiActiveDocument.Editor; editor.WriteMessage(" .."+Environment.NewLine); } public void Terminate() { } } } 


IExtensionApplicationからの継承はオプションであり、Autocadはラむブラリ内のすべおのパブリッククラスを自動的に取埗したすが、先ほど述べたように、より高速になりたす。 さらに、Initialize / Terminateプラグむンを制埡できたす。

コンパむルし、autocadを実行し、netloadコマンドでプラグむンをロヌドしたすマネヌゞDLL遞択りィンドりが開きたす
これで、helloコマンドを入力するず、予想される応答が埗られたす。

Autocadアプリケヌション構造


画面に衚瀺されるもの、 ゚ンティティから継承されたグラフィックオブゞェクト
目に芋える情報オブゞェクトに加えお、 レむダヌ 、 ç·šçš® 、 寞法スタむル 、 衚スタむルなどの䞍可芖の情報オブゞェクトがありたす。
これはすべお、 デヌタベヌステヌブルレコヌド 、 TYPETableタむプリポゞトリおよびTYPETableRecordタむプクラスに栌玍されたす 。

オブゞェクト識別子



詳现 ハンドルはAutoCADセッション間で氞続的であるため、埌で図面の曎新に䜿甚する必芁のある倖郚ファむルに図面情報を゚クスポヌトする必芁がある堎合、オブゞェクトにアクセスする最良の方法です。 デヌタベヌス内のオブゞェクトのObjectIdは、デヌタベヌスがメモリにロヌドされおいる間のみ存圚したす。 デヌタベヌスが閉じられるず、オブゞェクトに割り圓おられたオブゞェクトIDは存圚しなくなり、次回デヌタベヌスを開いたずきに異なる可胜性がありたす。

デヌタベヌスを操䜜する


通垞、デヌタベヌスの操䜜はトランザクションを䜿甚しお行われたす。 オブゞェクトがデヌタベヌスから芁求され、倉曎され、コミットトランザクションが保存されたす。
トランザクション䞭、3぀のForRead、ForWrite、ForNotifyモヌドのいずれかでデヌタベヌスからオブゞェクトが芁求されたす。
最初の2぀の目的は明らかであり、3番目は䜕らかの圢でむベントのメカニズムに䜿甚されたす。
ForWriteモヌドでは、autocadはトランザクション内の倉曎を砎棄できる远加のオブゞェクトを䜜成したす。
「ForRead」ずしお開かれたオブゞェクトを倉曎する堎合、そのUpgradeOpenメ゜ッドが呌び出されたす。
既に倉曎モヌドで開いおいるオブゞェクトでこのメ゜ッドを呌び出すず、メ゜ッドは䟋倖をスロヌしたす。

ObjectIdによっおPolylineオブゞェクトを取埗する䟋
 // UPDATE:     ,   .     ,     UpgradeOpen  , ..    public static Polyline GetPolylineByEname(ObjectId ename) { if (ename == ObjectId.Null) return null; Polyline polyline = null; var db = Application.DocumentManager.MdiActiveDocument.Database; using (var transaction = db.TransactionManager.StartTransaction()) { return transaction.GetObject(ename, OpenMode.ForRead) as Polyline; } } 


トランザクションはネストでき、最䞊䜍のトランザクションがキャンセルされるず、すべおの子もキャンセルされたす。
最初は、長い間、゚ラヌの堎所を特定できたせんでした。画像の倉曎は保存されたせんでした。 結局のずころ、私は䞊のトランザクションを閉じるのを忘れおいたした。
むラスト
 public void OwnerTransFunction() { var db = Application.DocumentManager.MdiActiveDocument.Database; using (var transaction = db.TransactionManager.StartTransaction()) { //   ChangePolylineInChildTransation() } } public void ChangePolyline() { var db = Application.DocumentManager.MdiActiveDocument.Database; using (var transaction = db.TransactionManager.StartTransaction()) { //   //   transaction.Commit(); //       ,       Commit'a    } } 


曎新コメントで瀺唆されおいるように、トランザクションをキャンセルする必芁がある堎合を陀いお、垞にtransaction.Commitを呌び出すこずをお勧めしたす。 トランザクションがコミットされおいない堎合、transaction.Abortが自動的に呌び出され、远加のコストが発生したす。

蟞曞


䞍芁なファむルを䜜成しないように、蟞曞を䜿甚しおデヌタをDWGに保存したした
図面で2皮類の蟞曞-NamedObjectDictionaryずExtensionDictionaryに 出䌚いたした
蟞曞のデヌタはレコヌドレコヌドに栌玍され、レコヌドには型付きの倀が栌玍されたす。
テキストキヌのデヌタをアドレス指定したす。

NamedObjectDictionary-グロヌバルな描画蟞曞。 ドキュメントの䜜成時に自動的に生成されたす。
䜿甚するメむンオブゞェクトぞのリンクを保存するために䜿甚したした。

ExtensionDictionary-各オブゞェクトの蟞曞。手動で䜜成する必芁がありたす。
entity.ExtensionDictionaryフィヌルドをObjectId.Nullず比范するこずにより、その存圚を確認できたす。

ExtensionDictionaryから文字列倀を曞き蟌んで取埗する䟋
 public static void SetExtDictionaryValueString(ObjectId ename, string key, string value) { if (ename == ObjectId.Null) throw new ArgumentNullException("ename"); if (String.IsNullOrEmpty(key)) throw new ArgumentNullException("key"); var doc = Application.DocumentManager.MdiActiveDocument; using (var transaction = doc.Database.TransactionManager.StartTransaction()) { var entity = transaction.GetObject(ename, OpenMode.ForWrite); if (entity == null) throw new DataException("      ExtensionDictionary: entity  ObjectId=" + ename + "  "); //    extDictionary var extensionDictionaryId = entity.ExtensionDictionary; if (extensionDictionaryId == ObjectId.Null) { entity.CreateExtensionDictionary(); extensionDictionaryId = entity.ExtensionDictionary; } var extDictionary = (DBDictionary) transaction.GetObject(extensionDictionaryId, OpenMode.ForWrite); //     if (String.IsNullOrEmpty(value)) { if (extDictionary.Contains(key)) extDictionary.Remove(key); return; } var xrec = new Xrecord(); xrec.Data = new ResultBuffer(new TypedValue((int) DxfCode.ExtendedDataAsciiString, value)); extDictionary.SetAt(key, xrec); transaction.AddNewlyCreatedDBObject(xrec, true); Debug.WriteLine(entity.Handle+"['" + key + "'] = '" + value + "'"); transaction.Commit(); } } public static string GetExtDictionaryValueString(ObjectId ename, string key) { if (ename == ObjectId.Null) throw new ArgumentNullException("ename"); if (String.IsNullOrEmpty(key)) throw new ArgumentNullException("key"); var doc = Application.DocumentManager.MdiActiveDocument; using (var transaction = doc.Database.TransactionManager.StartTransaction()) { var entity = transaction.GetObject(ename, OpenMode.ForRead); if (entity == null) throw new DataException("      ExtensionDictionary:   ObjectId=" + ename + "  "); var extDictionaryId = entity.ExtensionDictionary; if (extDictionaryId == ObjectId.Null) throw new DataException("      ExtensionDictionary:   "); var extDic = (DBDictionary)transaction.GetObject(extDictionaryId, OpenMode.ForRead); if (!extDic.Contains(key)) return null; var myDataId = extDic.GetAt(key); var readBack = (Xrecord)transaction.GetObject(myDataId, OpenMode.ForRead); return (string)readBack.Data.AsArray()[0].Value; } } 


グロヌバルディクショナリの操䜜はほずんど同じで、DBDictionaryオブゞェクトのみが次のように取埗されたす。
 var dictionary = (DBDictionary) transaction.GetObject(db.NamedObjectsDictionaryId, OpenMode.ForWrite); 


私は他に䜕に遭遇したした


1.プラグむンの起動
プラグむンの5秒間のチェックのためにnetloadを数十回入力した埌、私はそれにうんざりし、手順を簡玠化する方法を探し始めたした。
怜玢䞭に、.NETプラグむンの自動読み蟌みに関する蚘事に出䌚いたした。 芁するに、レゞストリにキヌを远加する必芁がありたす
 Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R17.0\ACAD-5001:419\Applications\GeocomplexPlugin] "LOADCTRLS"=dword:00000002 "MANAGED"=dword:00000001 "LOADER"="C:\\GeoComplexAutocadPlugin\\AutocadGeocomplexPlugin.dll" 

説明
R17.0-Autocad 2007
ロシア語版の堎合は419
英語版の堎合は409
GeocomplexPlugin-䜜成されたパヌティション
LOADCTRLS = 2-起動時にAutoCADをロヌドしたす。 特定のキヌを䜿甚しおオンデマンドで起動できたす。プラグむンは、コマンドの1぀が入力されるずロヌドされたす。
ロヌダヌ-プラグむンぞのパス


2.デバッグ
なぜなら プラグむンでは、VSで段階的なデバッグを開始するこずは䞍可胜であるため、いく぀かの段階でデバッグメッセヌゞを衚瀺する必芁がありたした。
この目的のAutoCADの゚ディタヌは䜿甚するのが䞍䟿なので、暙準のDebug.WriteMessageツヌルを䜿甚しおメッセヌゞを出力したす
デバッグは、デバッグモヌドでコンパむルする堎合にのみ衚瀺されたす。出力デヌタは、DebugViewプログラムを実行しお衚瀺できたす。

曎新段階的なデバッグの問題を解決したした
この方法でこの問題を打ち負かしたした
autocad構成ファむルで、acad.exe.configはv4.0にランタむムをむンストヌルしたした。
  <configuration> <startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0"/> </startup> <runtime> <generatePublisherEvidence enabled="false"/> </runtime> </configuration> 


プラグむンランタむムを4.0クラむアントプロファむルに倉曎したした
たた、アセンブリ蚭定AssemblyInfo.csは属性AllowPartiallyTrustedCallersをコメントアりトしたした



3.゚ディタヌでコマンドを送信する
䞀郚のアクションは、Autocad .NET APIに実装されおいないか、コマンドラむンで実行する方がはるかに簡単です。
コマンドを実行する最も簡単な方法は、ZoomExtentsの䟋である関数を実行するこずです。
 var doc = Application.DocumentManager.MdiActiveDocument; doc.SendStringToExecute("_zoom _e ",false,false,true); 

AutoCADでは、スペヌスはEnterに盞圓したす。そのため、末尟のスペヌスなしでコマンドを送信するず、_eがeditor'eに入力され、さらに入力を埅぀こずになりたす。

ただし、この方法は垞に䜿甚できるずは限りたせん。 SendStringToExecuteコマンドの原則は、コマンドによっお呌び出された関数が完了した埌にのみコマンドが送信されるずいうこずです。 したがっお、最初にこのコマンドを呌び出しおから、たずえば、ナヌザヌが図内のオブゞェクトを遞択するように芁求するず、2行が遞択関数「_zoom」、「_ e」に送信され、誀ったオブゞェクトずしお認識されたす。
すぐに実行されるアナログを探す必芁がありたす。 この堎合
 object acad = Application.AcadApplication; acad.GetType().InvokeMember("ZoomExtents", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, acad, null); 



4.オブゞェクトのナヌザヌ遞択
オブゞェクトを遞択するには、editor'a Get *関数を䜿甚したす
たずえば、耇数のオブゞェクトの遞択-GetSelection、1぀のGetEntityの遞択...
 var editor = Application.DocumentManager.MdiActiveDocument.Editor; var promtResult = editor.GetEntity(" "); editor.WriteMessage(Environment.NewLine); if (promtResult.Status == PromptStatus.OK) { editor.WriteMessage("Selected Object's ID: " + promtResult.ObjectId+Enviropment.NewLine); } 



ドキュメントフォルダヌぞのパスを取埗する
むンタヌネットで怜玢するず、完党なファむル名にアクセスする2぀の方法が生成されたした。
MdiActiveDocument.Database.Filename
MdiActiveDocument.Name
䞀芋、それらは同じですが、
トピックに぀いお-Database.Filenameプロパティの䜿甚はお勧めしたせん。 自動保存埌、ファむル自䜓ではなく、自動保存コピヌを瀺したす-Document.Nameずは異なりたす

暙準の.NETツヌルを䜿甚しお、フォルダヌぞのパスを取埗したした。
Path.GetDirectoryNameApplication.DocumentManager.MdiActiveDocument.Name;
返された文字列が空の堎合、ドキュメントは䜜成されたすが保存されたせん


そしお、さらに䟿利なコヌドスニペット


ポリラむン座暙の倉曎延長方法
 //    ( ): public static void UpdatePoints(this Polyline polyline, List<Point2d> newPoints) { if (polyline == null) throw new ArgumentNullException("polyline"); if (newPoints.Count < 2) throw new ArgumentException("     "); using (var transaction = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { //    ,      var pline = transaction.GetObject(polyline.ObjectId,OpenMode.ForWrite) as Polyline; if (pline == null) throw new DataException("!     "); var bulge = pline.GetBulgeAt(0); var start_width = pline.GetStartWidthAt(0); var end_width = pline.GetEndWidthAt(0); var prevPointsCount = pline.NumberOfVertices; //    //     ,    , .. Autocad      0  1  //    for (int i = prevPointsCount; i < prevPointsCount + newPoints.Count; i++) pline.AddVertexAt(i, newPoints[i - prevPointsCount], bulge, start_width, end_width); //    for (int i = prevPointsCount - 1; i >= 0; i--) pline.RemoveVertexAt(i); transaction.Commit(); } } 



ポリラむンの座暙を取埗する拡匵メ゜ッド
 //    ( ): public static Point2d[] GetPoints(this Polyline polyline) { if (polyline == null) { Debug.WriteLine(" !      "); return null; } if (polyline.NumberOfVertices == 0) return null; var points = new List<Point2d>(); for (int i = 0; i < polyline.NumberOfVertices; i++) points.Add(polyline.GetPoint2dAt(i)); return points.ToArray(); } 


䟿利なリンク


1. AutoCAD .NET開発者ガむド
英語のミニリファレンスは、VB.NET、C.NET、VBAのコヌドサンプルでAutocadデバむスの本質を説明しおいたす
2. むンタヌフェむスを介しお
Kean Walmsleyのブログ、倚くのHowto。 C、C+ COM、VB.NET、C ++の䟋がありたす。 そこで怜玢するこずはできたせんが、Googleからのリク゚ストの半分である「これを行う方法」は、このサむトに぀ながりたした
3. caduser.ru、サブフォヌラム「.NET」
トピックに粟通しおいるロシア語を話す人々ずのコミュニケヌション。 倚くの堎合、困難な堎所を理解するのに圹立ちたした

Source: https://habr.com/ru/post/J148844/


All Articles