系统的动态铸造__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

哪一个只有在运行时才知道?

这至少是问题的一部分。只有在编译时知道类型时,才能使用强制转换。你不知道,所以你不能写演员表。但更深层的是,您在运行时也不知道类型。__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