Здравствуйте! Есть ли возможность применять многопоточность и распараллеливание в работе с API Компаса? Есть ли есть такая возможность, то как?
Это я к чему: сделал программную замену текста в в объектах типа "текст" (IDrawingText), "выноски" (ILeader, IPositionLeader) - в ней дерево получения объектов от более старших, проверка на Null и перебор последовательный. Однако есть стандартная библиотека замены текста и она работает в разы шустрее. Подозреваю что дело в многопоточности или распараллеливании, может быть реализована асинхронность вызовов. Или может у меня принцип перебора объектов неправильный? Или дело в том, что я писал оба варианта на C# и Delphi, а надо было на C++? Хотя когда дело касается графической прорисовки - тоже самое - стандартные библиотеки прорисовывают практически мгновенно(кажется что все сразу параллельно отрисовывается), а собственные через API - медленно и видно как все идет последовательно. Еще замечание - почему то если зажать ролик мыши работа с API идет быстрее, но все равно со стандартными RTW-библиотеками не сравнится.
В общем как мне увеличить скорость работы замены текста до стандартной библиотеки? В чем хитрость?
Стандартные RTW-библиотеки грузятся внутрь Компаса (т.е. инициатор загрузки сам Компас) и для них не осуществляется маршаллинг на межпроцессное или межпотоковое(межапартментное) взаимодействие, как это, скорее всего, реализовано у тебя, если твой клиент дёргает Компас через АПИ или СОМ.
Как правило, ускорение достигается применением массивных (т.е. с массивами информации) операций, чтобы не было большого количества передач данных от клиента в Компас и обратно.
Если таких операций нет, то вряд ли можно ускорить. ИМХО, удобство внешнего клиента стоит больше, хоть и нужно подождать.
Цитата: Vi2 от 29.10.19, 22:13:37Стандартные RTW-библиотеки грузятся внутрь Компаса (т.е. инициатор загрузки сам Компас) и для них не осуществляется маршаллинг на межпроцессное или межпотоковое(межапартментное) взаимодействие, как это, скорее всего, реализовано у тебя, если твой клиент дёргает Компас через АПИ или СОМ.
Как правило, ускорение достигается применением массивных (т.е. с массивами информации) операций, чтобы не было большого количества передач данных от клиента в Компас и обратно.
Если таких операций нет, то вряд ли можно ускорить. ИМХО, удобство внешнего клиента стоит больше, хоть и нужно подождать.
У меня работа из под оконного приложения EXE идет через API5 и API7, а не из под RTW. То есть из самодельной пользовательской библиотеки RTW будет все работать быстро? Или только RTW созданные самим АСКОНом будут работать быстро?
Как считаете например такой код как-то можно ускорить или уже все хорошо в нем?:
IDrawingTexts drawingTexts = DrawingContainer.DrawingTexts;
if (drawingTexts != null)
{
if (cbTexts.Checked) ReplacesCount = drawingTexts.Count;
for (int j = 0; j < ReplacesCount; j++)
{
IDrawingObject drawingObject = drawingTexts.DrawingText[j];
if (drawingObject != null)
{
if (drawingObject.DrawingObjectType == DrawingObjectTypeEnum.ksDrDrawText)
{
IDrawingText drawingText = (IDrawingText)drawingObject;
if (drawingText != null)
{
IText text = (IText)drawingText;
if (text != null)
{
text.Str = text.Str.Replace("A", "B");
drawingObject.Update();
drawingText = null;
}
}
}
}
drawingObject = null;
}
drawingTexts = null;
}
Я не слишком хорошо знаком с объектной моделью Компаса. Предыдущее сообщение я дал на основании общих сведений о взаимодействии СОМ серверов. Из этих же сведений выскажу такие соображения по поводу кода.
1) drawingTexts.DrawingText[j] уже возвращает интерфейс IDrawingText, который также является и интерфейсом IDrawingObject. Так что и на этом интерфейсе можно запрашивать свойство DrawingObjectType. Т.е.
IDrawingText drawingText = drawingTexts.DrawingText[j];
if (drawingText != null && drawingText.DrawingObjectType == DrawingObjectTypeEnum.ksDrDrawText)
{ ...
На этом можно выиграть несколько обращений к серверу.
2) интерфейс IText сам имеет свойство Replace( BSTR SrcText, BSTR NewText, BOOL Case, BOOL WordOnly, BOOL ReplaceAll).
На этом также экономится одно обращение.
PS
В принципе, если критично время, то можно написать маленький RTW, который, будучи загруженным в Компас, сам может стать сервером и реагировать на внешние команды от стороннего приложения. Для этого нужно зарегистрировать свой объект и получить к нему доступ.
Цитата: Vi2 от 30.10.19, 19:06:52Я не слишком хорошо знаком с объектной моделью Компаса. Предыдущее сообщение я дал на основании общих сведений о взаимодействии СОМ серверов. Из этих же сведений выскажу такие соображения по поводу кода.
1) drawingTexts.DrawingText[j] уже возвращает интерфейс IDrawingText, который также является и интерфейсом IDrawingObject. Так что и на этом интерфейсе можно запрашивать свойство DrawingObjectType. Т.е.
IDrawingText drawingText = drawingTexts.DrawingText[j];
if (drawingText != null && drawingText.DrawingObjectType == DrawingObjectTypeEnum.ksDrDrawText)
{ ...
На этом можно выиграть несколько обращений к серверу.
2) интерфейс IText сам имеет свойство Replace( BSTR SrcText, BSTR NewText, BOOL Case, BOOL WordOnly, BOOL ReplaceAll).
На этом также экономится одно обращение.
PS
В принципе, если критично время, то можно написать маленький RTW, который, будучи загруженным в Компас, сам может стать сервером и реагировать на внешние команды от стороннего приложения. Для этого нужно зарегистрировать свой объект и получить к нему доступ.
1. Спасибо, попробую сделать подобный образом.
2. И так тоже попробую, если конечно не возникнет проблема, решенная ранее на другой ветке форума - если менять свойство str прям у него, то пропадают назначенные стили текста и становятся по умолчанию для документа, а если редактировать его у ITextItem то все в порядке. Пришлось делать так.
3. Да хорошая идея, сделать рабочую RTW, которая будет управляться из внешнего графического Exe-приложения. Только как как передавать в RTW параметры?
Еще момент, Вы не в курсе можно ли как-то в таблицах взять текст сразу всех ячеек? а то пробег по всем ячейкам тоже долго идет очень.
>Только как как передавать в RTW параметры?
1. После загрузки RTW в Компас создаётся свой СОМ объект, который регистрируется через функции подобные С++ RegisterActiveObject и удаляется после завершения через RevokeActiveObject. После регистрации этот объект доступен через GetActiveObject, точно так же как и сам Компас. Соответственно, после всего этого можно вызывать все его методы и передавать данные.
2. Или другой сценарий. После загрузки RTW в Компас эта RTW вызывает/создаёт СОМ объект, в который передаёт свой интерфейс.
3. Есть такой механизм, который называется ObjrefMoniker (функции CreateObjrefMoniker и GetDisplayName). Получается строчка текста, по которой можно получить доступ к СОМ объекту (функция CoGetObject). Эту строчку можно уже передавать между задачами как обычный текст.
>Вы не в курсе можно ли как-то в таблицах взять текст сразу всех ячеек? а то пробег по всем ячейкам тоже долго идет очень.
Я не совсем в курсе Компаса, но в Ворде/Экселе такое есть: можно одну ячейку за раз изменять, а можно сразу все в выделенной области Range. Для этого и используются массивные функции, где передаются массивы (SafeArray) - одномерные или двумерные. Реализовано ли такое в Компасе - не знаю. Хотя в твоём интерфейсе IText есть методы TextLine(i) по каждой строчке текста и Str() для всего текста целиком.
Немного посмотрел интерфейсы. Есть ITableRange, имеющий такой же смысл, как и в Ворде:
[propget, helpstring("Получить тексты из ячеек ввиде массива VT_ARRAY | VT_BSTR")]
HRESULT Texts([out, retval] VARIANT* PVal);
[propput]
HRESULT Texts([in] VARIANT PVal);
Спасибо большое! Вы прям для меня новый неизвестный затерянный мир открыли !!! Как много я не знаю оказывается.
Извините за долгое отсутствие ответа о результатах - был занят другой работой.
Что ускоряет:
1. Использование родной Replace у интерфейса IText ускоряет перебор объектов в 1,6-2 раза.
2. Также помогло - временного отключение визуальной оболочки КОМПАСа - об этом я узнал в другой теме. Это ускоряет в 5-6 раз.
До опробования распараллеливания по ядрам процессора и полного переноса функционала в RTW-библиотеку я пока не дошел, не успел.
Осталась проблема с медленным перебором ячеек таблицы. Было предложено использовать ITableRange и получать массив строк в виде VT_ARRAY | VT_BSTR, но что-то я делаю не правильно похоже и это не работает. Тексты то считываются, а изменить их не получается. Вот пример нерабочего кода:
void RENTable2(IDrawingTable drawingTable, string A, string B) // Переименование в таблицах
{
if (drawingTable != null)
{
ITable table = (ITable)drawingTable;
if (table != null)
{
ITableRange tableRange = (ITableRange)table.Range[0, 0, table.RowsCount - 1, table.ColumnsCount - 1];
if (tableRange != null)
{
//MessageBox.Show("есть");
Array textsArray = (Array)tableRange.Texts;
for (long w=0; w<textsArray.LongLength; w++)
{
string s = textsArray.GetValue(w).ToString();
s = s.Replace(A, B);
textsArray.SetValue(s,w);
}
//foreach (string txt in textsArray)
//{
// txt = txt.Replace(A, B);
// //string text = (string)obj;
// //if (text != null)
// //{
// // MessageBox.Show(text);
// //}
//}
}
drawingTable.Update();
}
table = null;
}
drawingTable = null;
}
Array textsArray = (Array)tableRange.Texts; // Это получение данных
for (long w=0; w<textsArray.LongLength; w++)
{
string s = textsArray.GetValue(w).ToString();
s = s.Replace(A, B);
textsArray.SetValue(s,w);
}
tableRange.Texts = textsArray; // Это обратная функция установки измененных данных. Синтаксис может быть неточен.
Цитата: Vi2 от 21.11.19, 22:58:41tableRange.Texts = textsArray; // Это обратная функция установки измененных данных. Синтаксис может быть неточен.
Вот на этой обратной функции вылазит ошибка:
System.Runtime.InteropServices.COMException
HResult=0x80010105
Сообщение = Ошибка на сервере. (Исключение из HRESULT: 0x80010105 (RPC_E_SERVERFAULT))
Вероятно требуется преобразование типа объекта textsArray в тип соответствующий tableRange.Texts, только как?
Цитата: Sprinter500 от 22.11.19, 07:03:42Вот на этой обратной функции вылазит ошибка:
System.Runtime.InteropServices.COMException
HResult=0x80010105
Сообщение = Ошибка на сервере. (Исключение из HRESULT: 0x80010105 (RPC_E_SERVERFAULT))
Вероятно требуется преобразование типа объекта textsArray в тип соответствующий tableRange.Texts, только как?
В документации написано, что нужно передавать массив строк. Как это выглядит на Шарпе, я не в курсе.
Провел эксперимент создал в Delphi библиотеку RTW, которая меняет определенный текст в таблице, без использования ITableRnge, то есть простым перебором ячеек путем 2х вложенных циклов For перебирающих строки и столбцы. RTW обычная, не Automation. То есть не ActiveX DLL. Далее создал тестовую таблицу 44х44 ячейки. Библиотека отработала за доли секунды, тогда как созданное в C# (а там только ActiveX и по другому никак) - работало около 20 сек. По итогу получается обычный COM при работе с КОМПАС шустрее чем ActiveX в десятки раз.
Теперь вопрос как на рабочем месте без прав админа (они у сисадминов, дергать их каждый раз не хочется) и без участия Delphi писать на C# шустрые варианты? Можно конечно управляемые извне обычные RTW, но скоро Delphi удалят сисадмины, останется только C#. Или может быть на Lazarus тоже возможно RTW клепать? У C++ не нравится синтаксис.