Как получить список документов, если открыто два приложения (процесса) Компаса

Автор kabakov, 13.07.11, 16:36:48

« назад - далее »

0 Пользователи и 1 гость просматривают эту тему.

kabakov

Добрый день.

Я за сторонней помощью обращаюсь редко, но возник такой случай, что без нее никак.

Проблема заключается в следующем. Пусть запущено два приложения Компаса. В одном из них открыты одни файлы, в другом – другие. Вопрос состоит в том, как программно (во внешнем exe файле) получить общий список файлов, который открыт и в одном приложении, и в другом.

Задача сводится к тому, чтобы получить объект Kompas.Application сначала для одного приложения, затем для другого.

Если использовать функции (например, на языке C#)

var myKompasObj = Marshal.GetActiveObject("Kompas.Application.5");

то в результате получим объект только для одного из приложений. Подключиться ко второму приложению Компаса в такой способ нельзя.

Пробовал использовать RunningObjectTable (ROT). Оказалось, что в данную таблицу заносится информация только об одном из открытых приложений Компаса (а вот, например, для Excel, то для каждого приложения есть отдельная запись).

Привожу часть программы использованием RunningObjectTable:

IntPtr numFetched = IntPtr.Zero;
IRunningObjectTable runningObjectTable;
IEnumMoniker monikerEnumerator;
IMoniker[] monikers = new IMoniker[1];

GetRunningObjectTable(0, out runningObjectTable);
runningObjectTable.EnumRunning(out monikerEnumerator);
monikerEnumerator.Reset();
while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
{
   IBindCtx ctx;
   CreateBindCtx(0, out ctx);

   string runningObjectName;
   monikers[0].GetDisplayName(ctx, null, out runningObjectName);
            
   if(runningObjectName.ToUpperInvariant()=="!{6B0B5194-4ACD-4095-9BC1-11179FBBB05A}")
   {
      object runningObjectVal;
      runningObjectTable.GetObject(monikers[0], out runningObjectVal);
               
              // runningObjectVal – искомый объект приложения
   }
}

Пробовал использовать функцию AccessibleObjectFromWindow. В результате хоть и удалось получить объекты для двух главных окон приложений, но далее как из них получить объект Kompas.Application не имею ни малейшего понятия.

foreach (var p in Process.GetProcessesByName("KOMPAS"))
{
object ptr = null;
   int hr = AccessibleObjectFromWindow((int)p.MainWindowHandle, 0x00000000,
      new Guid("{00020400-0000-0000-C000-000000000046}").ToByteArray(), ref ptr);

   if (hr >= 0)
   {
      // Что делать не знаю
   }
}

Перепробовал еще кучу разных «глупых» вариантов, результат примерно тот же.

Если кто-то ставил себе задачу по работе с несколькими приложениями Компаса, пожалуйста, отпишитесь.
Заранее всем спасибо.

Pollitruk

А зачем нужно запускать 2 Компаса? аскон ведь в мануале запрещает это!

kabakov

Аскон то в мануале запрещает, но далеко не все пользователи следуют этому. Каждый работает так как ему удобно - кто в одном окне, кто в 5-ти разных

Все, что запрещено - это не значит, что не возможно, а зачем нужно запускать 2 Компаса?

kabakov

Я с Вами полностью согласен, что незачем. Но в реалии так бывает, и хотелось бы иметь возможность программно управлять как одним, так и другим приложением

Скорее всего Вам нужно создать многопоточное приложение, чтобы контролировать Компас запущенный в отдельных потоках.

Alexey_Ovtses

У меня та же самая задача - получить контроль над объектами Kompas.Application если запущено более одного компаса.

Через ROT как я понял невозможно отследить экземпляры Kompas.Application, ROT всегда выдает один экземпляр - последний запущенный.

Возможно есть какой либо способ получить Экземпляры Kompas.Application через дескриптор окна?
на данный момент имею вот такой код:

        Dim ProList() As Process
        ProList = Process.GetProcesses()
        Dim i As Integer = 1
        For i = 0 To ProList.Length - 1
            If ProList(i).ProcessName = "KOMPAS" Then
                Dim IntPt As System.IntPtr = Nothing
                IntPt = ProList(i).MainWindowHandle
                Const OBJID_NATIVEOM As UInteger = &H0&
                Dim IID_IDispatch As New Guid("{00020400-0000-0000-C000-000000000046}")
                Dim ptr2 As System.IntPtr = Nothing
                Dim hr As Integer = AccessibleObjectFromWindow(IntPt, OBJID_NATIVEOM, IID_IDispatch.ToByteArray(), ptr2)
                Dim obj As Object = Nothing
                obj = Runtime.InteropServices.Marshal.GetObjectForIUnknown(ptr2)
                If obj IsNot Nothing Then
                    Dim ka As Kompas6API5.Application = Nothing
                    ka = CType(obj, Kompas6API5.Application)
                    ka.Visible = False
                    MsgBox(123)
                    ka.Visible = True
                End If
            End If
        Next

некий объект находится с помощью AccessibleObjectFromWindow но далее при попытке привести его к Kompas6API5.Application вылетает ошибка

Невозможно привести COM-объект типа "System.__ComObject" к интерфейсному типу "Kompas6API5.Application". Операция завершилась со сбоем, поскольку вызов QueryInterface COM-компонента для интерфейса с IID "{E36BC97C-39D6-4402-9C25-C7008A217E02}" возвратил следующую ошибку: Интерфейс не поддерживается (Исключение из HRESULT: 0x80004002 (E_NOINTERFACE)).

Falcon555

Цитата: Sabahs от 13.07.11, 18:33:11
а зачем нужно запускать 2 Компаса?
Сам так пользовался раньше, пока Компас перестраивал чертеж сборки (минут по 10-15), запускал вторую копию Компаса и работал там с другими файлами.
Вполне себе работалось, без сбоев и глюков.

Сделайте Dll типа AddIns, которая автоматически будет внедрятся в процесс Компаса при запуске, далее контроль, ограничивается, полётом Вашей фантазии и возможностей.

Alexey_Ovtses

Сделал по вашему совету Addins библиотеку, в процессе она отслеживается, но что делать дальше никак не пойму.
Можно создать в библиотеке процедуру которая будет выдавать ссылку на объект Kompas.Application но как вызвать эту процедуру из моего приложения?

Дальше наладить общение между библиотеками и Вашим приложением. Для двух запущенных Компасов будет запущено две Addins библиотеки, они могут найти Ваше приложение и послать ему сообщение, также Ваше приложение может послать сообщения библиотекам с определёнными действиями, которые выполнят библиотеки.
+ Благодарностей: 2

Alexey_Ovtses

Решение найдено:

Алгоритм такой (может кому-нибудь будет полезно)

создаем библиотеку addin

в головной функции библиотеки - LibInterfaceNotifyEntry создаем новый моникер (я выбрал CreateItemMoniker)
CreateItemMoniker(Nothing, kompas.ksGetHWindow.ToString, moniker)
в качестве идентификатора экземпляра я выбрал хендл окна - он потребуется чтобы отловить компасы в управляющем приложении

регистрируем созданный моникер в ROT
runningObjectTable.Register(1, kompas, moniker)

в управляющем приложении находим хендалы окон компаса

в управляющем приложении подключаемся к ROT и в цикле находим моникеры совпадающие с хендалами окон компаса

и подключаемся к требуемому экзепляр компаса  с помощью runningObjectTable.GetObject(monikers(0), ComObject)

В данный момент решение протестировано на 2-х машинах - win7 х86 и win7 х64 под обычным пользователем