【问题标题】:Getting a specific instance of COM object in VB.Net在 VB.Net 中获取 COM 对象的特定实例
【发布时间】:2013-07-13 13:32:55
【问题描述】:

我正在.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 应用程序。不知道为什么,有人吗?

【问题讨论】:

  • 没有通用的解决方案。你需要看看有问题的服务器是否有枚举实例或获取特定实例的方法。
  • @RaymondChen - 为了从未回答的队列中删除此问题,我已将您的评论移至社区 wiki 答案中。如果您想自己发布答案,请在帖子上发表评论,我会删除它。

标签: vb.net com catia


【解决方案1】:
【解决方案2】:

代码中的“问题”是调用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 核);添加更多会话可提高吞吐量。

【讨论】:

    【解决方案3】:

    通过 COM 接口是不可能的。 This problem is permanent restriction in Catia

    【讨论】:

    • 可能的——只需要一点点努力和准备。看我的回答。
    猜你喜欢
    • 2021-01-04
    • 2012-03-09
    • 1970-01-01
    • 2011-07-24
    • 2017-06-09
    • 2015-08-19
    • 1970-01-01
    • 1970-01-01
    • 2021-04-22
    相关资源
    最近更新 更多