[转载]在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:\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.
 

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.

原文地址:https://www.cnblogs.com/jason2004/p/8534671.html

时间: 2024-11-08 23:06:20

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

在SpringMVC中获取request对象的几种方式

1.最简单的方式(注解法) 1 2 @Autowired private  HttpServletRequest request; 2.最麻烦的方法 a. 在web.xml中配置一个监听 <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> b.之后在程序里可以用 Http

在SpringMVC中获取request对象

1.注解法 Java代码   @Autowired private  HttpServletRequest request; 2. 在web.xml中配置一个监听 Xml代码   <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> 之后在程序里可以用 Java代码   H

如何在SpringMVC中获取request对象

1.注解法 @Autowired private HttpServletRequest request; 2. 在web.xml中配置一个监听 HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); 3.直接在参数中引入 public String hello(HttpServletRequest request,HttpS

9.Struts2在Action中获取request-session-application对象

为避免与Servlet API耦合在一起,方便Action类做单元测试. Struts2对HttpServletRequest.HttpSession.ServletContext进行了封装,构造了三个Map对象来替代这三种对象. 注意,这三个对象与Servlet API中的三个对象是相同的. 即,在Action中放入Session中信息,在JSP页面中是可以读出来的. 方式一:通过使用ActionContext类获取 在Struts2框架中,通过Action的执行上下文类ActionConte

springmvc中获取request对象,加载biz(service)的方法

获取request对象: 首先配置web.xml文件--> [html] view plaincopy <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> 然后在程序中获取: 代码: [java] view plaincopy HttpServletRequest 

Lucene.Net 3.0.3如何从TokenStream中获取token对象

Lucene.Net最高版本为3.0.3,并且apache已经不再提供Lucene.Net的更新,没仔细研究过Lucene.Net的所有版本,Lucene.Net3.0.3遍历TokenStream获取Token对象,已经和以前的版本有了很大的区别,很多方法都已经删除了或者过时. 以前版本的Lucene.Net从TokenStream中获取Token时调用Next方法就行了,源代码如下 public void ReusableTokenStream2() { string testwords =

在spring中获取代理对象代理的目标对象工具类

问题描述:: 我现在遇到个棘手的问题,要通过spring托管的service类保存对象,这个类是通过反射拿到的,经过实验发现这个类只能反射取得sservice实现了接口的方法,而extends类的方法一律不出现,debug后发现这个servie实例被spring替换成jdkdynmicproxy类,而不是原始对象了,,它里面只有service继承的接口方法,而没有extends 过的super class方法,怎么调用原生对象的方法!!!!! 用托管的spring service类调用getCl

Magento路由分发过程解析(一):在前端控制器中获取路由对象(转)

Magento的路由系统,需要考虑到两个抽象层. 1,首先你需要了解,可能会有无数多个路由对象负责处理路由逻辑,最后只有一个路由对象能够获取并处理该请求.默认情况下,Magento拥有四个路由对象. 2,在这四种路由对象内,又有一系列不同的规则用于匹配url地址到相应的控制器方法.这些规则非常相似,只有一些细微的差别. 路由匹配迭代过程 Magneto的路由开始于前端控制器对象的Mage_Core_Controller_Varien_Front::dispatch()方法,在如下循环中,选择合适

手动从Spring中获取指定对象

1.创建ApplicationContext对象: ApplicationContext context = new FileSystemXmlApplicationContext("D:/workspace/test/resources/applicationContext-resource.xml"); 2.调用getBean方法(注意参数以xml配置中的bean的名字匹配):     trDAO = (ITestResultDAO) context.getBean("t