最近在开发中发现一个奇怪的现象,百思不得其解。程序是用 VS 2005 写的,其中使用了 ParallelGraphics 公司的 Cortona VRML Viewer 控件,以提供 3D 场景浏览的功能。其中,我希望当场景里发现某些事件的时候,程序能够作出一定的反应,为此,需要实现一个事件回调。相关的代码是这样的:
2{
3 IVRMLEventCallback Members
11}
这里的 IVRMLEventCallback 是在类型库里引入的,其原型定义如下:
2 uuid(6BEF46D1-FD35-11d2-812F-00A0C94C695A),
3 helpstring("IVRMLEventCallback Interface"),
4 pointer_default(unique)
5]
6interface IVRMLEventCallback : IUnknown
7{
8 HRESULT OnEvent([in] LPFIELDOBJECT Value,
9 [in] VARIANT* Hint,
10 [in] double TimeStamp);
11};
这是引入类型库后反射得到的 metadata:
2[Guid("6BEF46D1-FD35-11D2-812F-00A0C94C695A")]
3public interface IVRMLEventCallback
4{
5 void OnEvent(VRMLField value, ref object Hint, double TimeStamp);
6}
继续往下,实现了回调接口后,需要把该 Callback 向一个 VRMLField 注册,相关代码如下:
2{
3 try
4 {
5 VRMLField field;
6 TestSyncCallback callback;
7 object hint = "hint";
8 // 相关设置略
9 field.Advise(callback, ref hint);
10 }
11 catch (COMException e)
12 {
13 MessageBox.Show(e.ToString());
14 }
15 catch (Exception e)
16 {
17 MessageBox.Show(e.ToString());
18 }
19}
这里,第 9 行的 Advise 函数的原型定义是:
2 [in] VARIANT* Hint);
引入后反射为:
关于这个函数的正确使用方法,ParallelGraphics 公司提供的 C++ Demo 里是这样使用的:
2pField->Advise(reinterpret_cast<LPDISPATCH>(pVrmlEventCallback), &Hint);
这个函数的用法,Listener 参数是一个实现了 IVRMLEventCallback 接口的对象,在本例中将 TestSyncCallback 的一个实例传递进去。这个用法是绝对正确的。编译通过也完全没有问题,但是这段代码在运行的时候,Advise 函数执行会抛出一个异常:Specified cast is not valid。
请注意,这个异常不是一个 COMException,而是一个 InvalidCastException。那么到底是哪里转换失败了?奇怪之余,我自己写了一个 IVRMLEventCallback 的 C# Wrapper,结果仍然不行。再然后改用 VB.NET,使用 Wizard 自动产生了接口函数,然而这个 Specified cast is not valid 依然如故。看来不是 C# 的问题,而是 .net 2.0 或是 VS2005 的问题了。
实在通不过运行。无奈之下用 VS 2003 一试,结果同样的代码在 VS 2003 里一点错误都没有,运行十分正常。
再然后,将在 VS 2003 下执行通过的工程 Upgrade 到 VS 2005,意外地发现,居然一切 OK 了!即使是 Rebuild 之后,也仍然是可以运行的!可是我调出原先无法通过的工程,一试之下却仍然不行!仔细核对两个项目之间的差别,从 Properties 到 References,再使用 ILDASM 检查生成的运行代码,实在是无法看出任何的差异!
极其奇怪。是不是 .net 2.0 或是 VS 2005 在与 COM 交互的局部细节作了一些调整?可是就算是调整也应当有一些提示什么的啊。难不成,这是一个 Bug?
Such a strange exception。
(暂发首页,希望有解决过或是遇过类似问题的朋友指点)