4

サードパーティの CAD/CAM ソフトウェア (この場合は CATIA) の実行中のすべてのインスタンスを一覧表示し、ユーザーがそのうちの 1 つを選択していくつかの自動化されたタスクを実行できるようにするために、.Net で Windows フォーム アプリケーションを作成しています。自動化されたタスクを実行するには、COM オブジェクトの特定のインスタンスを取得する必要があります。Getobject() では非特定の COM インスタンスが得られます。ウィンドウ ハンドルまたはその他の方法を使用して特定の COM インスタンスを取得する方法はありますか?

更新:レイモンドが言ったように、すべての 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 アプリケーションを参照します。理由がわからない、誰か?

4

3 に答える 3

3

コードの「問題」は、実行中のオブジェクト テーブル (ROT) で検出された最初のアクティブなサーバーを呼び出しGetObject が常に返すことです。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 コアを使用)。セッションを追加すると、スループットが向上します。

于 2014-10-31T23:02:14.030 に答える