Создание записей в таблицах стилей, слоев и т.п.

Как показала практика, приведенный пример не совсем жизнеспособен. Он прекрасно будет работать только в том случае, если в файле либо уже существует указанный текстовый стиль (и тогда выполняется его модификация), либо в файле с момента его открытия этого стиля еще не существовало.
Если же стиль был создан, а потом удален, то строка

вернет 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). С удаленными объектами, какая-то катавасия получается. )

Размещено в .NET, AutoCAD, Новости · Метки:



Поделитесь своим мнением


Я не робот.