在VB.Net中获取特定的COM对象实例

4
我正在使用.Net编写一个Windows窗体应用程序,以列出所有运行中的第三方CAD/CAM软件实例(在本例中为CATIA),并允许用户选择其中一个来执行一些自动化任务。为了执行自动化任务,我需要获取特定实例的COM对象-与GetObject()相比,后者给我一个非特定的COM实例。是否有一种方法可以使用窗口句柄或其他方法获取特定的COM实例?
更新: 正如Raymond所说,对于所有COM对象没有单一的解决方案;但是,我成功地使用以下代码获取CATIA COM对象(该代码使用ROT来填充一个列表,其中包含所有CATIA COM实例的名称):
<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
没有通用的解决方案。你需要查看相关的服务器是否有枚举实例或获取特定实例的方法。 - Raymond Chen
@RaymondChen - 为了移出未回答的问题队列,我已将您的评论转成社区维基回答。如果您想自己发布答案,请在帖子下留言,我会将其删除。 - JDB
3个回答

4
您代码中的"问题"在于调用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会话,而不是CPU资源(使用每个处理器4或6个核心的双处理器机器);添加更多会话可提高吞吐量。



0

这是可能的 - 只需要一点努力和准备。看看我的回答。 - Bob Reynolds

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接