为什么这会引发 InvalidClassCast 异常?
Why would this raise an InvalidClassCast exception?
出于某些原因,我必须为我的商店提供的许多 COM 组件提供手动编写的运行时可调用包装器。
这是组件 A 的接口定义:
[ComImport, Guid("02922621-2EAE-4442-8A0A-C1C3CD886027")]
public interface IProdistLogging
{
[DispId(1000)]
IProdistLoggingHierarchy CreateHierarchy ([MarshalAs(UnmanagedType.BStr)] string type, object configuration);
}
这是组件 B 的接口定义:
[ComImport, Guid("8D841E5C-F25B-4C12-B03A-70A899B3A32E")]
public interface ISts
{
[DispId(1001)]
IProdistLoggingHierarchy Logging { get; set; }
[DispId(1000)]
IStsSession CreateSession ();
}
这是组件 C 的接口定义:
[ComImport, Guid("13385FC6-2618-4830-A3A9-703398AA5A0B")]
public interface IStsRsfn
{
[DispId(1000)]
ISts Sts { get; set; }
[DispId(1010)]
IStsRsfnSession CreateSession();
}
现在,以下测试程序以 InvalidCastException 终止:
public static void Main (string[] args)
{
IProdistLogging logging = (IProdistLogging)System.Activator.CreateInstance(Type.GetTypeFromProgID("prodist.logging.Logging.5.4"));
IProdistLoggingHierarchy loggingHierarchy = logging.CreateHierarchy("log4cxx", null);
ISts sts = (ISts)System.Activator.CreateInstance(Type.GetTypeFromProgID("prodist.sts.Sts.5.4"));
sts.Logging = loggingHierarchy;
IStsRsfn rsfn = (IStsRsfn)System.Activator.CreateInstance(Type.GetTypeFromProgID("prodist.sts.rsfn.StsRsfn.5.4"));
// The following statement raises an InvalidCastException
// with message "Specified cast is not valid"
rsfn.Sts = sts;
IStsRsfnSession session = rsfn.CreateSession();
return;
}
为什么会这样?
编辑 1:这是异常对象上 toString() 的结果:
System.InvalidCastException: Specified cast is not valid.
at prodist.sts.rsfn.IStsRsfn.set_Sts(ISts value)
at sandbox.Program.Main(String[] args) in (...)
我们已经发现了解释上述症状的缺失信息:接口的实际 IDL 定义。
有问题的 .NET 包装器没有包含相应接口的所有方法,因此 IDL 和 .NET 中 Sts put 属性 的序号索引不相同——即使 DispId 值是正确的。
在我们更新 .NET 包装器以完全反映 IDL 定义中的所有方法之后——这样每个方法都与另一个方法处于相同的序号索引——一切正常。
程序员假设方法是 "offset" 根据 DispId 属性的值,但似乎它们是 "offset" 根据它们在方法列表中从头到尾的实际位置。这意味着为接口提供部分包装器是不合法的——省略某些方法的包装器。
出于某些原因,我必须为我的商店提供的许多 COM 组件提供手动编写的运行时可调用包装器。
这是组件 A 的接口定义:
[ComImport, Guid("02922621-2EAE-4442-8A0A-C1C3CD886027")]
public interface IProdistLogging
{
[DispId(1000)]
IProdistLoggingHierarchy CreateHierarchy ([MarshalAs(UnmanagedType.BStr)] string type, object configuration);
}
这是组件 B 的接口定义:
[ComImport, Guid("8D841E5C-F25B-4C12-B03A-70A899B3A32E")]
public interface ISts
{
[DispId(1001)]
IProdistLoggingHierarchy Logging { get; set; }
[DispId(1000)]
IStsSession CreateSession ();
}
这是组件 C 的接口定义:
[ComImport, Guid("13385FC6-2618-4830-A3A9-703398AA5A0B")]
public interface IStsRsfn
{
[DispId(1000)]
ISts Sts { get; set; }
[DispId(1010)]
IStsRsfnSession CreateSession();
}
现在,以下测试程序以 InvalidCastException 终止:
public static void Main (string[] args)
{
IProdistLogging logging = (IProdistLogging)System.Activator.CreateInstance(Type.GetTypeFromProgID("prodist.logging.Logging.5.4"));
IProdistLoggingHierarchy loggingHierarchy = logging.CreateHierarchy("log4cxx", null);
ISts sts = (ISts)System.Activator.CreateInstance(Type.GetTypeFromProgID("prodist.sts.Sts.5.4"));
sts.Logging = loggingHierarchy;
IStsRsfn rsfn = (IStsRsfn)System.Activator.CreateInstance(Type.GetTypeFromProgID("prodist.sts.rsfn.StsRsfn.5.4"));
// The following statement raises an InvalidCastException
// with message "Specified cast is not valid"
rsfn.Sts = sts;
IStsRsfnSession session = rsfn.CreateSession();
return;
}
为什么会这样?
编辑 1:这是异常对象上 toString() 的结果:
System.InvalidCastException: Specified cast is not valid.
at prodist.sts.rsfn.IStsRsfn.set_Sts(ISts value)
at sandbox.Program.Main(String[] args) in (...)
我们已经发现了解释上述症状的缺失信息:接口的实际 IDL 定义。
有问题的 .NET 包装器没有包含相应接口的所有方法,因此 IDL 和 .NET 中 Sts put 属性 的序号索引不相同——即使 DispId 值是正确的。
在我们更新 .NET 包装器以完全反映 IDL 定义中的所有方法之后——这样每个方法都与另一个方法处于相同的序号索引——一切正常。
程序员假设方法是 "offset" 根据 DispId 属性的值,但似乎它们是 "offset" 根据它们在方法列表中从头到尾的实际位置。这意味着为接口提供部分包装器是不合法的——省略某些方法的包装器。