2013-07-15 3 views
3

Я пишу приложение Windows Form в .Net для перечисления всех запущенных экземпляров стороннего программного обеспечения CAD/CAM (в данном случае CATIA) и позволяет пользователю выбрать один из них выполнить несколько автоматизированных задач. Для выполнения автоматических задач мне нужно получить конкретный экземпляр объектов COM - по сравнению с Getobject(), который дает мне неспецифический экземпляр COM. Есть ли способ получить конкретный экземпляр COM, используя дескриптор окна или любые другие методы?Получение конкретного экземпляра COM-объекта в VB.Net

ОБНОВЛЕНИЕ: Поскольку Раймонд сказал, что для всех объектов COM нет единого решения; Однако мне удалось получить объекты CATIA COM, используя следующий код (который использует ROT, чтобы заполнить список со всем CATIA COM именем Instances):

<DllImport("user32.dll", CharSet:=CharSet.Auto)> Private Shared Sub GetClassName(ByVal hWnd As System.IntPtr, ByVal lpClassName As System.Text.StringBuilder, ByVal nMaxCount As Integer) End Sub 
<DllImport("ole32.dll", ExactSpelling:=True, PreserveSig:=False)> Private Shared Function GetRunningObjectTable(ByVal reserved As Int32) As IRunningObjectTable End Function 
<DllImport("ole32.dll", CharSet:=CharSet.Unicode, ExactSpelling:=True, PreserveSig:=False)> Private Shared Function CreateItemMoniker(ByVal lpszDelim As String, ByVal lpszItem As String) As IMoniker End Function 
<DllImport("ole32.dll", ExactSpelling:=True, PreserveSig:=False)> Private Shared Function CreateBindCtx(ByVal reserved As Integer) As IBindCtx End Function 

Try 

    Dim ROTObject As Object = Nothing 
    Dim runningObjectTable As IRunningObjectTable 
    Dim monikerEnumerator As IEnumMoniker = Nothing 
    Dim monikers(1) As IMoniker 

    runningObjectTable = GetRunningObjectTable(0) 
    runningObjectTable.EnumRunning(monikerEnumerator) 
    monikerEnumerator.Reset() 

    Dim numFetched As IntPtr = New IntPtr() 
    While (monikerEnumerator.Next(1, monikers, numFetched) = 0) 
     Dim ctx As IBindCtx 
     ctx = CreateBindCtx(0) 

     Dim runningObjectName As String = "" 
     monikers(0).GetDisplayName(ctx, Nothing, runningObjectName) 

     runningObjectName = runningObjectName.ToUpper 
     If (Not runningObjectName.Equals("")) Then 
      Dim runningObjectIns As Object = Nothing 
      runningObjectTable.GetObject(monikers(0), runningObjectIns) 

      'Check if object is a Catia object 
      Try 
       Dim catiaIns As INFITF.Application = Nothing 
       catiaIns = DirectCast(runningObjectIns, INFITF.Application) 
       ListCATIA.Items.Add(catiaIns.Windows.Count) 
      Catch Exc As Exception 
       MessageBox.Show(Exc.ToString()) 
      End Try 
     End If 
    End While 

Catch Exc As Exception 
    Throw Exc 
End Try 

Однако все экземпляры CATIA относящиеся к первому приложению CATIA загруженном. Не знаю, почему, кто-нибудь?

+5

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

+0

@RaymondChen - Чтобы удалить этот вопрос из оставшейся без очереди очереди, я переместил ваш комментарий в ответ на wiki сообщества. Если вы хотите опубликовать ответ самостоятельно, оставьте комментарий к сообщению, и я его удалю. – JDB

ответ

3

«Проблема» в вашем коде, что вызов GetObjectвсегда возвращает первый активный сервер, который находит в таблице запущенных объектов (ROT). Перечисление ROT не меняет этого поведения и немного расстраивает, потому что оно показывает, что в ROT имеется более одного сервера. Обратите внимание, что некоторые из элементов, возвращаемых в перечислении, могут фактически не выполняться: GetObject возвращает первый сервер - не обязательно первый, возвращенный перечислением.

Однако, в случае CATIA, в частности, это есть возможно получить конкретный экземпляр. Я подозреваю, что это возможно во многих приложениях, если вы можете получить конкретный пример интереса к запуску некоторого кода по запросу до, вы фактически получите указатель на экземпляр COM.

Для CATIA, это грубая схема процесса я использую:


1. Make a dll with two functions: 
    HRESULT __stdcall CoMarshalToFile(IUnknown* punk, const char* const filePath) 
    /* uses `::CreateStreamOnHGlobal`, `::CoMarshalInterface`, `::CoGetMarshalSizeMax`, 
    and `::GetHGlobalFromStream` to marshal the IUnknown to the specified file. 
    */ 
    HRESULT __stdcall CoMarshalFromFile(IUnknown** ppunk, const char* const filePath) 
    /* uses `::CreateStreamOnHGlobal` and `::CoUnmarshalInterface` to marshal 
    from the file to an IUnknown pointer. 
    */ 

2. In CATIA: 
    Note: this only needs to be done on the development computer. 
    Make a new "VBA projects" macro library. 
    Add "declare" statements for: 
     "LoadLibrary" (Windows API) 
     "CoMarshalToFile" (DLL specified above) 
    Add a function 
     Public Function MarshalCatiaToFile _ 
      (marshalInstanceFilePath As String, _ 
       marshalDllFolder As String) As Long 

    MarshalCatiaToFile calls "LoadLibrary" to load the C++ DLL 
    and then calls CoMarshalToFile (in DLL) to marshal the CATIA instance 
    to a file. 

    Remove the macro library from CATIA's list of macro libraries. 

3. Create a file: 
    "C:\Temp\CatiaOnTheFlyCatScripts\OnTheFlyCatScript.catvbs" 
    The file can be empty. 

4. In CATIA: 
     Note: this must be done for *each* user of CATIA on *each* computer used. 
     It may be possible to make this available to all users without individual 
     setup required: it is saved in "FrameUserAliases.CATSettings" 
     It may also be possible to reverse engineer the settings file and set up 
     the needed data from outside CATIA. 

    Add "C:\Temp\CatiaOnTheFlyCatScripts\" as a new "Directories" macro library. 
    Make the added library "current" 
    Use "Tools --> Customize --> Commands --> Macros" to assign a 
     "User Alias:" to the "OnTheFlyCatScript.catvbs" script file. 
     Name the alias "ExecuteOnTheFlyCatScript". 
    Remove the macro library from CATIA's list of macro libraries. 
    Close CATIA at this point to force the changes to be saved. 

5. VB.net/C# program: 
     Add the DLL (from step 1) and the CatVBA macro library (from step 2) as 
     "Embedded Resource" to the project. 

     During program execution: 
     Extract the DLL and macro library to an appropriate location. 
     Load the DLL into session using "LoadLibrary". 
     Create the file: 
      "C:\Temp\CatiaOnTheFlyCatScripts\OnTheFlyCatScript.catvbs" 

     The "OnTheFlyCatScript.catvbs" will be executed in CATIA. It 
      uses CATIA.SystemService.ExecuteScript to execute the 
      "MarshalCatiaToFile" function in the CatVBA macro library. 
      Add method of choice to this file to indicate success/failure. 
      I use a dialog box with the appropriate title. 

     To execute the "OnTheFlyCatScript.catvbs": 
      Using the Windows API functions, get the window handle for the 
      "Power Input" box at the bottom right of the "desired" 
      CATIA window. 
      Using the Windows API functions (*NOT* "SendKeys") send 
      "c:ExecuteOnTheFlyCatScript" + {Enter} to the "Power Input". 
      Wait for the "completion" signal from the script. If you used 
      a dialog box, use the Windows API function to close it. 

     Assuming the script succeeded in marshaling the CATIA instance to 
      a file, call the DLL function CoMarshalFromFile to get the CATIA 
      instance. 

Это много работы со многими «движущихся» частей, но это позволяет автоматизировать несколько сеансов CATIA «одновременно». Хорошо работает для моих целей: автоматическое извлечение данных из набора моделей CATIA и автоматическое создание набора моделей CATIA с использованием более чем одного сеанса CATIA за раз. Узким местом для моего приложения является отдельный сеанс CATIA - не ресурсы ЦП (с использованием двухпроцессорного процессора 4 или 6 ядер на процессор); добавление дополнительных сессий повышает пропускную способность.

Смежные вопросы