C# COM服务端与客户端转换触发InvalidCastException如何解决?
你遇到的这个InvalidCastException,本质是客户端自定义的ITestObject接口和服务端注册的COM接口元数据不兼容,而dynamic能正常调用是因为它走了COM的后期绑定逻辑。下面给你详细拆解原因和解决办法:
问题根源
服务端的ITestObject标记的是InterfaceType(ComInterfaceType.InterfaceIsDual)(双接口,同时支持IUnknown和IDispatch),但客户端的接口却用了InterfaceType(ComInterfaceType.InterfaceIsIUnknown),再加上额外的[PreserveSig]属性(虽然对void方法影响不大,但属于不必要的差异),导致CLR在尝试将COM对象转换为静态接口类型时,无法匹配到正确的COM接口契约,最终抛出转换异常。
而dynamic之所以能工作,是因为它会绕过静态类型检查,直接通过服务端双接口支持的IDispatch进行后期绑定调用,不需要提前匹配接口的静态定义。
解决方案
方案1:对齐客户端接口定义与服务端完全一致
把客户端的ITestObject接口修改成和服务端完全匹配的样子,确保GUID、InterfaceType、方法签名都一致:
[ComImport, Guid("565D8202-6C0F-4AAB-B0F6-49719CD13045"), InterfaceType(ComInterfaceType.InterfaceIsDual)] public interface ITestObject { void DoSomething(); }
修改后,客户端接口的元数据和服务端注册的COM接口完全对齐,强制转换(ITestObject)o就能正常执行了。
方案2:直接引用服务端程序集(如果可行)
如果你的客户端项目可以直接访问服务端的程序集,那完全不需要自己重新定义接口,直接引用服务端程序集后,用服务端原本的ITestObject接口进行转换:
// 先添加服务端程序集的引用 Type type = Type.GetTypeFromCLSID(new Guid("21293767-A713-49E2-968E-7DDE0B0DAB94")); object o = Activator.CreateInstance(type); ITestObject t = (ITestObject)o; // 使用服务端的ITestObject接口
这种方式最稳妥,完全避免了手动定义接口可能出现的元数据不一致问题。
额外提示
如果因为某些原因必须使用InterfaceIsIUnknown,那你需要确保服务端的接口也改成InterfaceIsIUnknown,并且客户端的方法签名(包括参数顺序、类型)和服务端完全一致,不过这种情况不如直接对齐双接口来得省心。
内容的提问来源于stack exchange,提问作者Thomas

