新建项目>>Win32项目>应用程序类型选择DLL 勾选空项目
然后首先新建一个idl文件,我们取名为 MyCom.idl
MyCom.idl文件里删掉向导给我们创建的两行代码。并在里面写入:
import "unknwn.idl"; [object, uuid("288220D0-1677-4B57-B8FA-C71DA81E7C34")] interface IDog :IUnknown{ HRESULT Bark([in, out]long* plong); }; [object, uuid("637C3E42-173F-4AEF-B3B7-DA9218AFD166")] interface IPig :IUnknown{ HRESULT Heng([in, out]long* plong); }; [object, uuid("E02418EC-FCFB-489B-8D4C-5B186E4AEDEE")] interface IMonkey :IUnknown{ HRESULT Zhi([in, out]long* plong); }; [uuid(9D3931EB-019C-441D-B6C6-0477600DDF02),]//这个com组件的CLSID就是这个 coclass MyCom //定义一个com组件 组件的名字叫MyCom { interface IMonkey; interface IPig; interface IDog; };
以上代码 我们定义了三个接口 分别是 狗,猪,猴 对应的都有一个叫声的方法: bark,heng,zhi
每个接口里都有一个 object和uuid 这两个属性是每个接口必须要有的
接口的uuid 可以在vs里点击工具>>创建GUID来获取
每个接口都继承了IUnknown接口
然后点击生成 会自动给我们生成一些文件,我们把Mycom_h.h和MyCom_i.c分别引入到项目中
然后 再新建一个MyCom.cpp 和 MyCom.h
MyCom.h里写入接口对应的函数声明:
#include"MyCom_h.h" EXTERN_C const CLSID CLSID_MyCom; class MyCom :public IDog, public IMonkey, public IPig { long m_Count; public: MyCom(); virtual HRESULT STDMETHODCALLTYPE QueryInterface( /* [in] */ REFIID riid, /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR *__RPC_FAR *ppvObject); virtual ULONG STDMETHODCALLTYPE AddRef(void); virtual ULONG STDMETHODCALLTYPE Release(void); virtual HRESULT STDMETHODCALLTYPE Bark( /* [out][in] */ long *plong); virtual HRESULT STDMETHODCALLTYPE Heng( /* [out][in] */ long *plong); virtual HRESULT STDMETHODCALLTYPE Zhi( /* [out][in] */ long *plong); //long MyTest(); };
MyCom.cpp 里写实现
#include"MyCom.h" MyCom _MyCom; MyCom::MyCom() :m_Count(0){}; HRESULT STDMETHODCALLTYPE MyCom::QueryInterface(REFIID riid, void**ppvObject)//查询接口的函数 { if (!ppvObject) return E_POINTER; *ppvObject = NULL; if (riid == IID_IDog)//判断传入的riid是不是dog的 { AddRef();//如果是 计数加一 *ppvObject = dynamic_cast<IDog*>(this); //并把狗的 函数指针的数组 返回去(类实际上就是一堆函数指针数组) } else if (riid == IID_IPig)//判断传入的riid是不是pig的 { AddRef();//如果是 计数加一 *ppvObject = dynamic_cast<IPig*>(this);//并把猪的 函数指针的数组 返回去(类实际上就是一堆函数指针数组) } else if (riid == IID_IMonkey)//判断传入的riid是不是Monkey的 { AddRef();//如果是 计数加一 *ppvObject = dynamic_cast<IMonkey*>(this);//并把猴的 函数指针的数组 返回去(类实际上就是一堆函数指针数组) } if (*ppvObject == NULL)//如果传入的iid 不是我们已实现了的接口 { return E_NOINTERFACE;//返回一个错误 表示没有这个接口 } return S_OK; } ULONG STDMETHODCALLTYPE MyCom::AddRef(void)//计数器 增加的函数 { InterlockedIncrement(&m_Count); //InterlockedIncrement :原子递增 return m_Count; } ULONG STDMETHODCALLTYPE MyCom::Release(void) { InterlockedDecrement(&m_Count); //if(m_Count==0) //delete this; //如果类实例作为全局变量,那么显然就不能这么释放这个类实例了。应该怎么释放呢? //如果是全局变量,要释放这个类实例,只能把这个dll从进程的地址空间中拿掉、 return m_Count; } HRESULT STDMETHODCALLTYPE MyCom::Bark( /* [out][in] */ long *plong) { MessageBox(NULL, L"Dog", NULL, MB_OK); return S_OK; } HRESULT STDMETHODCALLTYPE MyCom::Heng( /* [out][in] */ long *plong) { MessageBox(NULL, L"Pig", NULL, MB_OK); return S_OK; } HRESULT STDMETHODCALLTYPE MyCom::Zhi( /* [out][in] */ long *plong) { MessageBox(NULL, L"Monkey", NULL, MB_OK); return S_OK; } //CoGetClassObject //由于一个dll 可能有多个com组件 所以这里会根据com组件的CLSID和接口的IID来查询接口 //本示例程序里 只有一个com组件 STDAPI DllGetClassObject(const CLSID &rclsid, const IID &riid, void **ppv) { if (rclsid == CLSID_MyCom) { return _MyCom.QueryInterface(riid, ppv); } else { return S_FALSE; } }
再在源文件里增加一个.def文件 文件名默认Source.def就好
里面写入: //后面的 PRIVATE 意思是不要静态装入的意思
LIBRARY EXPORTS DllGetClassObject PRIVATE
然后就可以编译了,此时虽然可以编译成功,但是并不是一个真正的com组件,要想变成真正的com组件还需要继续往下看
当程序要调用某个com组件的时候,会通过ole32.cll的一个服务来查询这个组件的所在位置(根据注册表来查)
并把这个com的cll装载到程序的内存中, 然后调用一个叫做DllGetClassObject的函数
未完待续