﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-提琴协奏-随笔分类-COM/COM+</title><link>http://www.cppblog.com/flagman/category/15575.html</link><description>提琴协奏</description><language>zh-cn</language><lastBuildDate>Sun, 19 Dec 2010 08:25:12 GMT</lastBuildDate><pubDate>Sun, 19 Dec 2010 08:25:12 GMT</pubDate><ttl>60</ttl><item><title>关于COM和.net的思考</title><link>http://www.cppblog.com/flagman/archive/2010/12/19/AboutComAndDotNet.html</link><dc:creator>flagman</dc:creator><author>flagman</author><pubDate>Sun, 19 Dec 2010 03:04:00 GMT</pubDate><guid>http://www.cppblog.com/flagman/archive/2010/12/19/AboutComAndDotNet.html</guid><wfw:comment>http://www.cppblog.com/flagman/comments/136922.html</wfw:comment><comments>http://www.cppblog.com/flagman/archive/2010/12/19/AboutComAndDotNet.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/flagman/comments/commentRss/136922.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flagman/services/trackbacks/136922.html</trackback:ping><description><![CDATA[<p><em>【 某某提到: 】<br>: 一般说COM复杂，首先是名词太多，其次是基于ATL的实现比较难懂<br>: 这并不是COM本身复杂，而是C++已经落后于时代了。所以ATL看起来才会像天书一般</em></p>
<p><br>虽然对于全新的工程项目，推荐通过.net实现，但是，只要你工作在Windows平台上，必然会遇到和COM相关的技术和机制，无论是大量的legacy的工程和代码，还是作为OS重要功能以及native组件的首选交互形式和接口暴露方式，比如DirectX API，比如一些WMI的API；最有趣的是，即使是.net的核心CLR本身也是一个COM组件，可以通过Host相关接口让native应用来加载，以在当前进程中启动整个CLR的虚拟执行环境或者叫托管执行环境(managed executive environment)。</p>
<p>把握COM有两点很关键，<br>1）Interface-based design，从设计和编码思路上就是要完全基于接口；<br>2）VirtualTable-based binary compatibility, 实现上无论何种语言或者机制，只要符合基于虚表的二进制兼容规范，就都可以实施；</p>
<p>COM仅仅是个规范，基于COM的具体技术非常之多，OLE，Automation，Structural storage，ActiveX...汗牛充栋，还有COM+，这个是提供企业级开发必备的一些基础功能和设施，比如，事务管理机制，对象池，安全管理，消息队列...需要指出，目前即便是.net Framework也没有实现COM+所提供这些机制，只是简单的封装了后者。</p>
<p>COM技术中可能有一些比较困难的地方，接口的一致性，对象的聚合和生命周期，套间，跨套间的接口访问，名字对象，等等；这些并不是COM规范人为制造的困难，而是为了设计和提供，可以跨进程和机器边界，跨异构平台（当然必须实现了COM所规定的基础服务），透明化具体对象类型及对象生命周期，便于统一部署和版本管理的组件技术，所必须付出的代价，这个代价从开发人员角度看具体表现为，概念理解的困难以及具体二进制实现的困难；</p>
<p>不过从另一个角度看，COM已经很容易了，<br>a) COM规范已把要达致这些目标的系统，所必须提供的接口和特性抽象了出来，只不过为了表达这些抽象的概念而新造的术语名词有些陌生和突兀；如果让遇到相似问题的每一个设计和开发人员都自己来做抽象，未必会生成更好的方案；</p>
<p>b) 为了帮助设计和开发人员，人们提供了很多的开发库，以提高COM开发的正确性和效率；最显著的就是MFC中关于COM/OLE的辅助类和函数，以及为了COM而生的ATL；从本质上看，这些类库都是把COM规范中必须实现的，Windows平台本身没有提供，具体设计和开发人员实际实施时会重复实现的，同时又非常容易出错的那部分功能，集中到了这些类库里统一实现，让具体设计和开发人员以代码重用的形式来实现COM规范；</p>
<p>当然人们也意识到了COM这样的一些问题，特别是具体实现时设计和开发人员必须要关注几乎所有的二进制细节，于是.net就诞生了，把这些规范的许多复杂性都封装在了虚拟机里面，把这些目标功能（跨边界、透明性等等）通过一致而又平滑的平台接口和自描述的meta data，以一种让设计和开发人员更易接受的风格开放了出来；</p>
<p>COM的影响是非常广大的，比如XPCOM ，Firefox上的一种插件技术标准，就是根据COM的思想和原则制定的；许多评论说，Firefox的成功是因为它插件是如此的成功，这也算是COM本身所意料不到的贡献之一。</p>
<p>在.net的平台上，即使是.net CLR/SSCLI的具体实现也大量运用了COM的思想和机制，可以说.net就是搭建在COM二进制组件平台之上的虚拟机托管平台。</p>
<p>最后，.net开始时的内部编号是COM 2.0</p>
<p>&nbsp;</p>
<p>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++</p>
<p>*) 关于&#8220;名词太多&#8221;<br>这是要实现可以跨进程和机器边界，跨异构平台（当然必须实现了COM所规定的基础服务），透明化具体对象类型及对象生命周期，便于统一部署和版本管理的组件技术，所必须付出的代价。</p>
<p>COM规范已把要达致这些目标的系统，所必须提供的接口和特性抽象了出来，只不过为了表达这些抽象的概念而新造的术语名词有些陌生和突兀；如果让遇到相似问题的每一个设计和开发人员都自己来做抽象，未必会生成更好的方案；</p>
<p>举个例子，apartment，套间，就是为了抽象传统OS中进程和线程的实现而新造的术语名词和概念；任何人要抽象这样的一些概念，不新造术语，是非常困难的，对比.net，后者用了CLR虚拟机来封装了大多数的实现细节，并用让人更容易接受的风格来开放接口，可事实上仍然新造了一些名词和概念，如类似范畴的AppDomain；</p>
<p>*) 关于&#8220;基于ATL的实现比较难懂&#8221;<br>ATL主要使用了template技术，COM接口智能指针，用静态转换来模拟动态绑定，等等，实际并不是很复杂，只能算c++实现机制的中等难度，主要涉及Modern C++ design一书中一些相关设计理念的运用。对比Boost中某些库的实现，ATL很人道了。</p>
<p>*) 关于&#8220;这并不是COM本身复杂，而是C++已经落后于时代了&#8221;<br>首先COM的规范的确是复杂的，为啥？第一点已经说了，就是为了要抽象出跨边界和对象透明的组件技术；.net表象上看比较&#8220;简单容易&#8221;，风格亲近设计和开发人员，实际上复杂事务和实现细节都被划分到CLR那个层面上去实现了；去看一下CLR的开源实现SSCLI，你会发现，整个虚拟机平台的实现，大量运用了COM的思想和机制，就是一个巨型系统平台级的COM server；</p>
<p>其次，COM规范本身是独立于实现语言的，只要构建出的组件符合规范制定的二进制兼容，系统就可以运作，这和C++是否落后时代没有关系。如果开发人员认为，.net才够先进，也完全可以用.net中的托管语言，如C#来实现COM组件；</p>
<p>最后，每种语言都有其适用的范围，现在可以这么说&#8220;如果有一个全新的项目需求，要达致跨边界和对象透明组件，并且没有太过严苛的性能需求，那么.net平台及其上的托管语言来实现，比用C++及相关辅助类库来以COM组件形式来实现，要更合适，也更快速便捷和节省预算。&#8221;但是，在这个判断上我们加了很多严格的约束，一旦需求变更，特别是项目的非功能性需求，要求高性能运算或者更顺畅的与legacy的native系统相互，那么&#8220;使用native语言来实现性能关键以及legacy交互功能，通过COM封装，再由COMInterop交.net托管应用调用&#8221;可能是更现实的方案。C++是一门活的语言，不断发展的语言，即使在最新的托管时代里，C#成为标准主流，但C++/CLI仍然是托管语言里功能最完整的语言。</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/flagman/aggbug/136922.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flagman/" target="_blank">flagman</a> 2010-12-19 11:04 <a href="http://www.cppblog.com/flagman/archive/2010/12/19/AboutComAndDotNet.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>思考系统API设计的问题</title><link>http://www.cppblog.com/flagman/archive/2010/12/01/Thinking_Of_API_Design.html</link><dc:creator>flagman</dc:creator><author>flagman</author><pubDate>Wed, 01 Dec 2010 13:28:00 GMT</pubDate><guid>http://www.cppblog.com/flagman/archive/2010/12/01/Thinking_Of_API_Design.html</guid><wfw:comment>http://www.cppblog.com/flagman/comments/135191.html</wfw:comment><comments>http://www.cppblog.com/flagman/archive/2010/12/01/Thinking_Of_API_Design.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flagman/comments/commentRss/135191.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flagman/services/trackbacks/135191.html</trackback:ping><description><![CDATA[<p>最近正好在思考系统API设计中考量的一些问题，<br><br>【某网友讨论到】<br><em>: 那地址是不是同一个地址呢。我现在的理解是这样的，假设有巨大的真实内存。windows首先将高2G的内存自己占了，用作各种内核对象。这2G内存共享给每个进程，但进程不能直接访问，只能通过windows给定的函数访问。<br>: 然后每个进程都给他2G内存，进程如果创建自己的对象就放到自己那2G内存里面，如果要建立内核对象就放到共享的那高2G里面去。<br>: 所以不同进程如果可以访问高2G内存的话，任何进程访问到同一个高地址实际上都是访问到同一个对象。但如果访问低2G地址的话，不同进程是对应不同的对象的。<br></em><br><br><br>在不同的进程中，询问同一个内核对象的实际地址（无论是线性地址还是物理地址），是无意义的：</p>
<p>首先，内核对象只能由在内核态下的例程才能直接访问，在我们日常的代码中，所调用的Windows API，比如CreateFile, （注意调用刚开始时是处于用户态下的），一般都会在ntdll.dll中找到对应的内核函数或例程，接着系统切换到内核态，开始调用实际对应的内核函数(KiCreateFile)，这个时候才会去访问内核对象的实际地址，然后建立一个该内核对象对应当前进程的Handle，并把它返回给caller，同时切换回用户态；因此，对于用户态程序来说，只要且只能知道该内核对象在当前进程中的对应的Handle就可以对其进行操作了；</p>
<p>其次，这样的设计是出于对OS核心数据结构（当然包括我们正在讨论的内核对象）的保护；如果用户态程序可以轻易的获取内核数据结构的实际地址，那么对于整个OS的安全和稳定显然构成很大的问题；一个用户态的误操作可以轻易的引起整个OS的崩溃，而有了这一层的保护，崩溃的只是当前进程而不是整个系统；</p>
<p>接着上面这点，也可以看出，内核对象的如此设计达到了接纳OS本身的平滑演进的目的。从Windows 3.0到95/98，从NT到Win2k/XP，再到眼下的Vista/Win7，Windows操作系统本身发生了巨大的变化和进步，采纳了无数的新技术新方法，但是它基本的系统应用编程接口，也就是我们所熟知的windows API，却并没有发生太大的改变，很多Win 3.0 这个16位OS时代的程序代码只要当初设计规范编码规范，稍许修改就可以在最新版的OS上运行如飞；是什么做到了这些？也就是所谓的极为重要的向后兼容性，我个人认为，把操作系统的重要/主要功能抽象成内核对象，并通过一套极为solid的API暴露出来，达成了这个目标。</p>
<p>这是一种更高层次上的面向对象，把实现的细节，把系统的复杂，简单而优雅的封装了起来。你只要调用CreateFile去建个文件或管道或邮槽，不用担心当前OS是Windows 3.0还是Win7，获得的Handle，你也不用去关心它以及它所指向的内核对象是Windows 3.0的实现还是Win7的实现。</p>
<p>Windows上所有的精彩几乎都是基于这套通过内核对象概念抽象并暴露的API基础之上，COM/OLE，这个二十年前震撼性的ABI和IPC范畴的技术规范，其中很多的设计思路也是植根于内核对象的设计理念，如COM对象的引用计数和内核对象引用计数，IUnknown和Windows Handle(前者是指向某个二进制兼容的组件对象，后者引用或间接指向某个内核对象，都是对于某个复杂概念的一致性抽象表述)，等等；</p>
<p>十年前的.net，本来是作为COM的升级版本推出，把COM/OLE的实现复杂性封装在了虚拟机平台CLR里面，而从这个虚拟机的开源实现SSCLI，我们可以看到大量的COM机制在.net的具体实现里面起了举足轻重的作用。在这些VM中大量symbol有着COR的前缀或者后缀，COR指代什么？Common Object Runtime, 原来CLR/SSCLI的设计思路也是把OS通过虚拟机VM的形式，并通过common object向应用程序暴露功能。</p>
<p>小结一下，<br>OS内核对象API，三十年前系统级别的对象抽象；<br>COM/OLE，二十年前二进制组件级别的对象抽象；<br>.net/CLR, 十年前虚拟机平台级别的对象抽象；</p>
<p>写到这里倒是引起了我其他的一些思考，软件工业界一直以来对面向对象OO是热火朝天，特别是语言层面，从C++/Java/C#到Python/JScript，不一而足；</p>
<p>但是我们有没有从根本性的设计理念上对面向对象，察纳雅言了呢？</p>
<p>如果现在设计Windows这套API的任务放在大家面前，会采用内核对象/Handle方案还是直接指向OS内部数据结构的方式来暴露功能？</p>
<p>从三十年前的这套API的设计中，我们真的可以学到很多。</p>
<p><br>&nbsp;</p>
<img src ="http://www.cppblog.com/flagman/aggbug/135191.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flagman/" target="_blank">flagman</a> 2010-12-01 21:28 <a href="http://www.cppblog.com/flagman/archive/2010/12/01/Thinking_Of_API_Design.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>