COM相关文章:
http://dev.yesky.com/29/2037029.shtml
2.COM编程小结
http://blog.csdn.net/byxdaz/article/details/6595210
3. COM组件的调用
http://www.cppblog.com/woaidongmao/archive/2011/01/10/138250.html
4.
VC++ : VS2008 使用ATL开发COM组件
1.COM编程小结
VC6中用ATL写组件
一、前言
1、如果你在使用 vc5.0 及以前的版本,请你升级为 vc6.0 或 vc.net 2003;
2、如果你在使用 vc6.0 (ATL 3.0)请阅读本回内容;
3、如果你在使用 vc.net(ATL 7.0)请阅读下回内容;(当然读读本文内容也不错)
4、这第一个组件,除了所有 COM 组件必须的 IUnknown 接口外,我们再实现一个自己定义的接口 IFun,它有两个函数: Add()完成两个数值的加法,Cat()完成两个字符串的连接。
5、下面......好好听讲! 开始了:-)
二、建立 ATL 工程
步骤2.1:建立一个工作区(WorkSpace)。
步骤2.2:在工作区中,建立一个 ATL 工程(Project)。示例程序叫 June12,并选择DLL方式,见图一。
图一、建立 ATL DLL 工程
Dynamic Link Library(DLL) 表示建立一个 DLL 的组件程序。
Executable(EXE) 表示建立一个 EXE 的组件程序。
Service(EXE) 表示建立一个服务程序,系统启动后就会加载并执行的程序。
Allow merging of proxy/stub code 选择该项表示把“代理/存根”代码合并到组件程序中,否则需要单独编译,单独注册代理存根程序。代理/存根,这个是什么概念?还记得我们在上回书中介绍的吗?当调用者调用进程外或远程组件功能的时候,其实是代理/存根负责数据交换的。关于代理/存根的具体变成和操作,以后再说啦......
三、增加 ATL 对象类
步骤3.1:菜单 Insert\New ATL Object...(或者用鼠标右键在 ClassView 卡片中弹出菜单)并选择Object 分类,选中 Simple Object 项目。见图二。
图二、选择建立简单COM对象
Category Object 普通组件。其中可以选择的组件对象类型很多,但本质上,就是让向导帮我们默认加上一些接口。比如我们选 "Simple Object",则向导给我们的组件加上 IUnknown 接口;我们选 "Internet Explorer Object",则向导除了加上 IUnknown 接口外,再增加一个给 IE 所使用的 IObjectWithSite 接口。当然了,我们完全可以手工增加任何接口。
Category Controls ActiveX 控件。其中可以选择的 ActiveX 类型也很多。我们在后续的专门介绍 ActiveX 编程中再讨论。
Category Miscellaneous 辅助杂类组件。
Categroy Data Access 数据库类组件(我最讨厌数据库编程了,所以我也不会)。
步骤3.2:增加自定义类 CMyObject(接口 IMyObject) ,见图三。
图三、输入类中的各项名称
其实,我们只需要输入短名(Short Name),其它的项目会自动填写。没什么多说的,只请大家注意一下 ProgID 项,默认的 ProgID 构造方式为“工程名.短名”。
步骤3.3:填写接口属性,见图四。
图四、接口属性
Threading Model 选择组件支持的线程模型。COM 中的线程,我认为是最讨厌,最复杂的部分。COM 线程和公寓的概念,留待后续介绍。现在吗......大家都选 Apartment,它代表什么那?简单地说:当在线程中调用组件函数的时候,这些调用会排队进行。因此,这种模式下,我们可以暂时不用考虑同步的问题。
Interface 接口基本类型。Dual 表示支持双接口,这个非常 非常重要。(经试验,如果不选Dual ,而选Custom,则Qt应用程序调用起来有问题。)
Aggregation 我们写的组件,将来是否允许被别人聚合使用。Only 表示必须被聚合才能使用,有点类似 C++ 中的纯虚类,你要是总工程师,只负责设计但不亲自写代码的话,才选择它。
Support ISupportErrorInfo 是否支持丰富信息的错误处理接口。以后就讲。
Support Connection Points 是否支持连接点接口(事件、回调)。以后就讲。
四、添加接口函数
图五、调出增加接口方法的菜单
图六、增加接口函数 Add
图七、增加接口函数 Cat
请严格按照图六的方式,增加Add()函数;由于图七中增加Cat()函数的参数比较长,我没有适当的输入空格,请大家自己输入的时候注意一下。[in]表示参数方向是输入;[out]表示参数方向是输出;[out,retval]表示参数方向是输出,同时可以作为函数运算结果的返回值。一个函数中,可以有多个[in]、[out],但[retval]只能有一个,并且要和[out]组合后在最后一个位置。
图八、接口函数定义完成后的图示
我们都知道,要想改变 C++ 中的类函数,需要修改两个地方:一是头文件(.h)中类的函数声明,二是函数体(.cpp)文件的实现处。而我们现在用 ATL 写组件程序,则还要修改一个地方,就是接口定义(IDL)文件。别着急 IDL 下次就要讨论啦。
由于 vc6.0 的BUG,导致大家在增加完接口和接口函数后,可能不会向上图(图八)所表现的样式。解决方法:
1 | 关闭工程,然后重新打开 | 该方法常常有效 |
2 | 关闭 IDE,然后重新运行 | |
3 | 打开 IDL 文件,检查接口函数是否正确,如不正确请修改 | |
4 | 打开 IDL 文件,随便修改一下(加一个空格,再删除这个空格),然后保存 | 该方法常常有效 |
5 | 打开 h/cpp 文件,检查函数是否存在或是否正确,有则改之 | 无则嘉勉,不说完这个成语心理别扭 |
6 | 删除 IDL/H/CPP 中的接口函数,然后 | 再来一遍 |
7 | 重新建立工程、重新安装vc、重新安装windows、砸计算机 | 砸! |
五、实现接口函数
鼠标双点图八中CFun\IFun\Add(...)就可以开始输入函数实现了:
STDMETHODIMP CMyObject::Add(long n1, long n2, long *pVal) { *pVal = n1 + n2; return S_OK; }
1、自己先按照今天讲的内容写出这个组件;
2、不管你懂不懂,一定要去观察 IDL 文件,CPP 文件;
3、编译后,看都产生了些什么文件?如果是文本的文件,就打开看看;
4、下载本文的示例程序(vc6.0版本)编译运行,看看效果。然后预习一下示例程序中的调用方法;
====================使用COM的方法=============================================
http://www.cppblog.com/woaidongmao/archive/2011/01/10/138250.html
1.创建myCom.dll,该COM只有一个组件,两个接口:
IGetRes--方法Hello(),
IGetResEx--方法HelloEx()
2.在工程中导入组件或类型库
#import "组件所在目录myCom.dll" no_namespace
或
#import "类型库所在目录myCom.tlb"
using namespace MYCOM;
方法一:
CoInitialize(NULL);
CLSID clsid;
CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);
CComPtr<IGetRes> pGetRes;//智能指针
pGetRes.CoCreateInstance(clsid);
pGetRes->Hello();
pGetRes.Release();//小心哦!!请看最后的“注意”
CoUninitialize();
方法二:
CoInitialize(NULL);
CLSID clsid;
HRESULT hr=CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);
IGetRes *ptr;
hr=CoCreateInstance(clsid,NULL,CLSCTX_INPROC_SERVER,
__uuidof(IGetRes),(LPVOID*)&ptr);
ptr->Hello();
CoUninitialize();
方法三:
CoInitialize(NULL);
HRESULT hr;
CLSID clsid;
hr=CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);
IGetRes* ptr;
IGetResEx* ptrEx;
//使用CoCreateClassObject创建一个组件(特别是mutilThreads)的多个对象的
时候,效率更高.
IClassFactory* p_classfactory;
hr=CoGetClassObject(clsid,CLSCTX_INPROC_SERVER,
NULL,IID_IClassFactory,
(LPVOID*)&p_classfactory);
p_classfactory->CreateInstance(NULL,__uuidof(IGetRes),
(LPVOID*)&ptr);
p_classfactory->CreateInstance(NULL,__uuidof(IGetResEx),
(LPVOID*)&ptrEx);
ptr->Hello();
ptrEx->HelloEx();
CoUninitialize();
方法四:
直接从dll中得到DllGetClassObject,接着生成类对象及类实例(这方法可以
使组件不用在注册表里注册,这是最原始的方法,但这样做没什么意义,至少失去了COM
对用户的透明性),不推荐使用.
typedef HRESULT (__stdcall * pfnHello)(REFCLSID,REFIID,void**);
pfnHello fnHello= NULL;
HINSTANCE hdllInst = LoadLibrary("组件所在目录myCom.dll");
fnHello=(pfnHello)GetProcAddress(hdllInst,"DllGetClassObject");
if (fnHello != 0)
{
IClassFactory* pcf = NULL;
HRESULT hr=(fnHello)(CLSID_GetRes,IID_IClassFactory,(void**)&pcf);
if (SUCCEEDED(hr) && (pcf != NULL))
{
IGetRes* pGetRes = NULL;
hr = pcf->CreateInstance(NULL, IID_IFoo, (void**)&pGetRes);
if (SUCCEEDED(hr) && (pFoo != NULL))
{
pGetRes->Hello();
pGetRes->Release();
}
pcf->Release();
}
}
FreeLibrary(hdllInst);
方法五:
通过ClassWizard利用类型库生成包装类,不过前提是com组件的接口必须是派
生自IDispatch,具体方法:
调出添加类向导(.NET中),选择类型库中MFC类,打开,选择"文件",选择
"myCom.dll"或"myCom.tlb",接下来会出来该myCom中的所有接口,选择你想
生成的接口包装类后,向导会自动生成相应的.h文件.这样你就可以在你的MFC中
像使用普通类那样使用组件了.(CreateDispatch("myCom.GetRes") 中的参数就是ProgID通过Clsid在注册表中可以查询的到)
CoInitialize(NULL);
CGetRes getRest;
if (getRest.CreateDispatch("myCom.GetRes") != 0)
{
getRest.Hello();
getRest.ReleaseDispatch();
}
CoUninitialize();