C# - 如何钩入现有的COM对象

4
假设我们有一个已经存在的进程(或应用程序),调用了一个来自ocx文件的COM对象,例如“MyCOMLibrary.ocx”。
是否有一种方法编写一个C#库,以完全复制ocx文件?这样原始应用程序就可以调用您的C#代码而不是原始的COM对象?
当然,您必须使用与原始ocx相同的CLSID和ProgID。假设没有签名,例如.NET世界中的SNK。
此外,是否存在任何工具自动化此过程?是否有一些工具可以接收OCX并生成一些实现方法的C#文件?
编辑:我想补充说,原始应用程序是VB6,并且根本不使用.NET。他们很可能像VB6应用程序一样加载ocx(ProgId或Guid)。这会引起任何问题吗?
我们还没有完全重写ocx的问题 - 我们很可能只为所有方法返回成功错误代码,并仅使用我们情况需要的方法/事件。
编辑:您会认为这不应该太难实现。我们可以创建一个VB6 ocx文件来替换旧的ocx,并将所有调用传递给.NET程序集吗?
编辑:我尝试使用以下开源库:EasyHook 但似乎这个问题仍然可行。VB6似乎以防止挂钩的方式加载COM对象。我看不到使用EasyHook挂钩类/接口或类的构造函数的方法。

你所提出的做法是可行的,但却存在风险。这将会影响到任何应用程序中的控制器。我不建议除非你在试图攻击某个系统,但我也不建议这样做。 :) - csharptest.net
1
我们正在尝试模拟一个硬件设备,以便与传统的销售点应用程序进行互操作。在全局范围内替换此ocx不会有任何问题。 - jonathanpeppers
可能是重复的问题:C# - 编写COM服务器 - 属性映射到方法 - jonathanpeppers
4个回答

3
您可以使用ActiveX Import AxImp导入OCX,创建包装类,然后调用它。该程序在此处描述:http://msdn.microsoft.com/en-us/library/8ccdh774(VS.80).aspx。基本上,您需要在命令提示符下执行以下操作:
c:/>AxImp MyControl.ocx

结果是一个MyControl.dll和一个AxMyControl.dll。第一个可以像普通的.NET DLL一样在您的项目中使用(即没有图形用户界面),第二个可以像任何其他控件(如TextBoxLabel)一样在表单上绘制。

要使用它,打开Visual Studio,在您的项目上右键单击并选择添加引用。浏览到新创建的DLL并添加它。就这些。


TlBimp.exe可以在不输出ActiveX控件的情况下完成相同的操作。你明白我试图替换COM对象而不是调用它,对吧?我可以看到我可以在Reflector中打开生成的Interop.??.dll并将其导出为C#文件以实现方法。这是你的意思吗? - jonathanpeppers
只有在拥有源代码的情况下,才能完全替换二进制COM对象(即不使用包装器):将其转换为.NET。 .NET架构和COM非常不同。您可以使用.NET创建COM,并且可以将COM导入.NET。但是,COM二进制本身在任何方面都不符合CLR标准,因此无法在不重写的情况下将其转换为.NET。 - Abel
关于“有没有办法编写一个 C# 库来精确复制 ocx 文件”的问题,答案是:是的,但只能进行完全重写(使用 ComVisible 等)。可能可以将 OCX 和包装器添加为资源,并通过注册到 AssemblyLoad 事件来转移 AppDomain 的程序集加载过程。但这会变得非常复杂,并需要深入了解 Domain、程序集加载和可能的动态 OCX 包装。 - Abel
现有的应用程序是VB6,除了在我们的类上使用ComVisible、Guid、ProgId和使用RegAsm注册我们的程序集(当然我们会注销原来的)之外,我们还需要做些什么吗?我们还没有让这个工作起来。 - jonathanpeppers
有一些帖子讨论了整个过程。这里是其中之一:http://foxsys.blogspot.com/2007/03/activex-controls-in-vs2005-and-c-part-1.html 你是否已经完成了所有这些步骤? - Abel
我们唯一没有做的事情就是给程序集添加强名称并将其放入全局程序集缓存中。当VB6应用程序加载时,我们仍然会收到一个通用的弹出窗口:“OurOcx.oxc未注册”。 - jonathanpeppers

2

1

0

这并不是完整的答案,但我认为这可能会有用。如果在要替换的对象的CLSID下添加一个名为“TreatAs”的键,并将默认值设置为要创建的对象的CLSID,这将指示COM运行时创建您的对象而不是原始对象。因此,您无需强制新的替换对象具有与旧对象相同的CLSID和ProgID。

例如,如果您的原始对象的ProgID为“MyComLibrary.Object”,CLSID为“{ABC}”,而您的新对象的ProgID为“MyDotNet.Object”,CLSID为“{123}”,则在HKLM / CLSID / {ABC}下添加一个名为TreatAs的键,并将默认值设置为{123}。然后,任何对“MyComLibrary.Object”或“MyDotNet.Object”的请求都将获得新对象的副本(假设它们实现了相同的接口)。


在我们的最终解决方案中,我们实现了一个C# COM对象,该对象是二进制兼容的,并使用Reg-Free COM为目标应用程序设置了它。因此,我们的目标应用程序通过COM加载了我们的程序集,认为它仍在与原始的COM对象通信。 - jonathanpeppers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接