一个COM调用时出现的错误及解决办法

来源:互联网 发布:iphone免费解压缩软件 编辑:程序博客网 时间:2024/05/08 08:08
 

一个COM调用时出现的错误及解决办法


这两天一直在用COM里的复合文档来做个东东。我通过C#的平台调用方法使用IStorageIStream这些接口的,并写了一个流类来包装IStream。在“调试”模式下,运行得很好,但是在“Release”下,则报错了!

症状为:

在长时间读写一个复合文档时,开始是可以正常运行的,但是一段时间后,就会报错

说“试图使用一个不存在的对象异常来自HRESULT 0x800030100 STG_E_REVERTED ”

而且问题是在Debug模式下,根本不出现这个问题,问题只出现在Release模式下。


开始我以为是和COM平台调用时的问题,看遍了MSDNGoogle上能找到的相关资料,都没有提到类似的错误。没办法,我只能一段代码一段代码来查找原因。考虑到错误是引用了一个不存在COM对象,那么有可能是COM中对象计数器的问题,我想是不是什么的代码导致释放了引用COM对象的变量。

整整花了近六个小时,我才发现我犯了一个基本错误。我在包装类里写一个析构函数

~StorageStream()

{

this.Dispose();

}

 

而我的主程序源代码比较长,不贴出来了,大概如下面代码

1 Storage storage = Storage.CreateStorageFile(...);

2 StorageStream streamA = storage.CreateStream(...);

3 StorageStream streamB = storage.CreateStream(...);

4 for(int i =0;i < VeryLargeNum;i++)

5 {

6 streamA.Dosomething();

7 streamB.Dosomething();

8 }

9 streamA.Close();

10 streamB.Close(); // 这一句没写

11 storage.Close();

其中第8句没有写上去,编译时很正常,在“Debug”模式下也很正常,因为GC没有对streamB进行回收,而“Release”模式下,GC工作了,把streamB回收了,因此就导致引用了不存在的对象。

找到问题就好办了,加上Close(); 删掉析构函数,并在Dispose函数里加上一句

GC.KeepAlive(_stream);

一切搞定。


总结: GC的垃圾收集模式在“Debug”和“Release”模式下是不同的,因此常常可以看到很多人发贴子说“为什么我调试下可以运行,发布状态下则报错”,这个十有八九是GC搞的鬼。另外GC在判断是否可以收集一个对象时,它是根据后面还有没有代码引用这个对象,如果有的话它就不会收集,否则它会将其进行收集。为什么要加一个GC.KeepAlive是因为GC对于COM对象等非托管对象是不知情的,因此如果你将对象暴露给COM使用,就需要用到它。


 





 
原创粉丝点击