代码之家  ›  专栏  ›  技术社区  ›  Cleric

C#运行主动自动化对象-如何源事件?

  •  2
  • Cleric  · 技术社区  · 6 年前

    我有一个(长时间运行的)控制台应用程序,是用C#编写的,我希望能够通过COM操作它(因此没有InProc DLL和regasm.exe)。 IDispatch 这是我所需要的全部,所以是一个经典的OLE自动化对象。

    在这里,我将介绍我尝试做的事情的一个最低版本。我定义了如下COM类:

    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    [Guid("9009311a-c0b2-42a4-8e7c-f42091d71594")]
    public interface ITestEvents {
        void OnEvent();
    }
    
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDispatch)]
    [ComSourceInterfaces(typeof(ITestEvents))]
    public class ComClass {
        public event Action OnEvent;
    
        public int Test() {
            return 100;
        }
    }
    

    在里面 Main() 我只需在运行对象表(ROT)中注册对象 "comTestApp" 命名并休眠应用程序。 您可以查看完整的源代码 here .

    当我尝试调用对象的方法时,这很好。例如,此VBScript工作正常:

    Set obj = GetObject("comTestApp")
    WScript.Echo obj.Test() Rem prints 100
    

    但当我尝试将事件联系起来时:

    Set obj = GetObject("comTestApp")
    WScript.Echo obj.Test() Rem Works
    
    WScript.ConnectObject obj, "obj_" Rem Fails
    
    Sub obj_OnEvent
        WScript.Echo "Wish it worked"
    End Sub
    

    我打电话时出错 ConnectObject (错误0x80020009“无法连接对象”)。

    如果我将程序集注册为regasm的InProc对象,则相同的代码(只需添加类GUID/ProgID)也可以使用。但我不需要那个。我需要访问正在运行的应用程序,这就是我使用ROT的原因。

    我创建了一个简单的C++测试,看看是否可以找到有关该问题的更多信息。来源是 here . 我已经编写了一个最小COM对象来实现 分发接口 应充当事件接收器的接口。首先,我从ROT中获取对象,查询 IConnectionPointContainer ,然后获取 IConnectionPoint 对于 ITestEvents 是IID,最后称其为 Advise() 方法与VBScript一样,它失败了(尽管我收到另一个错误-0x80040202)。我在 QueryInterface 事件接收器的方法,以查看 建议() 被调用。我看得出来 查询接口 为各种接口调用,最后它请求 ITestEvents公司 ,返回并设置状态 S_OK . 但仍然 建议() 方法返回上述错误。

    我还尝试了另一件事:我设置了 ITestEvents公司 {00020400-0000-0000-C000-000000000046} 这是的IID 分发接口 . 现在呢 建议() 退货 S\u正常 ! 我甚至模拟了一个事件 Invoke() 调用事件接收器的方法!唉,总的来说,这并不能解决问题。如果您直接从 I连接点容器 通过IID-你得到了,但它似乎没有被正确地列举出来 ITypeInfo VBScript仍然无法工作。

    我几乎没有COM方面的经验,所以我不知道接下来该怎么办。如果我使用 分发接口 IID让它工作起来,让我想知道 ITestEvents公司 界面,尽管它是纯的 分发接口 所以我认为运行时应该很好地处理它。

    非常感谢。

    1 回复  |  直到 6 年前
        1
  •  0
  •   Cleric    6 年前

    我的结论是,不可能提供接口IID未在注册表中注册的连接接收器(到进程外对象)。

    为了将接收器接口发送到服务器,封送信息似乎是必需的。我希望既然C类课程供应充足 ITypeInfo ,COM运行时将发现接口 IDispatch 键入并使用默认代理。唉,情况似乎并非如此,因此唯一的选择仍然存在——注册表。

    至少我发现这是必要的:

    [HKEY_CLASSES_ROOT\Interface\{9009311a-c0b2-42a4-8e7c-f42091d71594}\ProxyStubClsid32]
    @="{00020424-0000-0000-C000-000000000046}"
    

    这定义了我们的事件接口,并表示我们使用标准IDispatch代理作为代理。

    我已经在Windows 7和10上测试过了,效果很好VBScript can ConnectObject 一切正常。

    无意中,我发现一个Win7安装无法正常运行(win ver 6.1 build 7601 sp1)。它只有在注册类型库后才起作用( regasm.exe /tlb ). 所讨论的安装已禁用Windows更新(我的另一个Win7已完全更新),因此我猜在某种程度上发生了更改,这使得即使没有类型库,也可以封送接口,因为我们指定了默认的IDispatch代理。

    最后,由于我想保持我的应用程序的可移植性和独立于注册表,我求助于手动事件实现。可以看到类似的情况 here .