.NET进阶篇07-.NET和COM
知识需要不断积累、总结和沉淀,思考和写作是成长的催化剂
内容目录
一、COM和.NET元数据内存管理接口注册线程编组二、.NET客户端调用COM组件三、COM客户端调用.NET组件四、嵌入互操作类型五、平台调用DllImport六、等等
一、COM和.NET
COM组件对象模型是在.NET之前的一种编程规范,它允许不同的语言之间可以互相操作。由于COM规范比较复杂,注册表,内存对象管理,错误处理机制都和.NET不同,.NET做为其后秀,应用起来更简单,但一般不会因为新技术可用就重写已有的代码,所以就引来COM的互操作性
我们可能不必编写COM组件,但了解是有用的。经常会遇到嵌入互操作类型,为COM设置互操作问题
先看一下COM的一些基本概念,挑了几个重要的也是比较好理解的
元数据
COM的元数据信息存储在tlb类型库中,包含接口、方法和参数名称等,在.NET程序集中元数据都存储在程序集中的。
内存管理
我们知道.NET托管对象的内存释放都有垃圾回收器GC完成,不同于COM,COM依赖引用计数,
接口
COM三个基本接口,IClassFactory、IUnknown、Idispatch
IClassFactory,每个组件都有一个相关的类厂用于创建COM组件对象。非托管对象,客户端是无法直接New对象的,所以只能通过交给类厂来创建实例然后把实例的指针交给客户端
每个COM对象必须实现IUnknown接口,QueryInterface用于查询组件实现的其它接口,说白了也就是看看这个组件的父类中还有哪些接口类,AddRef()递增引用计数,Release()递减引用计数,为0后就销毁对象
IDispatch调度接口派生自IUnknown接口,在其基础上又增加了GetIDsOfNames()和Invoke(),调用接口会创建方法或属性对应的调用ID映射表,这样调用时先获取根据名字获取调度ID然后Invoke调用。因为并不是所有的语言(客户端)(像一些js脚本语言)都支持指针,也就不能通过虚函数表来调用,所以用调度接口增加函数ID映射。
注册
.NET中区分私有程序集和共享程序集。在COM中,通过注册表配置的所有组件都是全局可用的。所有COM对象都有一个唯一标识符CLSID类ID,创建COM对象时,COM API调用CoCreateInstacne()方法,在注册表中查找CLSID的dll或exe路径,然后加载,实例化组件
线程
COM使用单元模型,单元模型有单线程单元模型STA和多线程单元模型MTA
STA单线程单元模型,在Winfrom程序中经常看到Main入口函数上面标记STAThread特性。在STA中只允许创建实例的线程访问组件。一个进程中也可以包含多个STA
MTA多线程单元模型,在MTA中,多个线程可以同时访问组件
编组
.NET和COM之间的数据传递必须经过转换,这种机制就是编组(marshaling)。转换过程取决于数据类型。简单的数据类型如byte、short、int和long属性blittable类型,在com和net中是一样的表示方法,其他nonblittable类型的则需要进行转换,当然会有些开销
COM数据类型 | .Net数据类型 |
---|---|
SAFEARRAY | Array |
VARIANT | Object |
BSTR | String |
Iunknown,Idispatch | Object |
二、.NET客户端调用COM组件
由于COM对象和.NET对象在生命周期、内存管理、接口服务上的差异,运行时提供了包装类来使其互相调用。托管客户端调用 COM 对象方法时,运行时就会创建一个运行时可调用包装器 (RCW)
来封送引用机制之间的差异。 也会创建了一个 COM 可调用包装器 (CCW)
来逆转此过程
三、COM客户端调用.NET组件
没写过COM,也不是很了解,但一些约定规范必须遵守,原理和.NET客户端调用COM组件类似
比如在C#类库的AssemblyInfo.cs中修改
// 将 ComVisible 设置为 false 使此程序集中的类型
// 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
// 则将该类型上的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
在程序集属性中勾选COM互操作注册
然后任何一个需要暴露给COM客户端的都需要有接口
[ComVisible(true)]
[Guid("35A5CE1E-551C-41EC-81D4-005318550119")]
public interface IMyClass
{
void Initialize();
void Dispose();
int Add(int x, int y);
}
编译时候因为勾选的为COM互操作注册,所以需要以管理员运行的才能注册成功
四、嵌入互操作类型
引用PIA
(主互操作程序集,COM组件生成)时,可以设置是否嵌入互操作类型。嵌入互操作类型时(True)则PIA不随着程序一起部署,程序只是引用COM中的类型信息,这样的好处就是可以部署到不同COM版本的环境中。比如常用的Office开发Microsoft.Office.Interop.Excel,设置嵌入互操作类型,就可以不依赖office版本。改为互操作false后也就将PIA复制到本地
有时候会无法嵌入互操作类型请改为适当的接口,单纯一点就修改嵌入互操作设为false,OK编译通过。不太单纯的,可以修改创建对象的方式,像下面这样,直接实例化的普通类,无法嵌入互操作类型
Application excelApp = new ApplicationClass();
Application excelApp = new Application()
这样是可以的,Application虽然是一个接口,理论上应该不能实例化的,当它上面标记了
[CoClass(typeof (ApplicationClass))
告诉运行时CLR,当有人要创建类型为Application的实例时,它实际上应该继续创建ApplicationClass的实例。
用COM接口的可以嵌入,直接使用coclass的无法嵌入
五、平台调用DllImport
还有一些非托管库不包含COM对象,只包含倒出的函数,这时候需要使用平台调用服务(P-Invoke)
,CLR会加载包含所需调用函数的dll,并编组参数。在C++的非托管库中使用dllexport暴露函数,在C#中使用dllimport导入。基本语法如下
[DLLImport(“DLL文件”)]
修饰符 extern 返回变量类型 方法名称 (参数列表)
dllimport在命名空间System.Runtime.InteropServices下,该特性用于对照非托管库中导出的函数
[AttributeUsage(AttributeTargets.Method)]
public class DllImportAttribute: System.Attribute
{
public DllImportAttribute(string dllName) {…} //定位参数为dllName
public CallingConvention CallingConvention; //入口点调用约定
public CharSet CharSet; //入口点采用的字符接
public string EntryPoint; //入口点名称
public bool ExactSpelling; //是否必须与指示的入口点拼写完全一致,默认false
public bool PreserveSig; //方法的签名是被保留还是被转换
public bool SetLastError; //FindLastError方法的返回值保存在这里
public string Value { get {…} }
}
需要注意的就是数据类型的映射,必须映射到.NET数据类型上。可以使用P/Invoke Interop Assistant
工具,它支持托管代码和非托管代码之间的方法签名的转换,可以直接生成调用代码
一般会在一些特殊场合来调用win 32的api,比如像输入法程序设置永远不获取焦点,一些任务处理时不希望用户点击别的操作(当然窗体也不能崩了),这时候可以使用下面设置窗体控件不可用
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int wndproc);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
public const int GWL_STYLE = -16;
public const int WS_DISABLED = 0x8000000;
public static void SetControlEnabled(Control c, bool enabled)
{
if (enabled)
{
SetWindowLong(c.Handle, GWL_STYLE, (~WS_DISABLED) & GetWindowLong(c.Handle, GWL_STYLE));
}
else
{
SetWindowLong(c.Handle, GWL_STYLE, WS_DISABLED + GetWindowLong(c.Handle, GWL_STYLE));
}
}
六、等等
关于COM也只是知晓一二,平常主要写业务,COM用的不多,充其量就是调用。做底层嵌入式开发应该用的比较多,比如设备打印机驱动等。了解总没坏处,拜了个拜
.NET进阶篇07-.NET和COM的更多相关文章
- 2. web前端开发分享-css,js进阶篇
一,css进阶篇: 等css哪些事儿看了两三遍之后,需要对看过的知识综合应用,这时候需要大量的实践经验, 简单的想法:把qq首页全屏另存为jpg然后通过ps工具切图结合css转换成html,有无从下手 ...
- RMAN备份演练进阶篇
前篇介绍了通过rman进行各种备份,进阶篇则主要是rman的一些功能扩展和增加功能,利用前篇你已经完全可以完成数据库的备份,而通过本篇你可以更好更方便的完成数据库的备份. 一.建立增量备份 如果数据库 ...
- web前端开发分享-css,js进阶篇
一,css进阶篇: 等css哪些事儿看了两三遍之后,需要对看过的知识综合应用,这时候需要大量的实践 经验, 简单的想法:把qq首页全屏另存为jpg然后通过ps工具切图结合css转换成html,有无 从 ...
- 02:Django进阶篇
目录:Django其他篇 01:Django基础篇 02:Django进阶篇 03:Django数据库操作--->Model 04: Form 验证用户数据 & 生成html 05:Mo ...
- Sass-学习笔记【进阶篇】
特别说明: 没有sass基础请移步:[Sass-学习笔记[基础篇]]http://www.cnblogs.com/padding1015/articles/7056323.html 最底部附结构图(实 ...
- 【目录】sql server 进阶篇系列
随笔分类 - sql server 进阶篇系列 sql server 下载安装标记 摘要: SQL Server 2017 的各版本和支持的功能 https://docs.microsoft.com/ ...
- 【目录】mysql 进阶篇系列
随笔分类 - mysql 进阶篇系列 mysql 开发进阶篇系列 55 权限与安全(安全事项 ) 摘要: 一. 操作系统层面安全 对于数据库来说,安全很重要,本章将从操作系统和数据库两个层面对mysq ...
- Membership三步曲之进阶篇 - 深入剖析Provider Model
Membership 三步曲之进阶篇 - 深入剖析Provider Model 本文的目标是让每一个人都知道Provider Model 是什么,并且能灵活的在自己的项目中使用它. Membershi ...
- iOS系列 基础篇 07 Action动作和输出口
iOS系列 基础篇 07 Action动作和输出口 目录: 1. 前言及案例说明 2. 什么是动作? 3. 什么是输出口? 4. 实战 5. 结尾 1. 前言及案例说明 上篇内容我们学习了标签和按钮 ...
- idea 插件的使用 进阶篇
CSDN 2016博客之星评选结果公布 [系列直播]零基础学习微信小程序! "我的2016"主题征文活动 博客的神秘功能 idea 插件的使用 进阶篇(个人收集 ...
随机推荐
- nginx 做反向代理
1.Nginx的常用配置大家可以去搜一下,有很多优秀的博客,我这篇文章要实现的需求是: a.根据访问的域名不同,跳转到不同的项目(html首页,80端口) b.拦截访问中所有带有api的请求,转发到后 ...
- 2019-3-21-win10-uwp-修改图片质量压缩图片
title author date CreateTime categories win10 uwp 修改图片质量压缩图片 lindexi 2019-03-21 15:29:20 +0800 2019- ...
- [Java]ssh网上商城总结 标签: hibernatessh 2016-05-15 21:03 1099人阅读 评论(32)
前几日敲完了ssh网上商城,虽然现在已经敲完了整个系统,却发现自己对于ssh,了解的一点都不多,什么是struts2,什么是spring,什么是hibernate,自己都是稀里糊涂,然后看了一下后面的 ...
- UI2CODE智能生成代码——组件识别篇
1.背景 在<UI2CODE——整体设计篇>中,我们介绍了UI2CODE工程的整体流程: 在组件识别这个环节,需要有一种处理布局信息的方法,来解析和计算控件间的布局关系(比如识别业务组件( ...
- 重置Mysql自增列的开始序号
ALTER TABLE TableName AUTO_INCREMENT = 5; 代表重新从5开始(包括5)
- phpstorm鼠标显示问题
https://segmentfault.com/q/1010000004319802 使用phpstorm,不知道碰到了什么键,鼠标变成了一个字符那么宽的灰色色块,原来是一根很细的竖线,怎么弄?没法 ...
- H3C DCC拨号配置任务
- H3C 常见ISDN连接方式
- Codeforces Round #170 (Div. 1 + Div. 2)
A. Circle Line 考虑环上的最短距离. B. New Problem \(n\) 个串建后缀自动机. 找的时候bfs一下即可. C. Learning Languages 并查集维护可以沟 ...
- webpack学习(五)entry和output的基础配置
1:entry和output就是打包的入口和出口的两个对象.但是如果入口文件就一个的话(应该说只希望打包出一个脚本文件), entry直接跟一个字符串(入口文件路径)就可以了.如:entry : &q ...