.NET进阶篇07-.NET和COM

2023-11-08

知识需要不断积累、总结和沉淀,思考和写作是成长的催化剂

一、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用的不多,充其量就是调用。做底层嵌入式开发应该用的比较多,比如设备打印机驱动等。了解总没坏处,拜了个拜

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

.NET进阶篇07-.NET和COM 的相关文章

  • 索引数据表

    我有一个Datatable其中包含数百万数据 数据表 创建索引之前 x x DateColumn Name x x 2014 01 02 10 01 03 A 2014 01 02 11 07 23 A 2014 01 03 07 12 1
  • 在 Visual Studio 安装项目中安装后如何启动应用程序

    我已经使用 Visual Studio 2008 创建了一个安装项目 应用程序安装完成后 我希望它立即启动 关于如何做到这一点有什么想法吗 我使用脚本在 MSI 的最终形式上放置了 启动 ProductName 复选框 不过 我不能对剧本有
  • 在 Resharper 分析中忽略设计器和生成的文件

    我已经使用 Resharper 几天了 我真的很喜欢这个工具 但是有一件事让我很恼火 我想知道它是否可以改变 我从生成的代码中收到大量问题通知 我的项目中几乎有 1400 个 我想将这些文件设置为忽略 这样它们就不会像使用 StyleCop
  • 有没有办法找到 .NET 中嵌入资源的最后修改日期?

    有人知道这样做的方法 在运行时 吗 我不确定我能在描述中详细阐述比标题中已经给出的更多信息 但如果您觉得我错过了某些内容 请说出来 resx 文件本身应该有一个与之关联的修改日期 但您将无法获取该文件中各个资源的修改日期
  • 标签文本每秒刷新一次

    我试图每秒刷新一次标签 以便倒计时更新 但遇到了一些麻烦 我对 C 非常陌生 对于菜鸟问题 表示歉意 private void Form1 Load object sender EventArgs e bool ephCD true int
  • 使用 VS 2012 在构建中生成 T4

    我正在尝试使用 VS2012 在项目的每个构建上生成代码 我的解决方案中有 3 个项目 项目1有一些类 项目 2 有通用模板 项目 3 的模板读取 json 文件 然后调用项目 2 中的通用模板来生成其文件 当我单击 构建 转换所有 T4
  • nAnt 是否仍受支持并适用于 .net 3.5/VS2008?

    我正在使用 MSBuild 来构建我的东西 我想通过构建服务器使用 CruiseControl net 现在 CCNET 经常引用 nAnt 但看起来 ccnet 可以通过项目配置和 msbuild 完成 nant 可以完成的大部分工作 另
  • 椭圆曲线加密的 .NET 实现(库)

    您能否建议在 NET 平台上使用椭圆曲线加密技术的任何实现 另外 如果您使用过它们 您能告诉我应该使用的推荐曲线吗 EDIT 正如 FatCat 提到的 它的实现在 NET Framework 3 5 中可用 但仅在 Windows Vis
  • 关闭 Quartz .Net 中的调试日志记录

    我正在使用 Quartz NET 在我们的应用程序中安排一些自定义任务 一切工作正常 只是它在一秒钟内记录大约二十个调试条目 我不知道如何关闭调试日志记录 任何帮助将非常感激 因为我一直试图在网上查找但没有运气 调试条目如下所示 DEBUG
  • C# 是“??”吗?运算符线程安全吗?

    大家都知道这不是线程安全的 public StringBuilder Builder get if builder null builder new StringBuilder return builder 那这个呢 public Stri
  • 使用具有 ThreadStatic 属性的并行扩展。会不会泄漏内存?

    我相当频繁地使用并行扩展 而且我刚刚遇到了一种情况 使用线程本地存储可能是明智的 允许工作线程重用对象 因此 我正在查看 ThreadStatic 属性 该属性将静态字段 变量标记为每个线程具有唯一值 在我看来 在没有任何 PE 线程重用保
  • Web API 帮助页面显示每个方法的两个版本

    如何避免帮助页面显示我的方法的两个版本 正如你所看到的 我已经设置了一条自定义路线 api property search finnId 但我不希望使用查询参数的那个出现在 帮助 页面中 有办法解决这个问题吗 我正在使用 ASP NET F
  • IoC比较[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 开发 ASP NET Web 应用程
  • 变量声明后的一个问号是什么意思? [复制]

    这个问题在这里已经有答案了 在玩开源项目时 我尝试ToStringDateTime 对象被编译器阻止 当我跳到定义时 我看到了这个 public DateTime timestamp 有人可以告诉我这叫什么以及为什么它可能有用吗 这是一个可
  • 获取 SSRS 的报告列表?

    我刚刚开始使用 SSRS 到目前为止 我已经能够通过对报告路径进行硬编码 使用 ReportViewer 在我的 Winforms 应用程序中显示报告 我想从 SSRS 获取一份报告列表 以便我可以显示它们并让用户选择他们想要查看的报告 有
  • 当 DataContext 更改时立即更新 Binding

    我试图在更改 DataContext 后立即测量对象 但对象的绑定没有得到足够快的更新 这是我的代码 In MeasureOverride Size m inputWidth 0 0 Size elemSize new Size doubl
  • 您如何使用 ReSharper 5 中的模式匹配功能?

    Resharper 5 的新模式匹配看起来非常强大 尽管需要一些修改才能弄清楚如何使用它 对于那些不熟悉此功能的人 它允许您在代码中搜索特定模式 此类模式的实例可以选择性地替换为替代方案 在 IntelliJ 中 这称为结构搜索和替换 它比
  • 如何清除客户端.Net SSL会话缓存

    我正在编写一个小测试工具 它使用 HttpWebRequest 来负载测试服务器 我想要 每次我尝试调用 HttpWebRequest GetResponse 时 它都会建立一个新的 SSL 会话 而不是使用缓存中的会话 注意 我提供客户端
  • C# Visual Studio 动态代码片段

    我正在开发一个 WinForms 项目 每天都会执行一些重复性的任务 所以我认为创建代码片段 https msdn microsoft com en us library ms165394 v vs 110 aspx会帮助我 但它仅适用于固
  • VS2010中VSHost.exe不断启动

    我正在 VS2010 中使用一个包含大量项目的解决方案 但它不断变得无响应 我注意到的一件事可能是一条线索 尽管我尚未开始任何调试 但 MyApplicationName vshost exe 不断出现在进程列表中 也许每当构建发生时它就会

随机推荐

  • 如何在chrome浏览器调试JS代码

    文章目录 资源 Sources 面板 控制台 Console 断点 Breakpoints debugger 命令 暂停并查看 日志记录 总结 参考文献 在编写更复杂的代码前 让我们先来聊聊调试吧 调试是指在一个脚本中找出并修复错误的过程
  • 如何解决merge conflict的方法

    如何解决merge conflict的方法 首先在pull的时候加上rebase 解决conflict 最后push git pull rebase origin remote if there is conflict clean it a
  • 3月份的字节跳动面经

    本人2本毕业 目前工作四年 一直是Android 做的都是些二线公司 没做过一线 四年跳了三家公司 在家休息了几个月 今年3月份开始面试 由于跳槽过多而且已经是现在Android市场的原因 内推的我的字节哥们儿 推了不知道多少个部门 才把我
  • Python轻松搞定免费语音合成,利用百度AI为短视频配音

    1 创建百度AI账号 1 1 点击进入百度AI 左上角 开放能力 gt 语音合成 gt 立即使用 如果是试用 可以直接点击在线语音合成 不过语音不能下载 要下载还得用下面方式 调用百度AI的API 1 2 然后登录百度云账户 进入管理中心
  • qemu-virtio基本原理

    virtio是相当复杂的 网上写virtio原理解析的文章也不少 这里我想通过最简练易懂的方式来解释一下virtio的原理 一方面也完善一下自己对virtio的理解 文中含有大量个人理解 如果发现有错误的地方欢迎与我交流 virtio整体流
  • 掌财社:掌握CCI指标捕捉爆发牛股

    什么是CCI指标 CCI指标又叫顺势指标 其英文全名为 Commodity Channel Index 是由美国股市分析家唐纳德R 兰伯特 Donald r Lambert 于20世纪80年代所创 是指导股市投资的一种中短线指标 CCI指标
  • linuxas3+apache2+mysql5+php5+discuz5+zend3.3+supesite.docx

    最近领导要装个supesite discuz 方便公司内部用 对于公司内部用来说是大了点 感觉有些大财小用了 但如果考虑以后做成门户 还是很值得的 于是就动手配置 出于linux系统的稳定与安全 选择linux作为平台 本配置所用系统与软件
  • 认识glBegin

    初学OpenGL的时候总有很多函数或者函数的参数不会用 不明白其作用 今天主要总结一下关于glBegin 中的参数用法 一 glBegin glBegin表示一组用于定义一个或者多个图元的顶点的开始 此函数通常与glEnd函数联用 在glB
  • 深度学习中常见的loss函数汇总

    损失函数 Loss Function 分为经验风险损失函数和结构风险损失函数 经验风险损失函数反映的是预测结果和实际结果之间的差别 结构风险损失函数则是经验风险损失函数加上正则项 L1或L2 深度学习中的损失函数被用于模型参数的估计 通常作
  • SpringBoot (6)- 自定义Starter

    SpringBoot 6 自定义Starter 1 简介 1 1启动器starter命名 1 2什么是SpringBoot starter机制 1 3为什么要自定义starter 1 4什么时候需要创建自定义starter 1 5自动加载核
  • Matlab——图像缩放(插值法)

    实验内容 用双线性内插法实现位深度为8的灰度图像的缩放 思路 输入原图像以及缩放后图像的像素要求 宽度 高度 处理后输出新图像 我是用matlab来实现scale input img scale size 函数的 输入图像路径以及要求实现的
  • 排序函数c++函数模板实现

    冒泡排序 插入排序 选择排序 归并排序 快排 堆排序 冒泡排序 插入排序 选择排序 这种简单的时间复杂度是O n2 归并排序 快排 堆排序时间复杂度O nlogn include
  • 最短路的应用(G - Easy Glide );

    题意 就是到达一个点可以进行加速 加速时间3s给出了他的加速后的速度 计算从起点终点的最短距离 思路 首先把每个点都加上2这可以进行点的设置 起点为1 第一个加速点就是2 下一个就是3 设置点后进行计算 一个点都另一个点的全部时间进行计算
  • 如何评估加解密代码?

    在不深入研究代码的具体实现的情况下 如何评估加解密代码的有效性 强度 背景 迫于无赖 项目组只能安排1位新手设计一系列的加密算法 用于对本地文件和二进制代码的加密 幸运的是 对加密强度并没有过高的要求 但也希望能够有效的评估代码 并实现自动
  • Enter passphrase for key提示

    Enter passphrase for key提示 找了很多博客 都没有解决 后来通过删除密码解决了 删除方法 ssh keygen p enpty new passphrase后按回车
  • Arxiv 2307

    Retentive Network A Successor to Transformer for Large Language Models 论文 https arxiv org abs 2307 08621 代码 https github
  • Base64算法,kotlin密封接口

    密码协议 也称安全协议 指以密码学为基础的消息交换的通信协议 目的是在网络环境中提供安全服务 密码系统 指用于加密 解密的系统 柯克霍夫原则 数据的安全基于密钥而不是算法的保密 即系统的安全起决于密钥 对密钥保密 对算法公开 现代密码学设计
  • 笔试题10:Runnable接口与Thread类的区别?

    1 线程类继承自Thread则不能继承自其它类 而Runnable接口可以 2 线程类继承自Thread相对于Runnable来说 使用线程的方法更方便一些 3 实现Runnable接口的线程类的多个线程 可以更方便的访问同一变量 而Thr
  • 23种设计模式(7):中介者模式

    定义 用一个中介者对象封装一系列的对象交互 中介者使各对象不需要显示地相互作用 从而使耦合松散 而且可以独立地改变它们之间的交互 类型 行为类模式 类图 中介者模式的结构 中介者模式又称为调停者模式 从类图中看 共分为3部分 抽象中介者 定
  • .NET进阶篇07-.NET和COM

    知识需要不断积累 总结和沉淀 思考和写作是成长的催化剂 文章目录 一 COM和 NET 元数据 内存管理 接口 注册 线程 编组 二 NET客户端调用COM组件 三 COM客户端调用 NET组件 四 嵌入互操作类型 五 平台调用DllImp