和COM来个了断

和COM来个了断
      今年4月的时候,突然心血来潮想把COM学习一遍。
      有人说,COM已经过时了,现在都是.NET了,所以别再学COM了。可是就我目前的经验来看,在Windows世界里COM仍是无处不在,而且其思想永远有效。不过我周围的同事对此好像熟视无睹,几乎无人提及这项技术,也很少有人真正去了解,也许是我们的项目用不着吧。
      不过这玩意真的不是三下五除二能学会的。我陆陆续续看了2个月,仍然有很多迷惑。于是下决心在7月和COM来个了断,不管学到什么,理解了多少,本次攻坚COM就到此为止了,在此作一个阶段性总结。
      以后有缘去开发一个COM组件时,我们再相见。
 
      我主要看了两本书,《COM本质论》和《COM+编程指南》。前一本讲了很多设计COM的来龙去脉;后一本对编程有直接的指导。所以,想知道COM为什么这样,请看第一本;想直接使用COM编程,请看第二本或者其他编程指导书。
一、什么是COM?
      Component Object Model。我认为它本质是一种可执行模块的存在方式,或者说可执行模块的二进制标准。这种方式在空间上,是一个独立的可执行的二进制的模块实体;可以被多个用户使用,甚至是远程机器;是全局可见的,即具有位置透明性;可以被多种编程语言调用;在时序上它可以独立升级演化。
 
二、COM有那些种类?
      从存在形式角度来分有dll形式和exe形式,即In-proc和Out-of-proc形式。
      从技术发展角度来分有COM,DCOM,COM+。
 
三、COM的构成
      是一种服务器和客户端的构架。服务器端有接口定义idl,类定义,类实现(可以使用ATL)及对象(具有对称性、传递性和自反性);客户端,即调用COM的程序。
 
四、COM的标准接口
      为了解决很多奇奇怪怪的问题,微软从IUnknown接口继承了众多的标准接口,例如IDispatch提供双接口,IMarshal解决多线程问题,IConnectionPoint解决类似事件的问题。主要的标准接口在VC8的comdefsp.h文件中都能找到智能指针的定义,有426个,但接口定义并不在此文件中,应该在idl文件中。
 
五、COM的实现
      不是一两句能说清楚的,忽略。
 
六、COM的目的
      一开始应该是重用和独立演化。但后来好像希望集成所有的优势,除了跨平台。
 
七、COM的演化过程
1、二进制重用
      不同的应用使用同一个模块,可以独立出dll文件来解决。
2、编译器及语言无关性
      即二进制模块可以被不同C++语言编译器重用,也可以被不同的语言直接调用。由此带来的问题和解决方法举例如下:修饰名问题,使用DEF统一;修改某个函数,直接替换dll模块即可;调用时参数传入方式没有规范,显式的标明参数传入方式为stdcall。
3、独立演化性
      同一个应用在软件生命周期中使用一个不断独立发展的模块,该模块最好使用COM组件。新的应用可以使用老的dll,老的应用可以适用新的dll。
例如,新的dll添加了新的数据成员,解决方法:dll文件名增加版本信息,但是容易造成版本混乱;增加引用方式的接口句柄类,但是如果方法很多不方便;添加没有数据的接口类,分离接口和实现,利用C++继承的虚函数表方法,同时增加全局的生成函数,但是析构函数即使是虚函数,不同编译器的实现方式也不一样,那么可以增加显式的虚销毁函数解决。使用虚函数表同时避免了修饰名问题;使用生成函数可以显式联入dll,则运行时对dll才有依赖性,并且可以动态换用dll。
      又例如添加了新的函数成员,解决方法:添加到虚函数表的末尾,但是如果新的应用调用了老的模块,会非法;从接口类继承一个接口子类,用户使用dynamic_cast来判断是否兼容接口子类,但是使用RTTI和编译器相关;变为一个函数调用模拟RTTI。
4、资源管理问题
      需要用户去额外管理new的对象并销毁,解决方法,把这部分工作交给dll,增加Add,Remove操作。
5、COM相关名词
      有太多的名词和COM相关。
      OLE(Object Link Embedded),ActiveX,ATL(Active Template Library),MTS(Microsoft Transaction Server)。
      OLE是COM发展的导火索;ActiveX是一种特殊的COM用于网页中;ATL是COM实现的模板库;MTS是COM+的前身。
 
八、COM头文件
      oaidl.h,ocidl.h,ObjIdl.h,ObjSafe.h等等文件,定义一些常用的接口,它们都有对应的.idl文件,这些.h均是从对应的idl文件使用MIDL编译得到的。说明这些.h均是和OS无关的接口定义,也没有对于的.lib。
      OleAuto.h包括了BSTR API; Time API; SafeArray API; VARIANT API; ITypeLib; ITypeInfo; ITypeComp; TypeInfo API; IDispatch implementation support; Active Object Registration API; Active Object Registration API; ErrorInfo API。这些是COM提供者的已有实现接口对应的API。
      OleCtl.h定义了诸如DllRegisterServer,Ole系列函数,COM提供者的控制接口。
      ObjBase.h定义一些Co系列函数,COM客户端Consumer使用。
      这些ole开头的头文件,除了少数有对应的idl文件,大多没有,都是定义了一些API函数,并且有对应的lib文件。
 
九、COM的发布
      COM组件发布时,可以把接口以资源的方式和dll一起发布,确实很巧妙。这样组件发布时只需要提供运行时dll和帮助文件,不需要专门为开发者提供头文件和库文件。这一特性给我留下了深刻的印象。
      微软的MsAgent,SpeechAPI,DirectX早期版本等等组件,都是以COM形式发布的。
 
十、对COM的编译器支持
      为了COM,编译器也进行了专门的支持。例如客户端使用#import "XXX.dll" no_namespace即可引入其资源中的.h(实际是.tlh和.tli);使用struct __declspec(uuid("faeae6b7-67be-42a4-a318-3256781e945a")) ISomeInterface定义接口GUID后,就可以直接使用__uuidof(ISomeInterface)来得到其GUID,避免了必须在一额外的c文件中定义其GUID的常量。
 
十一、COM的发展
      DCOM只针对exe形式的COM。DCOM应该需要DCOM Server Process Launcher(C:/WINDOWS/system32/svchost -k DcomLaunch)服务才能运行。
      COM+,即COM Service,或者说是COM + MTS,提供了一系列对COM的管理,COM+主要提供了如下服务:上下文,事务处理、补偿资源管理器、排队组件、对象缓冲、事件处理、即时激活、基于角色的安全性、共享属性等待。
      对于dll形式的COM如果也想加入COM+的话,需要dllhost.exe作为宿主进程。不幸的是dllhost.exe这个名称曾被病毒利用过。
      C:/WINDOWS/system32/Com/comexp.msc实现了对COM+和DCOM的可视化管理。COM+对应的配置文件是C:/WINDOWS/Registration下的文件,被称为RegDB;DCOM对应的配置存在与注册表中HKEY_CLASSES_ROOT/AppID。二者信息合起来被叫做COM+目录。看来微软并不把任何信息都注册到注册表里了,据微软自己讲RegDB更加灵活。
 
十二、一些COM资料
      http://msdn2.microsoft.com/en-us/library/aa139672.aspx MSDN的COM帮助。
      http://www.microsoft.com/china/MSDN/library/windev/COMponentdev/CDCOMplus4vb.mspx?mfr=true 面向 Microsoft Visual Basic 程序员的 COM+ 概述,这篇文章讲的不错。
 
十三、微软的霸道
      之所以说微软霸道,是因为本来微软加入了OMG组织,一起制定组件国际标准,但后来却摆脱了此组织,单独发展了COM,又不在Linux世界支持,搞的跨平台应用开发十分郁闷。
 
      COM是否完美?肯定还有很多不足之处,不过在现有技术的基础之上,它集合了众多优点,克服了传统软件的许多缺点,再加上微软的霸道式的推广与应用,Windows世界中它一定会在一定历史阶段长期存在下去。
 
# P5022 [NOIP 2018 提高组] 旅行 ## 题目背景 NOIP2018 提高组 D2T1 ## 题目描述 小 Y 是一个爱好旅行的 OIer。她来到 X 国,打算将各个城市都玩一遍。 小 Y 了解到,X 国的 $n$ 个城市之间有 $m$ 条双向道路。每条双向道路连接两个城市。 不存在两条连接同一对城市的道路,也不存在一条连接一个城市它本身的道路。并且, 从任意一个城市出发,通过这些道路都可以到达任意一个其他城市。小 Y 只能通过这些 道路从一个城市前往另一个城市。 小 Y 的旅行方案是这样的:任意选定一个城市作为起点,然后从起点开始,每次可 以选择一条与当前城市相连的道路,走向一个没有去过的城市,或者沿着第一次访问该 城市时经过的道路后退到上一个城市。当小 Y 回到起点时,她可以选择结束这次旅行或 继续旅行。需要注意的是,小 Y 要求在旅行方案中,每个城市都被访问到。 为了让自己的旅行更有意义,小 Y 决定在每到达一个新的城市(包括起点)时,将 它的编号记录下来。她知道这样会形成一个长度为 $n$ 的序列。她希望这个序列的字典序 最小,你能帮帮她吗? 对于两个长度均为 $n$ 的序列 $A$ $B$,当且仅当存在一个正整数 $x$,满足以下条件时, 我们说序列 $A$ 的字典序小于 $B$。 - 对于任意正整数 $1 ≤ i < x$,序列 $A$ 的第 $i$ 个元素 $A_i$ 序列 $B$ 的第 $i$ 个元素 $B_i$ 相同。 - 序列 $A$ 的第 $x$ 个元素的值小于序列 $B$ 的第 $x$ 个元素的值。 ## 输入格式 输入文件共 $m + 1$ 行。第一行包含两个整数 $n,m(m ≤ n)$,中间用一个空格分隔。 接下来 m 行,每行包含两个整数 $u,v (1 ≤ u,v ≤ n)$ ,表示编号为 $u$ $v$ 的城市之 间有一条道路,两个整数之间用一个空格分隔。 ## 输出格式 输出文件包含一行,$n$ 个整数,表示字典序最小的序列。相邻两个整数之间用一个 空格分隔。 ## 输入输出样例 #1 ### 输入 #1 ``` 6 5 1 3 2 3 2 5 3 4 4 6 ``` ### 输出 #1 ``` 1 3 2 5 4 6 ``` ## 输入输出样例 #2 ### 输入 #2 ``` 6 6 1 3 2 3 2 5 3 4 4 5 4 6 ``` ### 输出 #2 ``` 1 3 2 4 5 6 ``` ## 说明/提示 【数据规模与约定】 对于 $100\%$ 的数据所有样例, $1 \le n \le 5000 $ 且 $m = n − 1$ 或 $m = n$ 。 对于不同的测试点, 我们约定数据的规模如下: ![](https://cdn.luogu.com.cn/upload/pic/43271.png) 下面代码能否解决上述题目 #include <bits/stdc++.h> using namespace std; const int N=5e3+10; int n,m,idx,ind[N]; bool vis[N],vis1[N],frun=true; vector<int>G[N],ans,h,th; queue<int>q; bool init(int u) { for(int i=0,l=th.size(); i<l; i++) { if(u==th[i]) return true; } return false; } void mmp() { for(int i=1; i<=n; i++) { sort(G[i].begin(),G[i].end()); } } void topo() { for(int i=1; i<=n; i++) { if(ind[i]==1) q.push(i); } while(q.size()) { int x=q.front() ; q.pop(); vis1[x]=1; for(int i=0,len=G[x].size(); i<len ; i++) { ind[G[x][i]]--; if(ind[G[x][i]]==1) q.push(G[x][i]); } } for(int i=1; i<=n; i++) { if(!vis1[i]) th.push_back(i); } memset(vis1,0,sizeof(vis1)); } void dfs1(int u) { vis1[u]=true; if(init(u)) { idx=u; vis1[u]=false; frun=false; } for(int i=0,l=G[u].size(); i<l; i++) { if(frun==false) return; int v=G[u][i]; if(init(v)) { idx=v; frun=false; break; } if(!vis1[v]) dfs1(v); } } void dfs2(int u) { vis1[u]=1; h.push_back(u); for(int i=0,l=G[u].size(); i<l; i++) { if(h.size()==th.size()) return; int v=G[u][i]; if(init(v)&&(!vis1[v])) { dfs2(v); } } } void cutmp() { bool f=false; int cutu,cutv; int l=h.size(); if(h[1]>h[l-1]) { reverse(h.begin()+1,h.end()); } for(int i=1; i<l-1; i++) { if(h[i]>h[l-1]) { cutu=h[i-1]; cutv=h[i]; f=true; break; } } if(!f) cutu=h[0],cutv=h[l-1]; for(vector<int>::iterator it=G[cutu].begin(); it!=G[cutu].end(); it++) { if(*it == cutv) { G[cutu].erase(it); sort(G[cutu].begin(),G[cutu].end()); break; } } for(vector<int>::iterator it=G[cutv].begin(); it!=G[cutv].end(); it++) { if(*it == cutu) { G[cutv].erase(it); sort(G[cutv].begin(),G[cutv].end()); break; } } //cout<<cutu<<" "<<cutv<<"\n"; } void dfs(int u) { vis[u]=1; ans.push_back(u); for(int i=0,l=G[u].size(); i<l; i++) { if(!vis[G[u][i]]) dfs(G[u][i]); } } int main() { /* freopen("P5022_20.in","r",stdin); freopen("P5022_20.ans","w",stdout); */ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin>>n>>m; for(int i=0,u,v; i<m; i++) { cin>>u>>v; G[u].push_back(v); G[v].push_back(u); ind[u]++,ind[v]++; } mmp(); if(m==n) { topo(); dfs1(1); dfs2(idx); /* for(int i=0,l=h.size();i<l;i++){ cout<<h[i]<<" "; } cout<<"\n";*/ cutmp(); } dfs(1); for(int i=0,l=ans.size(); i<l; i++) { cout<<ans[i]<<" "; } return 0; }
最新发布
03-13
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值