Создание записей в таблицах стилей, слоев и т.п.
Как показала практика, приведенный пример не совсем жизнеспособен. Он прекрасно будет работать только в том случае, если в файле либо уже существует указанный текстовый стиль (и тогда выполняется его модификация), либо в файле с момента его открытия этого стиля еще не существовало.
Если же стиль был создан, а потом удален, то строка
1 |
tblStyle.Has(StyleName) |
вернет true, несмотря на то, что стиль имеет свойство IsErased = true. Что же делать?
Как всегда, спасает Александр Ривилис. Уровень знаний человека вызывает оторопь: столько знать невозможно...
Именно он показал класс для работы с удаленными записями. Автор кода - Tony Tanzillo. На сайте http://www.caddzone.com вообще безумное количество информации.
Для гарантии (ну и чтобы не потерялось, конечно) приведу код здесь.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | using System; using System.Collections.Generic; using System.Text; using Autodesk.AutoCAD.Runtime; using System.Runtime.InteropServices; using Autodesk.AutoCAD.DatabaseServices; namespace CaddZone.AutoCAD.DatabaseServices { class DBUtils { // This is a managed workaround for getting a non-erased // SymbolTableRecord, when there are also erased ones with // the same name: public static ObjectId GetTableRecordId(ObjectId TableId, string Name) { ObjectId id = ObjectId.Null; using (Transaction tr = TableId.Database.TransactionManager.StartTransaction()) { SymbolTable table = (SymbolTable)tr.GetObject(TableId, OpenMode.ForRead); if (table.Has(Name)) { id = table[Name]; if (!id.IsErased) return id; foreach (ObjectId recId in table) { if (!recId.IsErased) { SymbolTableRecord rec = (SymbolTableRecord)tr.GetObject(recId, OpenMode.ForRead); if (string.Compare(rec.Name, Name, true) == 0) return recId; } } } } return id; } // This is a much better/faster solution that P/Invokes // AcDbSymbolTableRecord::getAt() directly from managed code: public static class AcDbSymbolTable { // Acad::ErrorStatus AcDbSymbolTable::getAt(wchar_t const *, class AcDbObjectId &, bool) [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall, CharSet = CharSet.Unicode, EntryPoint = "?getAt@AcDbSymbolTable@@QBE?AW4ErrorStatus@Acad@@PB_WAAVAcDbObjectId@@_N@Z")] public static extern ErrorStatus getAt(IntPtr symbolTable, string name, out ObjectId id, bool getErased); } public static ObjectId GetSymbolTableRecordId(SymbolTable table, string name) { ObjectId id = ObjectId.Null; ErrorStatus es = AcDbSymbolTable.getAt(table.UnmanagedObject, name, out id, false); return id; } public static ObjectId GetSymbolTableRecordId(ObjectId TableId, string name) { using (Transaction tr = TableId.Database.TransactionManager.StartTransaction()) { SymbolTable table = (SymbolTable)tr.GetObject(TableId, OpenMode.ForRead); try { return GetSymbolTableRecordId(table, name); } finally { tr.Commit(); } } } } } |
Код приведен для применения в AutoCAD 2007 .. 2009. Для работы в 2010 понадобится менять всего пару строк (разберусь - приведу вариант).
Подумав, решил привести здесь же и общение с Александром по аське.
...
Ривилис 14:18:25: Так ты нашел что искал?
Я 14:18:32: Сейчас сижу тупо рыщу насчет примеров создания на C# текстовых и размерных стилей...
На autodesk.com вообще практически не достучаться
Я 14:19:53: Сам-то стиль у меня кое-как сосздается, но не дай боже он был удален. Он получает флаг IsErased == true и все, дальше мысль останавливается
Ривилис 14:19:56: > примеров создания на C# текстовых и размерных стилей...
А в чем проблема? Создаешь, устанавливаешь значения и добавляешь к таблице стилей/размерных стилей. Всё!
Я 14:20:24: Проблема, если стиль был удален пользователем в файле dwg и потом повторно вызвана моя команда / функция
Ривилис 14:20:47: Понял.
Я 14:21:09: Как обойти - не понимаю.
Ривилис 14:21:28: Это на theswamp Тони решение предлагал
Я 14:21:50: Понял, побег в "болото"... Спасибо
Ривилис 14:21:53: Это и для блоков тоже подходит.
Я 14:22:09: Ох, как хочется получить русский ресурс Аж зубы сводит
Ривилис 14:23:20: Ну если я один буду отвечать как сейчас на caduser.ru и dwg.ru на вопросы но
ObjectARX и .NET, то такого ресурса тебе еще долго ждать придется...
Я 14:23:38: Да я б и рад отвечать, только ведь не знаю ни фига...
Я 14:25:47: Все, спасибо за наводку, уполз изучать
Ривилис 14:25:58: Нашел?
Я 14:37:28: Аналог нашел, для слоев. Сейчас читаю.
Ривилис 14:37:44: http://www.theswamp.org/index.php?topic=12123.msg150999#msg150999
Ривилис 14:38:51: http://www.caddzone.com/DBUtils.cs
Я 14:39:08: Спасибо!
Я 14:39:33: Ушел в глубокое изучение... Знаний-то минимум )
Ривилис 14:39:47:
Ривилис 15:03:21: ?
Я 15:04:22: Александр, еще вопросик можно?
Я так понял, что по DBUtils.cs импортируется неуправляемый код через DllImport. Но там указывается точка вхождения (EntryPoint) в каком-то достаточно странном виде. Часть еще как-то понять можно, но все вместе "сливается" в абракадабру.
Этот импорт где-нибудь описан?
Ривилис 15:05:00:
Я 15:05:10: Имею в виду строку
1 "?getAt@AcDbSymbolTable@@QBE?AW4ErrorStatus@Acad@@PB_WAAVAcDbObjectId@@_N@Z"Если работать под 2009 или 2010, там же по идее это меняться должно. Или нет?
Ривилис 15:05:11: Это имя C++ функции
Я 15:06:08: ?<Имя ObjectARX-функции>@<Для чего будет вызываться>@???> и дальше - тьма...
Ривилис 15:06:26: public: enum Acad::ErrorStatus __thiscall AcDbSymbolTable::getAt(wchar_t const *,
class AcDbObjectId &,bool)const
Я 15:06:57: Так это описание функции для ObjARX, я правильно понял?
Ривилис 15:07:05: Это так называемые "декорированые C++" функции
Ривилис 15:07:53: > Так это описание функции для ObjARX, я правильно понял?
Фактически да. Но это для любой C++ функции (своя строка), которая в dll-файле
Я 15:08:40: То есть я должен открыть dll-файл (кстати, чем?) и найти эту строку, ориентируясь на имя ObjecARX-функции, верно?
Ривилис 15:08:50: Т.е. закодированное имя функции, ее параметры и возвращаемое значение.
Я 15:08:58: что-то запутался...
Ривилис 15:09:07: А зачем тебе открывать?
Ривилис 15:09:18: Там уже все готово.
Я 15:09:49: А если мне понадобится писать аналогичный код для 2010, там же и имя dll будет другим, и наверняка строка EntryPoint поменяется...
Ривилис 15:09:51: Только для AutoCAD 2010 нужно будет заменить R17 на R18
Ривилис 15:10:27: Строка EntryPoint с вероятностью 99% останется тойже.
Ривилис 15:11:22: Эти строки менялись при переходе с AutoCAD 2006 на 2007 когда появилось использование unicode
Ривилис 15:11:48: И тогда "char" заменился на "wchar_t"
Ривилис 15:13:25: Для просмотра можно использовать dumpbin.exe /EXPORTS
Ривилис 15:13:48: dumpbin.exe /EXPORTS "имя dll-файла"
Я 15:14:41: Сейчас попробую, спасибо.
Ривилис 15:15:24: А для расшифровки имени функции undname.exe
---
История комментирования (что удалось восстановить):
Александр, как всегда, был прав на 200%: в коде достаточно заменить строку "acdb17.dll" на "acdb18.dll": EntryPoint что для 2007, что для 2010 версий одинаков - "?getAt@AcDbSymbolTable@@QBE?AW4ErrorStatus@Acad@@PB_WAAVAcDbObjectId@@_N@Z"
...
Как говорится, "я фигею в этом зоопарке". Оказывается, для любых символьных таблиц (стилей, блоков и т.п.) необходимо постоянно проверять создаваемый элемент на IsErased. Если бы не подсказка Александра, я бы точно плюнул на изучение C# и продолжил жить в lisp. Александр, еще раз - земной поклон!
...
Странно, у меня tblStyle.Has(StyleName) дает false для удаленного стиля. И он нормально создается. А, удаленные записи можно увидеть, если для таблицы стилей применить IncludingErased. AutoCAD 2008, VS C# Express 2008.
...
А можно привести вариант кода с IncludingErased? Я просто только-только в себя начинаю приходить и возвращаться к .NET-коду... Все позабыл, даже то, что знал.
...
После открытия tbkStyle: TextStyleTable tblStyle = (TextStyleTable) tr.GetObject(db.TextStyleTableId, OpenMode.ForRead); tblStyle = tblStyle.IncludingErased; Ссылок на статьи по этому вопросу у меня нет. Хотелось бы разобраться - как, где и когда учитывать удаленные объекты. ps Если существует удаленный текстовый стиль, например Стиль1, и создать новый стиль Стиль1, добавить его в таблицу стилей и транзакцию, то он создастся с новым ObjectId. Получается, будут существовать два (и более) стиля с одним именем, но один(и более) из них удален. (IsErased=true). С удаленными объектами, какая-то катавасия получается. )