ADN Open CIS
Сообщество программистов Autodesk в СНГ

08/01/2014

Доступ к COM-приложениям из Таблицы исполняющихся объектов (ROT)

Вот тема, которая то и дело всплывает: как перейти к определенному экземпляру COM-приложения, если есть несколько экземпляров имеющих аналогичный ProgId? Вызов GetActiveObject (progId) может вернуть только один объект, так что нет никакого способа, чтобы выбрать нужный конкретный экземпляр.

Обойти это ограничение можно используя Таблицу исполняющихся объектов (Running Object Table), которая может предоставить вам доступ к каждому экземпляру COM, работающему на машине.

Вот пример кода на C#, который показывает, как добиться этого:

Код - C#: [Выделить]
  1. [DllImport("ole32.dll")]
  2. static extern int CreateBindCtx(
  3.     uint reserved,
  4.     out IBindCtx ppbc);
  5.  
  6. [DllImport("ole32.dll")]
  7. public static extern void GetRunningObjectTable(
  8.     int reserved,
  9.     out IRunningObjectTable prot);
  10.  
  11. // Не забываем: using System.Runtime.InteropServices.ComTypes
  12. // Получение всех исполняющихся экземпляров из ROT
  13. private List<object> GetRunningInstances(string[] progIds)
  14. {
  15.     List<string> clsIds = new List<string>();
  16.  
  17.     // Получение clsid для приложения
  18.     foreach (string progId in progIds)
  19.     {
  20.         Type type = Type.GetTypeFromProgID(progId);
  21.  
  22.         if(type != null)
  23.             clsIds.Add(type.GUID.ToString().ToUpper());
  24.     }
  25.  
  26.     // Получение Таблицы исполняющихся объектов (ROT) ...
  27.     IRunningObjectTable Rot = null;
  28.     GetRunningObjectTable(0, out Rot);
  29.     if (Rot == null)
  30.         return null;
  31.  
  32.     // Получаем перечислитель записей ROT
  33.     IEnumMoniker monikerEnumerator = null;
  34.     Rot.EnumRunning(out monikerEnumerator);
  35.  
  36.     if (monikerEnumerator == null)
  37.         return null;
  38.  
  39.     monikerEnumerator.Reset();
  40.  
  41.     List<object> instances = new List<object>();
  42.  
  43.     IntPtr pNumFetched = new IntPtr();
  44.     IMoniker[] monikers = new IMoniker[1];
  45.  
  46.     // Проходим по всем записям и идентифицируем экземпляры приложений
  47.     while (monikerEnumerator.Next(1, monikers, pNumFetched) == 0)
  48.     {
  49.         IBindCtx bindCtx;
  50.         CreateBindCtx(0, out bindCtx);
  51.         if (bindCtx == null)
  52.             continue;
  53.  
  54.         string displayName;
  55.         monikers[0].GetDisplayName(bindCtx, null, out displayName);
  56.                
  57.         foreach (string clsId in clsIds)
  58.         {
  59.             if (displayName.ToUpper().IndexOf(clsId) > 0)
  60.             {
  61.                 object ComObject;
  62.                 Rot.GetObject(monikers[0], out ComObject);
  63.  
  64.                 if (ComObject == null)
  65.                     continue;
  66.  
  67.                 instances.Add(ComObject);
  68.                 break;
  69.             }
  70.         }
  71.     }
  72.  
  73.     return instances;
  74. }
  75.  
  76. void TestROT()
  77. {
  78.     // Ищем AutoCAD 2009 и 2010 и 2014
  79.     string[] progIds =
  80.     {
  81.         "AutoCAD.Application.17.2",
  82.         "AutoCAD.Application.18",
  83.         "AutoCAD.Application.19.1"
  84.     };
  85.  
  86.     List<object> instances = GetRunningInstances(progIds);
  87.  
  88.     foreach (object acadObj in instances)
  89.     {
  90.         try
  91.         {
  92.             // Выполняем запланированные действия с выбранным приложением ... 
  93.         }
  94.         catch
  95.         {
  96.            
  97.         }
  98.     }
  99. }

Источник: http://adndevblog.typepad.com/autocad/2013/12/accessing-com-applications-from-the-running-object-table.html

Обсуждение: http://adn-cis.org/forum/index.php?topic=433

Опубликовано 08.01.2014