(изменено: Юрий Устинов, 26 июля 2010г. 16:14:16)

Тема: for each для ICollection

Здравствуйте, разрабатываю плагин для автокада, который позволяет работать со специфическими объектами, оперируя данными из ООСУБД Cache. Решение представляет из себя два проекта arx и dbx с опцией смешанного с .Net кода.

Вот в другом проекте чисто ObjectARX.Net
Такая конструкция работает без ошибок:

ASCTP.Geometry.Shape ExpShape = ASCTP.Geometry.Shape.OpenId(CacheConnect, reader[reader.GetOrdinal("ID")].ToString());
                foreach (ASCTP.Geometry.Primitive ExpPrim in ExpShape.Primitives.Values)
                {
                    if (ExpPrim.ClassName == "ASCTP.Geometry.Line")
                    {
                        ASCTP.Geometry.Line ExpLine = (ASCTP.Geometry.Line)ExpPrim;
                        Autodesk.AutoCAD.DatabaseServices.Line AutoLine = new Autodesk.AutoCAD.DatabaseServices.Line(new Point3d(ExpLine.Start.X.Value, ExpLine.Start.Y.Value, ExpLine.Start.Z.Value), new Point3d(ExpLine.End.X.Value, ExpLine.End.Y.Value, ExpLine.End.Z.Value));
                        BTR.AppendEntity(AutoLine);
                        TM.AddNewlyCreatedDBObject(AutoLine, true);
                    }
                }

А в этом:

Adesk::Boolean ZLOICacheProxy::worldDraw (AcGiWorldDraw *mode) {
    assertReadEnabled () ;
    
    cnCache = new CacheConnection("Server=localhost; Port=1972; Namespace=USER; Password=SYS; User ID=_SYSTEM;");
    cnCache->Open();
    
    Shape *Sp=Shape::OpenId(cnCache,"30");

    System::Collections::ICollection *zzz=Sp->Primitives->Values;//Чтобы поняли тип переменной
    for each(Primitive *BufPrim in zzz)//ERROR 1
    {
    if (BufPrim->ClassName=="ASCTP.Geometry.Line")
        {
            Line *Ln=(Line*)BufPrim;

            AcGePoint3d pts[2];
            pts[0]=AcGePoint3d((double)Ln->Start->X,(double)Ln->Start->Y,(double)Ln->Start->Z);
            pts[1]=AcGePoint3d((double)Ln->End->X,(double)Ln->End->Y,(double)Ln->End->Z);
            mode->geometry().worldLine(pts);

        }
    }

    Sp->Close();

    cnCache->Close();

    //------ Returning Adesk::kFalse here will force viewportDraw() call
    return (Adesk::kFalse) ;
}

Выдаёт ошибки:
Error    1    error C3285: for each statement cannot operate on variables of type 'System::Collections::ICollection __gc *'    d:\mydoc\Visual Studio 2005\Projects\CacheClientARX\CacheClientDBX\ZLOICacheProxy.cpp    209   

ну а дальше не по делу, из за того, что zzz не воспринимает.

Есть идеи как работать в моих проектах с этими ICollection?

Re: for each для ICollection

Если вы используете Managed C++, то указатели на управляемые типы (насколько мне известно) должны выглядеть так:

Shape __gc *Sp = Shape::OpenId(cnCache,"30"); 
System::Collections::ICollection __gc *zzz = Sp->Primitives->Values;

Может в этом причина.
Однако есть смысл перейти с Managed C++ на С++/CLI. C++/CLI - полноценный язык .NET. Cинтаксис его логичнее и понятнее, чем Managed C++.
Вот пара замечательных книг (правда на английском языке):
Stephen R.G. Fraser "Pro Visual C++ CLI and the .NET 2.0 Platform" - учебник по языку С++/CLI и использованию его в .NET программировании.
Nishant Sivakumar "C++/CLI in Action" - лучшая, если не единственная, полноценная книга о программировании на С++/CLI в смешанном режиме.

Re: for each для ICollection

Эх, не ту ошибку вписал, извиняюсь, я звёздочку не поставил и скомпилил, а потом поставил её уже здесь.
Домой приду исправлюсь :(
Там была ругань, что 'System::Collections::ICollection __gc *' нельзя использовать в for each

Однако есть смысл перейти с Managed C++ на С++/CLI.

Мне вообще не важно на чём писать. Есть просто набор интструментов, который необходим:
1) Osnap points - точки привязки.
2) Grip points - точки захвата.
3) Worlddraw - возможность прорисовки объекта
4) Gripdraw - возможность прорисовки объекта при наведении и перемещении точек.
В общем, пока в голову больше ничего не приходит. Что можете посоветовать по части инструментов для разработки? Под ObjectARX .Net я, к сожалению, не нашёл такого функционала.

Вот пара замечательных книг (правда на английском языке)

Спасибо за книжки, очень приятно, когда есть проверенная литература. Английский нормально воспринимаю, лишь бы не в стиле Шекспира, когда одно слово употребляется кучей синонимов.

Re: for each для ICollection

Мне вообще не важно на чём писать.

Как же не важно? Смешанный код можно писать или на полноценном .NET языке С++/CLI (опция компилятора /clr), или на устаревшем Managed C++ (опция компилятора /clr:oldSyntax). Другого не дано.

Иное дело, что можно , например писать основной функционал на C#, используя ObjectARX.NET, а недостающий функционал реализовывать в отдельной "родной" C++ ObjectARX библиотеке и использовать её функционал через P/Invoke.
А если вы уверенно владеете классическим С++, то лучше, наверное, наоборот. Основной функционал пишется на C++ ObjectARX, а взаимодействие с внешним миром (доступ к базам, пользовательский интерфейс и т.п.) выполняется с помощью подгружаемых специализированных ObjectARX.NET сброк.

Re: for each для ICollection

Поправил ошибку.

Error    1    error C3285: for each statement cannot operate on variables of type 'System::Collections::ICollection __gc *'    d:\mydoc\Visual Studio 2005\Projects\CacheClientARX\CacheClientDBX\ZLOICacheProxy.cpp    209    

Как же не важно?

Имеется ввиду, что если есть более функциональный способ на Яве, то буду на Яве, нет религиозных предпочтений.

Смешанный код можно писать или на полноценном .NET языке С++/CLI (опция компилятора /clr), или на устаревшем Managed C++ (опция компилятора /clr:oldSyntax). Другого не дано.

А ObjectARX визард, генерирующий проекты и код в них, генерит устаревший код, я так понял.
Придётся с нуля писать весь проект?
Да как бы это тоже не шибко важно, главное, чтобы я сильно не увяз в этой системщине, а то до задачи ещё самой не дошёл.

Иное дело, что можно , например писать основной функционал на C#, используя ObjectARX.NET, а недостающий функционал реализовывать в отдельной "родной" C++ ObjectARX библиотеке и использовать её функционал через P/Invoke.

Я обдумал это, но не знаю как лучше это реализовать. Я вообще думал, что через Objectarx.Net нельзя обращаться к объектам из обычного ObjectARX.

А если вы уверенно владеете классическим С++, то лучше, наверное, наоборот. Основной функционал пишется на C++ ObjectARX, а взаимодействие с внешним миром (доступ к базам, пользовательский интерфейс и т.п.) выполняется с помощью подгружаемых специализированных ObjectARX.NET сброк.

Вот тут и проблема, получается, что мне для решения моей задачи придётся написать dllку, в которой будет фунция обращения к конкретному элементу коллекции? Типа мы кидаем как аргументы номер и коллекцию, а на выходе получаем требуемый экземпляр?
Это всё слишком сложно, хотя возможно этого и добивались разработчики из Autodesk.
Желательно, чтобы проект был наиболее устойчив ко времени, легко расширяем и выполнял все поставленные задачи.

Re: for each для ICollection

Любое ObjectARX приложение, в т.ч. написанное на .NET и загруженное через NETLOAD может экспортировать в AutoCAD так называемые внешние функции, равно как и вызывать внешние функции, экспортированные другими ObjectARX приложениями. Так в .NET приложении достаточно для некоторого метода некоторого класса соблюсти правильный прототип и пометить этот метод атрибутом LispFunctionAttribute. Например:

[LispFunction("webmenu-begin")]
public ResultBuffer Begin(ResultBuffer argsRb)
{
   .....
}

B результате для текущего сеанса AutoCAD регистрируется функция, указанная в параметре атрибута, как внешняя, и её можно вызвать напрямую из Lisp, или из классического ARX-приложения, используя acedInvoke(), или из .NET приложения, используя ту же acedInvoke(), но уже через механизм P/Invoke. Это и есть самый простой (встроенный, присущий) способ взаимодействия между собой классических и .NET ARX приложений. Комплект функций, экспонируемый некоторым конкретным ARX-модулем, может смело рассматриваться, как интерфейс этого модуля. Ничуть не хуже, чем COM-интерфейс. :)
Есть ограничения по типам данных, передаваемых в качестве аргументов и типам возвращаемых значений таких функций. Это строки, целые и действительные числа, массивы из двух или трех действительных чисел (точки), два специальных символа T и NIL, указатели примитивов, указатели наборов примитивов и специальные маркеры, позволяющие группировать атомарные значения в списки или точечные пары.

А что касается смешанного кода, то язык C++/CLI нахрапом не возьмешь. Хотя бы по тому, что он включает в себя классический С++, как подмножество.

(изменено: Юрий Устинов, 27 июля 2010г. 08:24:49)

Re: for each для ICollection

:evil: Форум ужасный, закончилась сессия и он не сохранил моего собщения...

Любое ObjectARX приложение, в т.ч. написанное на .NET и загруженное через NETLOAD может экспортировать в AutoCAD так называемые внешние функции, равно как и вызывать внешние функции, экспортированные другими ObjectARX приложениями.

Но я не смогу использовать в проекте на С# классы из DBX-эенеблера. И тем более получать от них события и проч.

По теме, я не смогу получить элемент Sp->Primitives->Values в этом проекте?

И если я захочу писать на C++/CLI, то писать придётся с нуля? Визард не поможет?

Re: for each для ICollection

1. Чтобы в проекте на С# использовать классы из некоторой native dll (в т.ч. DBX-энеблера) необходимо написать на C++/CLI еще одну dll, которая в народе называется wrapper. Именно таким способом три таких wrapper'а и экспонируют dll-библиотеки классического ObjectARX в мир .NET.
Чтобы самому написать подобную оболочку, необходимы уверенные знания С++ и С++/CLI, плюс владение техникой создания таких оболочек. Базовые приемы описаны во второй книге, указанной мною ранее
(мои знания по этой теме носят теоретический характер и как раз ограничены объемом этой книги). Если вы освоите эту технику, то сможете экспонировать в мир .NET любые native dll в том виде, в каком вам будет удобно: оболочка вовсе не обязана копировать логику и структуру классов исходной dll. Таким образом функционал исходной dll будет представлен .NET классами. Свойства, события и пр. - всё как положено.

2. Если Sp - это native класс, то и обращаться с ним нужно соответственно и не пытаться его запихать в for each. Если это .NET класс, то он должен быть коллекцией и реализовывать интерфейс IEnumerable.

3. Визард ObjectARX ничего не пишет на смешанном коде. Он устанавливает устаревшую опцию компилятора (/clr:oldSyntax) да включает ссылки на стандарные .NET wrapper'ы для ObjectARX библиотек. Т.е. включается так называемая поддержка .NET (галочкой в чекбоксе), но что именно из .NET вам нужно, визард понятия не имеет.

Re: for each для ICollection

1) Чтож, спасибо за такой развёрнутый ответ. Видимо нужно будет пройти этот нелёгкий путь.
2) SP - это .Net класс, он генерируется средствами визарда СУБД. Можно код генерить, можно сборку .нет.

 System::Collections::ICollection *zzz=Sp->Primitives->Values;//Чтобы поняли тип переменной 

Я для этого эту строчку и поставил, чтобы показать, что это коллекция.

3) Он создаёт проект, генерит код, с ним проще чем врукопашную.
Попробую без OldSyntax откомпилить.

Re: for each для ICollection

Итак всё прекрасно работает... я в восторге :)

Убрал OldSyntax. Поставил ^ вместо * и gcnew вместо new и проект компилится и всё быстро обрабатывает :)
Тема закрыта :)