作者:收割者
本篇文章讲解如何实现COM接口,一方面我们可以实现COM的预定义接口,也就是系统中微软提供的接口,例如,IDataObject接口,这个接口是在实现拖放,剪切板(新的剪切板技术)的时候,需要我们实现的接口;另一方面,我们可以实现我们自定义的COM接口,但是自定义的COM接口应该在实现之前选择一种接口的驱动方式,如类型库(TypeLib)还是代理根服务器(proxy/sub dll),并实现这些驱动方式,在系统中注册,前面文章我用了代理根服务器的方式在系统中注册了我定义的接口。本篇文章,我继续上面一篇文章,实现自定义的COM接口。
我们将接口的定义视为类似于定义纯虚函数的过程。那么现在对接口进行实现(由Implement翻译过来),就相当于重写虚函数,纯虚函数只有函数的原型,没有实现,接口也是,接口的定义只是声明了它的方法的原型。要使用它,需要实现它,另外,接口的实现可以多次实现,不用在意原有的实现。接着上一篇文章,在解决方案中添加一个新的项目,在“解决方案视图”中,点击鼠标右键,弹出快捷菜单,选择“添加”,选择“新建项目”。如下图:
在“已安装模板中”,选择Win32项目,填写名称,我这里是ImplCom,设置项目的属性,设置为DLL,选中“空项目”.如下图:
现在项目里面是空的,我们添加一个源文件,DllMain,添加DllMain入口点代码,这个代码在msdn中有,直接复制过来就可以了。在“解决方案视图”中ImplCom项目名称上,点击右键,在快捷菜单中,选中“添加”,选中“新建项”,在弹出来的对话框中,选中cpp文件,添加名称,如下图:
上面图中DllMain入口点函数的内容,在msdn讲解DLL中关于DllMain入口点的时候,有现成的代码,直接复制过来就可以了,在以后,编写Win32的DLL的时候,也是这样,如果是你建立的是空项目,自己添加cpp文件之后,就从msdn中直接把入口点函数的代码复制过来就可以了。
现在我们再添加一个头文件和一个源文件用于实现接口。方法同上,在ImpleCom上点击右键......。完成之后,如下图:
我添加了Reaper.h,Reaper.cpp文件。现在编写实现接口的代码,首先在Reaper.h文件中,代码如下:
#pragma once #include "C:\\Users\\Administrator\\Documents\\Visual Studio 2010\\Projects\\COMSample\\COMSample\\COMSampleInterface_h.h" #include "Shlwapi.h" #pragma comment(lib,"Shlwapi.lib") class CReaper:public IShowGeZhe { public: CReaper(); private: ~CReaper(); private: LONG m_cRef; public: /////////////////////IUnknown接口的方法 STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); //////////////////////IShowGeZhe接口的方法 STDMETHODIMP ShowHelloCOM(HWND hwnd); };COMSampleInterface_h.h文件就是我们在定义接口的时候生成的四个文件之一(参考我的博客文章“COM专题三”).当中它声明了接口及接口方法的原型。这里我们要实现它,因此包含它,我这里使用的全路径,你可以使用相对路径或是将这个头文件复制到你当前的项目目录下,那么就直接用#include "COMSampleInterface_h.h"就可以了。Shlwapi.h头文件是一个系统头文件,属于Shell编程部分的,主要是提供帮助函数(helper function),后面会用到QISearch函数,这个函数在这个文件中声明。
所有的COM接口都必须直接或是间接的继承IUnknown接口,这个接口有三个方法,用管理COM对象的生命周期和接口的询问(query)。IShowGeZhe这个接口就是我们定义的接口,只有一个方法ShowHelloCOM。
接着我们在源文件Reaper.cpp中编写实现代码:
#include "Reaper.h" /////////////构造函数和析构函数 extern long g_cDllRef; CReaper::CReaper() { ///////在这里初始化项目 m_cRef=1; g_cDllRef++; } CReaper::~CReaper() { ///// g_cDllRef--; } HRESULT CReaper::QueryInterface(REFIID riid, void **ppv) { static QITAB rgqit[] = { QITABENT(CReaper,IShowGeZhe), { 0 } }; return QISearch(this, rgqit, riid,ppv); } ULONG CReaper::AddRef() { return InterlockedIncrement(&m_cRef); } ULONG CReaper::Release() { ULONG uCount = InterlockedDecrement(&m_cRef); if (uCount == 0) { delete this; } return uCount; } HRESULT CReaper::ShowHelloCOM(HWND hwnd) { MessageBox(hwnd,L"收割者COM测试",L"TIP",MB_OK); return S_OK; }
有关g_cDllRef这里暂不做讨论,在后面将COM组件注册的时候说明。在构造函数中,m_cRef引用计数(reference count)赋为1.引用计数用来控制COM对象的生命周期,当引用计数到0的时候,就会自动删除对象,它代表了这个COM对象的接口被引用了多少次,这也是为什么我们在使用COM接口的时候,当不需要那个接口的时候会调用Release方法,它会减少一个引用数,当接口复制给另外一个接口指针的时候,就会调用AddRef,它会增加一个引用计数。QueryInterface方法的作用就是将接口公开给调用者,同时自动调用一次AddRef,所以我们通过QueryInterface之后,我们不用调用AddRef。InterlockedIncrement(对引用数增加1),InterlockedDecrement(引用数减去1),QISearch(找到接口的偏移地址)都是帮助函数。对于IUnknown接口方法的编写了是固定的,如果说有变化,那就是QueryInterface中,一个COM对象可以支持多个接口。写法上就有点区别。
HRESULT CReaper::QueryInterface(REFIID riid, void **ppv) { static QITAB rgqit[] = { QITABENT(CReaper,IShowGeZhe), QITABENT(CRpaper,I*****),//别的接口 { 0 } }; return QISearch(this, rgqit, riid,ppv); }
现在就完成了COM接口的实现,下一篇,将讲解实现IClassFactory接口,如果要使用如CreateInstance这样的函数获取COM接口,就应该实现这个接口,这也是获取COM接口,初始化COM对象的标准方法。在完成COM组件编写的所有的过程结束之后,会把完整的项目代码附上!
本文如果出现在除CSDN或是磐实(Pansy)之外的网站,均为非法盗用。
推荐阅读地址(磐实Pansy):http://www.panshy.com/article/Sort_Desktop/other/2014-04-25/2488.php
COM专题四---实现COM接口(implement COM Interface),布布扣,bubuko.com
COM专题四---实现COM接口(implement COM Interface)
原文:http://blog.csdn.net/xinzhiyounizhiyouni/article/details/24478989