尝试释放正在使用的RCW,活动线程或其他线程上正在使用该RCW


  尝试释放正在使用的RCW,活动线程或其他线程上正在使用该RCW

尝试释放正在使用的 RCW。活动线程或其他线程上正在使用该 RCW。释放正在使用的 RCW 的尝试会导致损坏或数据丢失。




贴图图片



症状
在使用 ReleaseComObject 或类似方法释放 RCW 期间或之后发生访问冲突或内存损坏。

 原因
正在另一个线程或释放线程堆栈上使用 RCW。无法释放正在使用的 RCW。

 解决办法
不要释放当前或其他线程中可能正在使用的 RCW。

 对运行库的影响
此 MDA 对 CLR 无任何影响。

 输出
一条描述此错误的消息
 
参考代码:
private void OnFrameChanged(object sender, EventArgs e)
{
    if (this.InvokeRequired)
    {
        this.BeginInvoke(new EventHandler(this.OnFrameChanged), new object[] { sender, e });
    }
    else
    {
        this.Invalidate();
    }
}



什么是System.__ComObject: 强类型RCW和弱类型RCW?


大家在进行COM Interop编程的时候,不知道]是否会见到这样的情况。通常,我们通过TlbImp.exe把一个类型库(Type Library)转换成Interop Assembly。比如在Type Library里面有一个coclass叫做MyComObject,那么在Interop Assembly中也存在一个MyComObjectClass这样一个托管类型。用户可以直接使用这个MyComObjectClass操作MyComObject这样一个COM对象,比如使用new创建,调用方法,等等。因为MyComObjectClass并不是MyComObject这个COM对象本身,而是像一个代理(Proxy),.net中我们将其称为RCW (Runtime Callable Wrapper)。但是在有些情况下,在使用某些函数的时候,理论上应该返回一个MyComObjectClass,然而实际返回的却是一个System.__ComObject类型,这是什么原因呢?

事实上,RCW是存在两种类型的,一种是强类型(Strongly Typed)的RCW,另外一种是弱类型的RCW,即System.__ComObject。

所谓强类型的RCW,也就是说这种RCW具有关于这个COM对象实现了那些接口(Interface),有哪些方法等等各种信息,这些信息都在元数据(Metadata)里面,因此你一旦看到这个RCW,就知道这个RCW是何种COM对象。通常情况下,大家遇到的都是强类型的RCW,这主要是因为TlbImp对强类型的RCW提供了良好的支持。比如上面所提到的MyComObjectClass便是TlbImp生成的一个强类型RCW。对COM编程有基本了解的朋友应该非常清楚,对于COM对象而言,你在操作COM对象的时候(除了调用CoCreateInstanc创建的时候),大部分时候都是不清楚你在和具体哪个COM对象打交道,唯一的信息只是接口。那么.NET/CLR又是如何做到这一点的呢?当然了,我们无需考虑从.NET代码中直接创建强类型RCW的情形,以及从一个托管方法返回一个强类型RCW的情形,因为这两种情况没有歧义,很显然得到的结果均是一个强类型的RCW。我们主要考虑的是,在非托管方法中返回一个接口,CLR是如何知道这个接口是何种对象的。这种情况是最复杂的。

还是举一个例子:假设MyComObject对象实现了ITest接口。然后托管代码调用某个非托管函数(可以是COM对象/接口的函数,也可以是P/Invoke)的原型如下:

ITest *GetTest();

我们首先分析一下,如果用TlbImp来Import这个函数,对应的托管函数的原型是什么。有两种情况:

1. ITest是MyComObject的缺省的接口(default interface),并且没有其他coclass把ITest作为缺省的接口使用。这种情况下,TlbImp会将ITest转变为MyComObject接口。注意这个接口是由TlbImp生成的一个接口,而非type library本身里面所具有的,当然更不是这个MyComObject对象(MyComObject对象在TlbImp所生成的Interop Assembly中对应的类型是MyComObjectClass)。这个接口可以认为代表了这个COM对象本身,并且TlbImp会用CoClassAttribute这个属性把两者关联起来。因此最后的结果是:

MyComObject *GetTest();

2. 否则,TlbImp不做任何改变,直接使用原有的函数原型:

ITest *GetTest();

情况#1其实是最简单的情况。原因是,MyComObject接口因为上面标有CoClassAttribute这样一个属性,CLR在做数据转换(Marshalling/Unmarshalling)的时候,知道这个接口是一个coclass interface,也就是一个直接对应一个COM对象的接口,并且可以轻松通过CoClassAttribute找到对应的MyComObjectClass这个强类型RCW。因此,CLR可以很容易根据这个信息,创建出一个新的强类型RCW的实例(当然也可能发现这个接口的值符合一个已有的RCW,并且直接返回之),也就是MyComObjectClass的实例。

情况#2下,ITest所对应的对象可能有好几种不同情况:

1. MyComObject这个COM对象

2. 另外的非托管COM对象,实现了ITest接口

3. 托管的CCW,实现了ITest接口

CLR是如何对这几种情况作出区分的呢?关键在于IManagedObject接口和IProvideClassInfo接口。

先谈IManagedObject接口。这个接口是.net定义的,一旦某个对象实现了IManagedObject接口,说明此对象是托管对象,这也正是这个接口命名的由来。除此之外,IManagedObject在.NET Remoting中也起到了相当重要的作用,用于在服务器端获得序列化的缓冲区,然后在客户端反序列化得到原始对象的拷贝或者Proxy,因为与本文关系不大,因此这里从略。CLR在将非托管的接口指针转换成托管对象的时候,首先要做的就是做一个QueryInterface(IID_IManagedObject)调用,检查该COM对象是否是一个托管对象,如果是,则直接通过IManagedObject接口定义的GetObjectIdentity函数直接获得CCW的指针,返回之(这个CCW并非是原始托管对象,而是CLR的内部实现细节,要得到原始的托管对象,还需要做一系列的操作,这里从略)。顺便说一句,托管对象的CCW缺省实现了IManagedObject,并提供了IManagedObject接口中的函数实现,这个实现是所有CCW都共享的。同样的还有许多常用接口,如IUnknown,IMarshal,IConnectionPointContainer等等。

反之,如果这个COM对象没有实现IManagedObject接口,说明COM对象是非托管对象。这种情况下,我们必须要用到IProvideClassInfo接口。IProvideClassInfo,正如其名,是用来返回该COM对象所对应的信息的。IProvideClassInfo只有一个函数GetClassInfo,返回一个ITypeInfo指针。ITypeInfo也是一个COM接口,简单来说就是提供了COM对象的类型信息,类似.NET中的Type对象(但并不是Type对象)。通过ITypeInfo,可以拿到COM对象的CLSID,CLR然后根据这个CLSID来获得对应的托管类型。CLR会查找当前AppDomain中有那个类型是对应这个CLSID。如果没有找到,则回到注册表去查找CLSID所对应的COM注册表项(其实Manifest也可以,这是ReGIStration-Free Com Interop)。一旦CLSID所对应的注册表中指定了对应托管类型和Assembly的名称,CLR便可以通过这个信息加载Assembly并找到对应的类型。这也正是Type.GetTypeFromCLSID所作的事情。一旦这个步骤成功,CLR便知道了这个接口所对应的COM对象的对应托管对象,从而可以通过这个接口指针创建一个强类型RCW。

当然了,这个步骤需要COM对象本身实现IProvideClassInfo接口。因此,当在设计一个非托管COM组件并希望这个COM组件能够比较容易的被.net使用的话,推荐让这个非托管COM对象实现IProvideClassInfo接口。反之,如果这个COM对象没有实现这个接口(事实上很少COM对象实现这个接口),或者虽然COM对象实现了IProvideClassInfo接口,但是CLR无法通过CLSID成功找到对应的托管类型,CLR便别无选择,只能返回一个System.__ComObject作为弱类型的RCW。

本质上来讲,弱类型的RCW和强类型的RCW并没有太大的区别,只是强类型的RCW具有更多类型信息,比较容易调试。而在实际的编程中,使用方法是完全一致的,只是System.__ComObject必须要先转换到相应的接口,再调用。另外一个非常有趣的事情是,强类型RCW事实上是继承自System.__ComObject的,虽然从Metadata上面是无法看出来,但是在CLR内部这个继承关系确实存在。从本质上来讲,弱类型的RCW更接近非托管的COM编程方法(比如C++),因为用C++进行COM编程的时候,通常都是直接和接口打交道。而并不知道COM对象是什么。对于一个COM对象而言,使用接口来进行操作也是最符合COM本质的做法,我们也不希望在.NET中鼓励大家针对COM对象编程而不是针对COM接口编程。因此,CLR在将来有可能会渐渐减少对强类型RCW的支持,而转而建议使用弱类型RCW。当然,目前来讲由于工具的支持(主要是TlbImp),大部分情况下还是以强类型RCW为主。一旦大家遇到了弱类型的RCW,也无需惊慌,因为使用方法和强类型RCW并无太大区别。如果希望程序中不要出现弱类型的RCW,那么最好的方法还是使这个COM对象实现IProvideClassInfo接口。

作者:    张羿(ATField)

版权声明:本文为开发框架文库发布内容,转载请附上原文出处连接
C/S框架网
上一篇:DAL层重写Update方法手工启动事务
下一篇:解决VSS预设是administrator登录的问题
评论列表

发表评论

评论内容
昵称:
关联文章

尝试释放使用RCW活动线其他线使用RCW
C#异步编程(多线
C#.Net使用线池(ThreadPool)与专用线(Thread)
C#多线使用读写锁ReaderWriterLockSlim同步写入文件
C# 多线入门 - 开发实例
编辑网站绑定SSL证书提示:至少一个其他网站使用同一个https绑定
.NET5 HttpClient多线并发请求阻塞“发生一个多个错误”解决方案
C#.Net前台线与后台线区别
C#多线处理多个队列数据(交叉线访问及Invoke方法使用)
FastReport.Net报错-OS加载器锁定内尝试Managed执行(解决)
由于内部错误,服务器无法处理请求。有关错误详细信息,请打开服务器 IncludeExceptionDetailInFaults
[原创]C#键盘勾子(Hook),屏蔽键盘活动.(源代码下载)
C#异步操作等待窗体,异步多线处理数据通用界面(frmThreadOperating)
C#多线异步处理数据通用界面窗体(frmThreadOperating)
C#.Net COM交操作性 - 强类型RCW和弱类型CCW详解
C#使用Multipart form-data方式传文件及提交其他数据
C#使用则表达式替换去掉半角标点符号
CSFrameworkV6 - 软件用户数授权 - 通过控制线用户数实现逻辑
未能进入中断模式,源文件不属于调试项目
只能具有 Unchanged DataRowState DataRows 调用 SetAdded 和 SetModified

热门标签
.NET5 .NET6 .NET7 APP Auth-软件授权注册系统 Axios B/S B/S开发框架 Bug Bug记录 C#加密解密 C#源码 C/S CHATGPT CMS系统 CodeGenerator CSFramework.DB CSFramework.EF CSFrameworkV1学习版 CSFrameworkV2标准版 CSFrameworkV3高级版 CSFrameworkV4企业版 CSFrameworkV5旗舰版 CSFrameworkV6.0 DAL数据访问层 Database datalock DbFramework Demo教学 Demo下载 DevExpress教程 DOM EF框架 Element-UI EntityFramework ERP ES6 Excel FastReport GIT HR IDatabase IIS JavaScript LINQ MES MiniFramework MIS NavBarControl Node.JS NPM OMS ORM PaaS POS Promise API Redis SAP SEO SQL SQLConnector TMS系统 Token令牌 VS2022 VSCode VUE WCF WebApi WebApi NETCore WebApi框架 WEB开发框架 Windows服务 Winform 开发框架 Winform 开发平台 WinFramework Workflow工作流 Workflow流程引擎 版本区别 报表 踩坑日记 操作手册 代码生成器 迭代开发记录 基础资料窗体 架构设计 角色权限 开发sce 开发技巧 开发教程 开发框架 开发平台 开发指南 客户案例 快速搭站系统 快速开发平台 秘钥 密钥 权限设计 软件报价 软件测试报告 软件简介 软件开发框架 软件开发平台 软件开发文档 软件体系架构 软件下载 软著证书 三层架构 设计模式 生成代码 实用小技巧 收钱音箱 数据锁 数据同步 微信小程序 未解决问题 文档下载 喜鹊ERP 喜鹊软件 系统对接 详细设计说明书 行政区域数据库 需求分析 疑难杂症 蝇量级框架 蝇量框架 用户管理 用户开发手册 用户控件 在线支付 纸箱ERP 智能语音收款机 自定义窗体 自定义组件 自动升级程序