• [转载]在VB.Net中获取COM对象的特定实例(Getting a specific instance of COM object in VB.Net)


    转载:http://www.it1352.com/534235.html

    问题:

    I am writing a Windows Form application in .Net to list all running instances of a third-party CAD/CAM software (in this case CATIA) and let user to choose one of them to perform couple of automated tasks. For performing automated tasks, I need to get the specific instance of COM objects - compared to Getobject() which gives me a non-specific COM instance. Is there a way to get a specific COM instance using window handle or any other methods?

    UPDATE: As Raymond said there is no single solution for all COM objects; however I managed to get CATIA COM objects using following code (Which uses ROT to fill a list with all CATIA COM Instances name):

    <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
    

    However, all CATIA instances refer to first CATIA application loaded. No idea why, anybody?

    解决方案

    The "problem" in your code is that calling GetObject always returns the first active server that it finds in the Running Object Table (ROT). Enumerating the ROT doesn't change that behavior and is a little frustrating because it does show that there is more than one server in the ROT. Note that some of the items returned in the enumeration may not actually be running: GetObject returns the first active server -- not necessarily the first one returned by the enumeration.

    However, in the case of CATIA in particular it is possible to get a specific instance. I suspect it is possible with many applications if you can get the particular instance of interest to run some code, on demand, before you actually get a pointer to the COM instance.

    For CATIA, this is a rough outline of the process I use:

    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:TempCatiaOnTheFlyCatScriptsOnTheFlyCatScript.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:TempCatiaOnTheFlyCatScripts" 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:TempCatiaOnTheFlyCatScriptsOnTheFlyCatScript.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.
     

    It's a lot of work with many "moving" parts but it does allow you to automate multiple CATIA sessions "simultaneously". Works well for my purposes: automated extraction of data from a set of CATIA models and automated creation of a set of CATIA models using more than one CATIA session at a time. The bottleneck for my application is the individual CATIA session -- not CPU resources (using a dual processor 4 or 6 core per processor machine); adding more sessions improves throughput.

  • 相关阅读:
    吴裕雄--天生自然PHP-MySQL-JavaScript学习笔记:Cookies、会话和身份验证
    吴裕雄--天生自然PHP-MySQL-JavaScript学习笔记:表单处理
    吴裕雄--天生自然PHP-MySQL-JavaScript学习笔记:使用PHP访问MySQL
    吴裕雄--天生自然PHP-MySQL-JavaScript学习笔记:掌握MySQL
    吴裕雄--天生自然PHP-MySQL-JavaScript学习笔记:MySQL入门
    吴裕雄--天生自然PHP-MySQL-JavaScript学习笔记:实用PHP技术
    吴裕雄--天生自然PHP-MySQL-JavaScript学习笔记:PHP数组
    吴裕雄--天生自然PHP-MySQL-JavaScript学习笔记:PHP函数与对象
    (008)spring之BeanPostProcessor的执行时机及作用
    (007)spring容器获取ApplicationContext
  • 原文地址:https://www.cnblogs.com/jason2004/p/8534671.html
Copyright © 2020-2023  润新知