Вызов PInvoke ObjectARX AcArray для Hatch.AppendLoop in C#
Это смешно, но иногда такие вещи встречаются. Совсем недавно общался с разработчиком, который рассказывал мне о проблемах использования смешанного (“mixed”) кода (C++) Ему приходилось привязываться к конкретной версии .NET, так как без этого не работали другие связанные с этой части программы.Я предпочитаю не пользоваться смешанным (“mixed”) кодом, так как он действительно добавляет головной боли и мешает развивать программу. Поэтому очевиден вопрос: "Зачем вам нужен смешанный режим DLL?" После долгой беседы, оказывается, что единственная причина – необходимость вызвать конкретную версию ObjectARX AcDbHatch::appendLoop, которая почему-то не реализована в .NET. Вот её определение в ObjectARX:
- Acad::ErrorStatus appendLoop( Adesk::Int32 loopType,
- const AcGePoint2dArray& vertices,
- const AcGeDoubleArray& bulges );
Главная проблема P/Invoke этой функции заключается в том, что нет эквивалента AcGeDoubleArray в .NET API (Думаю, что именно по этой причине этот метод и не был реализован в .NET). Во всяком случае, мы (DevTech) заявили разработчикам некоторое время назад, что эта функция не будет поддержана в P/Invoke, потому что разработчик настаивал, что она нуждался в смешанном режиме DLL.
Тем не менее всё можно реализвать через P/Invoke – всё зависит от того сколько времени и сил вы готовы потратить для этого. Я полагаю, что обходные пути для этого были бы:
- PInvoke
- Работа с кривизной (Bulge) используя типы AcGeCurves и Edge
Я считаю, что P/Invoke было бы намного проще (и доставит намного больше удовольствия! )
Так, вызов P/Invoke этого метода appendLoop() следует делать осторожно, так как определения массива doubles в .NET и передача его в «неуправляемый» код следует делать очень осторожно. Основная причина – сборка мусора в .NET – всё что размещено - может быть перенесено .NET GC , что приведет к краху неуправляемого кода, который его использует. Так что требуется зафиксировать размещенную GC память, чтобы GC знал, что её не следует трогать. Фиксация памяти in C#.NET делается при помощи ключевого слова fixed.
Другой важный момент – размер и выравнивание элементов в классах/структурах в .NET должны быть точно такими же, как и в неуправляемом коде. Для этой цели используется атрибут StructLayout,
Т.е.:
Перед тем как смотреть код, я хотел бы напомнить вам об указателях на ячейки памяти в .NET!! Всё это требует ключевое слово unsafe для того, чтобы отметить часть кода как доступный для переполнения буфера, и т.д., так что исполняющая система .NET могла как-нибудь защититься от хакеров
- // Вызов appendLoop() при помощи P/Invoke, используя "fixed" память
- // in "небезопасном" ("unsafe") .NET контексте
- // by Fenton Webb, DevTech, Autodesk, 05/06/2012
- [CommandMethod("HatchLoop")]
- static public void HatchLoop()
- {
- Document doc = Application.DocumentManager.MdiActiveDocument;
- Database db = doc.Database;
- Editor ed = doc.Editor;
- using (Transaction Tx = db.TransactionManager.StartTransaction()) {
- Hatch hatch = new Hatch();
- hatch.SetDatabaseDefaults();
- hatch.Elevation = 0.0;
- hatch.Normal = Vector3d.ZAxis;
- hatch.SetHatchPattern(HatchPatternType.PreDefined, "AR-B816");
- Point2dCollection vertices = new Point2dCollection();
- vertices.Add(new Point2d(0, 0));
- vertices.Add(new Point2d(100, 0));
- vertices.Add(new Point2d(100, 50));
- vertices.Add(new Point2d(0, 50));
- vertices.Add(new Point2d(0, 0));
- DoubleCollection bulges = new DoubleCollection();
- bulges.Add(0.0);
- bulges.Add(0.0);
- bulges.Add(0.0);
- bulges.Add(0.0);
- bulges.Add(0.0);
- unsafe {
- // Создаем маршалер класс AcGePoint2dArray
- AcArray verticesAcArray = new AcArray();
- // преобразуем в массив double
- Point2d[] verticesArray = vertices.ToArray();
- // получаем указатель и закрепляем его, чтобы GC не мог его переместить
- fixed (Point2d* verticesPtr = &verticesArray[0]) {
- // прописываем свойства acarray
- verticesAcArray.array = (IntPtr)verticesPtr;
- verticesAcArray.mPhysicalLen = verticesArray.Length;
- verticesAcArray.mLogicalLen = verticesArray.Length;
- // создаем маршалер класс AcGeDoubleArray
- AcArray bulgesAcArray = new AcArray();
- double[] bulgesArray = bulges.ToArray();
- // получаем указатель и закрепляем его, чтобы GC не мог его переместить
- fixed (double* bulgesPtr = &bulgesArray[0]) {
- // записываем свойства acarray
- bulgesAcArray.array = (IntPtr)bulgesPtr;
- bulgesAcArray.mPhysicalLen = bulgesArray.Length;
- bulgesAcArray.mLogicalLen = bulgesArray.Length;
- appendLoop(
- hatch.UnmanagedObject,
- HatchLoopType.kDefault,
- ref verticesAcArray.array,
- ref bulgesAcArray.array);
- hatch.EvaluateHatch(false);
- BlockTableRecord btr = Tx.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
- btr.AppendEntity(hatch);
- Tx.AddNewlyCreatedDBObject(hatch, true);
- Tx.Commit();
- }
- }
- }
- }
- }
- enum HatchLoopType
- {
- kDefault = 0,
- kExternal = 1,
- kPolyline = 2,
- kDerived = 4,
- kTextbox = 8,
- kOutermost = 0x10,
- kNotClosed = 0x20,
- kSelfIntersecting = 0x40,
- kTextIsland = 0x80,
- kDuplicate = 0x100,
- kIsAnnotative = 0x200,
- kDoesNotSupportScale = 0x400,
- kForceAnnoAllVisible = 0x800,
- kOrientToPaper = 0x1000,
- kIsAnnotativeBlock = 0x2000
- };
- [StructLayout(LayoutKind.Auto)]
- struct AcArray
- {
- public IntPtr array;
- public int mPhysicalLen;
- public int mLogicalLen;
- public int mGrowLen;
- };
- // Для AutoCAD 2013 и 2014 - "acdb19.dll", для AutoCAD 2015 - "acdb20.dll"
- [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall,
- CharSet = CharSet.Unicode,
- EntryPoint = "?appendLoop@AcDbHatch@@QAE?AW4ErrorStatus@Acad" +
- "@@JABV?$AcArray@VAcGePoint2d@@V?$AcArrayMemCopyReallocator@" +
- "VAcGePoint2d@@@@@@ABV?$AcArray@NV?$AcArrayMemCopyReallocator@N@@@@@Z")]
- private static extern int appendLoop32(IntPtr ths,
- HatchLoopType loopType,
- ref IntPtr vertices,
- ref IntPtr bulges);
- // Для AutoCAD 2013 и 2014 - "acdb19.dll", для AutoCAD 2015 - "acdb20.dll"
- [DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall,
- CharSet = CharSet.Unicode,
- EntryPoint = "?appendLoop@AcDbHatch@@QEAA?AW4ErrorStatus@Acad" +
- "@@JAEBV?$AcArray@VAcGePoint2d@@V?$AcArrayMemCopyReallocator@" +
- "VAcGePoint2d@@@@@@AEBV?$AcArray@NV?$AcArrayMemCopyReallocator@N@@@@@Z")]
- private static extern int appendLoop64(IntPtr ths,
- HatchLoopType loopType,
- ref IntPtr vertices,
- ref IntPtr bulges);
- private static int appendLoop(IntPtr ths,
- HatchLoopType loopType,
- ref IntPtr vertices,
- ref IntPtr bulges)
- {
- if (Marshal.SizeOf(IntPtr.Zero) > 4)
- return appendLoop64(ths, loopType, ref vertices, ref bulges);
- return appendLoop32(ths, loopType, ref vertices, ref bulges);
- }
Обсуждение: http://adn-cis.org/forum/index.php?topic=692
Опубликовано 18.04.2014Отредактировано 19.04.2014 в 00:27:08