系统的动态铸造__ComObject
本文关键字:ComObject 动态 系统 | 更新日期: 2023-09-27 18:25:38
是否可以强制转换System__ComObject转换为某种类型,而这种类型只有在运行时才知道?我有以下代码
Type ComClassType = SomeDLLAssembly.GetType("ClassName");
dynamic comClassInstance = icf2.CreateInstanceLic(null, null, ComClassType.GUID, "License string");
//This will throw exception, because comClassInstance type is __ComObject and it does not contains ComClassMethod
comClassInstance.ComClassMethod();
当我使用下面的代码时,它工作得很好,但不幸的是,我不能在代码中使用InvokeMember,因为它会非常复杂。
ComClassType.InvokeMember("ComClassMethod", BindingFlags.InvokeMethod, null, comClassInstance, null);
所以我想问,是否可以将"comClassInstance"强制转换为"ComClassType",以便能够以这种方式调用方法comClassInstance.ComClassMethod();
哪一个只有在运行时才知道?
这至少是问题的一部分。只有在编译时知道类型时,才能使用强制转换。你不知道,所以你不能写演员表。但更深层的是,您在运行时也不知道类型。__ComObject
是RCW的底层包装器,它存储IDispatch接口指针。它没有说明您可以调用什么方法以及可以使用什么属性。
这是一种较低级别的键入,它是动态键入。这方面的通用术语是后期绑定。这是一种尝试调用它,并查看与另一个程序员编写的代码接口的方式会发生什么。C#团队抵制了很长一段时间,静态类型是该语言的核心,迫使程序员使用反射。但是,根据流行的需求,他们在v4中添加了dynamic关键字,使C#与一直支持它的Visual Basic具有同等地位。现在,您可以使用符号来代替将字符串传递给Reflection方法,DLR会自动使用反射来进行调用。否则,同样的编程工作,你必须知道字符串。
后期绑定的主要优点是它对版本控制有弹性,这也是第三方api喜欢它的基本原因。但它的主要缺点是,如果你没有一个好的手册或版本更改太多,那么你会得到一个讨厌的运行时异常,它只会告诉你"它不起作用"。编译器无法帮助您正确处理,因为它不知道类型。IntelliSense无法帮助您,它无法提供任何自动完成功能。只是运行时的一次爆炸,找出问题所在的唯一方法是仔细阅读(缺失的)手册或与程序员交谈。
许多COM组件同时支持早期绑定和后期绑定。您需要一个类型库来使用早期绑定,它是组件支持的接口和组件类的机器可读描述。它与.NET程序集中的元数据完全等效。并执行相同的角色,使用类型库,编译器现在可以检查代码,IntelliSense可以提供自动完成功能。
类型库通常嵌入在可执行文件(.dll或.exe)中,有时它作为单独的文件提供,.tlb和.olb是常见的扩展名。您可以使用Visual Studio的"文件">"打开">"文件"查看可执行文件的内部,如果嵌入了类型库,则会看到TYPELIB节点。然后,您可以使用OleView.exe,文件>查看Typelib命令查看其内容。并运行Tlbimp.exe生成互操作库。
如果你找不到类型库,也没有一本像样的最新编程手册,那么只有电话可以帮助你。请致电组件的所有者或作者以获得帮助。
调用COM对象的方法不需要强制转换。
C#动态可以用作(调用前不需要编写强制转换表达式)
dynamic comClassInstance = Activator.CreateInstance(Type.GetTypeFromProgID("ClassName"));
comClassInstance.ComClassMethod();
var result = comClassInstance.ComClassFMethod(param);
或
在vb.net中创建一个库项目(Option strict应为Off,project properties)。在vb.net中完成所有与COM相关的工作,并在C#项目中添加对它的引用。
将实例类型声明为Object
。
对COM组件执行CreateObject
调用,并将方法调用为comClassInstance.ComClassMethod()
Public Class Abc
Private _comClassInstance As Object
Public Sub New()
_comClassInstance = CreateObject("ClassName")
End Sub
Public Sub ComClassMethod()
_comClassInstance.ComClassMethod()
End Sub
Public Function ComClassFMethod(param As String) As Integer
Return _comClassInstance.ComClassFMethod(param)
End Function
End Class
如果您的"ClassName"是COM可见的,您应该能够使用基本类型转换来转换返回的COM对象。
Type ComClassType = SomeDLLAssembly.GetType("ClassName");
ClassName myInstance = (ClassName)icf2.CreateInstanceLic(null, null, ComClassType.GUID, "License string");
myInstance.ComClassMethod();
我不完全确定这就是你想要的,如果是这样,你需要将其适应创建实例的方式,因为我并不真正了解icf2的来源。但是,如果您只是想清理大多数代码与COM对象交互的方式,那么一种方法是扩展dynamic
解析过程。您可以通过继承DynamicObject
并重写TryInvokeMember
方法来实现这一点(还有其他方法可以重写,但TryInvokeMember
似乎是关键方法)。当动态调度程序无法定位某个方法以便执行某些自定义解析时,会在对象上调用此方法。
因此,在基本级别上(就像我跳过的所有错误处理等伟大的例子一样),您可以编写这样的类:
public class ComWrapper : DynamicObject {
Object _instance;
Type _type;
// Create + save type/instance information in constructor
// You seem to do this differently, so you'd want to change this...
public ComWrapper(Guid guid) {
_type = Type.GetTypeFromCLSID(guid, true);
_instance = Activator.CreateInstance(_type, true);
}
// Invoke requested method, passing in args and assigning return value
// to result
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args,
out object result) {
result = _type.InvokeMember(binder.Name,
System.Reflection.BindingFlags.InvokeMethod, null,
_instance, args);
return true; // Return true to indicate it's been handled
}
}
然后,基于一个创建Word实例的示例,然后告诉它退出,您将使用上面的代码如下:
dynamic val = new ComWrapper(new Guid("{000209FF-0000-0000-C000-000000000046}"));
val.Quit(0, 0, false );
我认为动态类很有可能已经在引擎盖下做了类似于上面的事情,所以这可能不会真正解决您的问题。然而,它可能会让你更接近找到解决方案。例如,对不支持调度的COM对象调用方法将通过InvokeMethod
报告错误,但在通过动态实例调用时将报告Does not contain a definition
。