Личный блог и информация по репозиториям
18 июня 2025г.
В ранней статье мы подняли понятие COM объекта и остановились на сущности элемента. Держа в памяти тот факт, что любой параметрический объект по сути представляет собой Element, рассмотрим, как можно создавать новые объекты при помощи COM. Данная статья будет не совсем про формулы, а скорее только про COM.
Примеры далее будем рассматривать на примере C# для случая внешнего связывания (консольное приложение на .NET 6.0). Так как из коробки в .NET 6 отсутствуют некоторые вспомогательные методы, позаимствуем их из Интернета.
В классических .NET API, NRX создание объектов начиналось с создания экземпляра класса нужного типа и далее в зависимости от задачи редактирования оного экземпляра класса до вставки в модель при помощь специального метода. В классическом COM нет понятия классов, а экземпляры интерфейсов нельзя создать при помощи директивы new(). Да, в C++ есть понятие coclass, но сейчас не об этом.
В COM API платформы nanoCAD среди функциональности интерфейса IAcadBlock имеются методы, создающие отдельные примитивы – AddCircle, AddText, AddTorus и т.д. Вместе с тем, имеется также особый метод, который позволяет создавать объекты любых типов, даже не имеющих COM-интерфейс – AddCustomObject.
На вход данный метод принимает имя объекта. Не класс, и не guid, и не то, что отображается в свойствах объекта в nanoCAD.
Не уверен, скажу однозначно верно, но он ожидает имя RXClass’а объекта. Этот класс имеется как в .NET, так и в MRX API. Имя типа можно также получить при помощи простой конструкции:
(entget(car(entsel)))
На выходе в консоли будет информация вида:
((-1 . #<Имя примитива: 0000023A218592F0>) (0 . "LINCSPARAMETRICSOLID") (5 . "A83") (330 . #<Имя примитива: 0000023A7962AEA0>) (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "0") (48 . 100.0))
Нас будет интересовать то, что идет в скобочках после 0. – в данном случае “LINCSPARAMETRICSOLID”. Регистр не важен. Пояснение - это DXF-строка с информацией об объекте, нулем обозначается DXF-код типа объекта.
Продолжая ряд, металлическая пластина имеет тип MSIRONPLATE, металлическая балка – IRNAXIS и т.д.
Вне зависимости от того, имеет ли создаваемый объект COM-интерфейс или нет, к нему можно применить трансформацию координат как к сущности AcadEntity (метод TransformBy). Так как мы говорим, всё таки, про параметрические COM-объекты, они могут быть приведены к специальному интерфейсу (например, металлические балки/колонны к IRNAXIS – ironObjComLib.WrIronAxis). Произвольные параметрические объекты приводятся к mdsUnitsLib.MDSParametricEnt. В любом случае, у объекта может быть получен элемент следующим способом:
dynamic obj; // = вашему объекту
mdsUnitsLib.Element objEl = obj.Element as mdsUnitsLib.Element;
Пример вставки металлической колонны выглядел бы так:
var nc_app = Marshal2.GetActiveObject("nanoCAD.Application.25.0") as nanoCAD.Application;
var nc_ms = nc_app.ActiveDocument.ModelSpace;
var obj_IRNAXIS1 = nc_ms.AddCustomObject("IRNAXIS") as ironObjComLib.WrIronAxis;
if (obj_IRNAXIS1 != null && axisTemplate != null)
{
obj_IRNAXIS1.StartPoint = new double[] { 2000, 2000, 0 };
obj_IRNAXIS1.EndPoint = new double[] { 4000, 6000, 1000 };
}
.. если бы всё было так просто. 😏
Дело в том, что все параметрические объекты создаются на основе так называемого профиля, в роли которого выступает объект-шаблон из Библиотеки стандартных компонентов и его геометрия.
Если просто взять и создать параметрический объект при помощи подхода выше, то у нас будет создано нечто, имеющее тип изделия но не имеющей геометрии:

(скриншот из среды nanoCAD BIM Строительство)
Даже если сделать Element.CopyFrom() от нормального объекта, это не возымеет действия. Таким образом, предпочтительно создавать новые объекты только копированием имеющихся и дальнейшим редактированием их положения в параметрах.
И всё-таки, для чего тогда использовать метод AddCustomObject? Его можно использовать для создания новых видов параметрических объектов, описываемых интерфейсом mdsUnitsLib.MDSParametricEnt. Код ниже иллюстрирует такой подход – создается определение параметрического объекта – linCSParametricSolid, ему задаются параметры спецификации и параметрическая геометрия из одного примитива – параллелепипеда. В соответствии с XPG-схемой для данного вида примитива задаются обязательные параметры.
var nc_app = Marshal2.GetActiveObject("nanoCAD.Application.25.0") as nanoCAD.Application;
var nc_ms = nc_app.ActiveDocument.ModelSpace;
mdsUnitsLib.MDSParametricEnt? ms_object = nc_ms.AddCustomObject("linCSParametricSolid");
if (ms_object == null) return;
//Задание параметров спецификации
var properties = ms_object.Element;
properties.Parameters.SetParameter("DIM_WIDTH", "100");
properties.Parameters.SetParameter("DIM_LENGTH", "100");
properties.Parameters.SetParameter("DIM_HEIGHT", "100");
//Задание параметрической геометрии
var box = ms_object.ParametricData.AddChild("BOX");
var boxParameters = box.Parameters;
boxParameters.SetParameter("Length", "", "", "[DIM_LENGTH]");
boxParameters.SetParameter("Width", "", "", "[DIM_WIDTH]");
boxParameters.SetParameter("Height", "", "", "[DIM_HEIGHT]");
boxParameters.SetParameter("StartPointX", "0");
boxParameters.SetParameter("StartPointY", "0");
boxParameters.SetParameter("StartPointZ", "0");
boxParameters.SetParameter("DirectionX", "1");
boxParameters.SetParameter("DirectionY", "0");
boxParameters.SetParameter("DirectionZ", "0");
boxParameters.SetParameter("OrientationX", "0");
boxParameters.SetParameter("OrientationY", "0");
boxParameters.SetParameter("OrientationZ", "1");

(вот так будет выглядеть объект)
Подводя промежуточный итог – просто так параметрические объекты не создать, даже созданные с помощью метода AddCustomObject они не будут иметь информацию о профиле и эту информацию через COM API не добавить. Единственное, для чего метод применим – это для создания пользовательских параметрических объектов произвольной сложности путем создания и редактирования параметров (Element).
Имеется библиотека типов “Model Studio Library Manager 1.0 Type Library”, содержащая инструменты для подключения к Библиотеке параметрических компонентов (БСК далее) , обзора её данных и вставки в модель (на основе выбранного профиля).
Получение экземпляра COM-оболочки БСК, как и nanoCAD осуществляется через системные вызовы:
mdsLibManagerLib.CADLibrary? library_BSK = Marshal2.GetActiveObject("LibManager.CADLibrary.1") as mdsLibManagerLib.CADLibrary;
Есть 2 пути получения конкретных параметрических объектов – найти их при помощи ручного запроса или выбрать вручную в диалоге объектов БСК.
//Выбор вручную
mdsLibManagerLib.CADLibQuery library_Query = library_BSK .GetLibQuery();
library_Query.AddCondition("PART_NAME", mdsLibManagerLib.CADLibRelationType.cl_cndLike, "%Колодец%");
library_Query.AddCondition("PART_TYPE", mdsLibManagerLib.CADLibRelationType.cl_cndEqual, "Колодцы");
mdsLibManagerLib.CADLibObjects? selObjects = library_Query.Execute();
//Выбор через диалог
var nc_app = Marshal2.GetActiveObject("nanoCAD.Application.25.0") as nanoCAD.Application;
mdsLibManagerLib.CADLibObjects? selObjects2 = library_Query.SelectObjects((int)nc_app.HWND, "Выборка профилей", 0);
Обратим внимание, что у метода SelectObjects имеется особый первый аргумент – это Windows-идентификатор приложения, мы принимаем его равным окну nanoCAD.
После получения набора объектов, удовлетворяющих запросу, выберем первый объект (нам же не важно для примера на чем показывать) и покажем, как вставить объект двумя путями – через стандартный механизм БСК и … из XPG файла 😲.
COM-оболочка БСК имеет метод Insert, позволяющий вставить в модель nanoCAD (AutoCAD) параметрический объект из выборки CADLibQuery.
Метод принимает 3 аргумента – первый = GUID объекта БСК и два других – параметры вставки. Второй описывает место вставки, а третий я хз (примеров нигде не нашел), в справке значится “условия вставки”.
И второй, и третий аргументы представляют собой XML-строку, буквально XML-документ в виде строки. Приведем ниже листинг кода на C#, который создаст нам XML-документ с параметрами вставки элемента:
//Инициализирует документ Constraints
private XmlDocument CreateConstraintsDocument()
{
XmlDocument doc = new XmlDocument();
XmlProcessingInstruction newPI = doc.CreateProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\"");
doc.AppendChild(newPI);
XmlElement root = doc.CreateElement("Constraints");
doc.AppendChild(root);
return doc;
}
//Создает определение Constraint
private XmlElement CreateConstraint(string strType, XmlDocument doc)
{
XmlElement eres = doc.CreateElement("Constraint");
doc.DocumentElement.AppendChild(eres);
eres.SetAttribute("type", strType);
return eres;
}
//Создает вспомогательный параметр, описывающий данный Constraint
private XmlElement CreateParameter(string strName, string strValue, XmlElement root)
{
XmlElement res = root.OwnerDocument.CreateElement("Parameter");
root.AppendChild(res);
res.SetAttribute("name", strName);
res.SetAttribute("value", strValue);
return res;
}
//Примеры различных Constraint:
//Создает Constraint типа position3d
private XmlElement CreateLocation3D(double x, double y, double z, XmlDocument doc)
{
XmlElement constr = CreateConstraint("position3d", doc);
CreateParameter("X", x.ToString(), constr);
CreateParameter("Y", y.ToString(), constr);
CreateParameter("Z", z.ToString(), constr);
return constr;
}
//Создает Constraint типа position3d + rotation
private XmlElement CreateLocation3DAndRotation(double x, double y, double z, double fRotation, XmlDocument doc)
{
XmlElement constr = CreateLocation3D(x, y, z, doc);
constr = CreateConstraint("rotation", doc);
CreateParameter("ANGLE", fRotation.ToString(), constr);
return constr;
}
Полный код включая запрос и вставка объекта:
mdsLibManagerLib.CADLibQuery library_Query = library_BSK .GetLibQuery();
library_Query.AddCondition("PART_NAME", mdsLibManagerLib.CADLibRelationType.cl_cndLike, "%Колодец%");
library_Query.AddCondition("PART_TYPE", mdsLibManagerLib.CADLibRelationType.cl_cndEqual, "Колодцы");
mdsLibManagerLib.CADLibObjects? selObjects = library_Query.Execute();
if (selObjects == null) return;
mdsLibManagerLib.CADLibObject? selObject = selObjects.Item(0);
if (selObject == null) return;
XmlDocument doc = CreateConstraintsDocument();
CreateLocation3DAndRotation(2400, 1500, 1000, 65, doc);
string constraints = doc.OuterXml;
library_BSK.Insert(selObject.GUID, constraints);
Метод Insert вернёт экземпляр интерфейса того типа, какой объект создается.
Имеется и альтернативный вариант создания параметрического объекта (вставки в модель) из XPG файла. Обратим внимание, что для выполнения этой операции потребуется особый интерфейс, который как и COM-оболочка БСК получается из системы:
mdsUnitsLib.MDSUnitsFactory? factory = Marshal2.GetActiveObject("UnitsCSCom.MDSUnitsFactory.1") as mdsUnitsLib.MDSUnitsFactory;
Покажем, как получить для целевого объекта из БСК этот XPG, а затем используя его, создать объект в модели:
mdsLibManagerLib.CADLibQuery library_Query = library_BSK.GetLibQuery();
library_Query.AddCondition("PART_NAME", mdsLibManagerLib.CADLibRelationType.cl_cndLike, "%Колодец%");
library_Query.AddCondition("PART_TYPE", mdsLibManagerLib.CADLibRelationType.cl_cndEqual, "Колодцы");
mdsLibManagerLib.CADLibObjects? selObjects = library_Query.Execute();
if (selObjects == null) return;
mdsLibManagerLib.CADLibObject? selObject = selObjects.Item(0);
if (selObject == null) return;
string xpgPath = selObject.GetGraphicsPath("Parametric");
//Вставка параметрического объекта из XPG
var fact = CreateFactory();
if (fact == null) return;
MDSParametricEnt? parObject = fact.CreateUnit(xpgPath, new double[3] {5000,5000,1000}, 1, 0, 0);
Требует пояснения тот момент, как от объекта БСК mdsLibManagerLib.CADLibObject мы перешли к XPG. Для этого был использован таинственный метод GetGraphicsPath с аргументом Parametric. Дело в том, что объекты в БСК хранятся рассредоточенным образом, и каждый фрагмент данных задается так называемой категорией (таблица БД FileCategories). При программном получении объектов БСК они кэшируются локально на ПК и пути к определяющим файлам возвращаются методом GetGraphicsPath. Тип файла Parametric как раз отвечает за XPG-файл.
Далее – просто, путь к XPG файлу подается на вход методу CreateUnit наряду с информацией о точке вставки элемента и информацией об угле поворота и … в модели будет вставленный элемент.
P.S. Для пункта 1.2 нужен, конечно, большой справочный блок “кто такой XPG”; я не буду выкладывать эту информацию, она будет в составе справки к API nanoCAD BIM Строительство к 25 версии. В целом, вся эта информация открыта, просто надо было порыться, чтоб её структурировать
P.S.S. Надеюсь, с этой статьей некоторые особенности COM стали более очевидными.