建立COM组件服务器
其他内容请浏览COM+分类
首先看下我们的目录结构:生成DLL的Project
这一节需要我们自己定义接口(发布接口),利用MIDL 接口定义语言
COM服务器的三个关键要求:
接口:客户机通过接口与服务器进行通信;
组件类:提供所定义接口的实现方法;
类型库:编译的IDL文件向支持的COM环境传送接口信息。
首先在 vs中 建立IDL 文件:
代码如下:
import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(19900225-0700-0000-0000-000000000001) ] interface IY : IUnknown { HRESULT Fy(); }; [ object, uuid(19900225-0800-0000-0000-000000000001) ] interface IZ : IUnknown { HRESULT Fz(); }; [ uuid(19900225-0900-0000-0000-000000000001), version(1.0) ] library CBLib { importlib("stdole32.tlb"); [ uuid(19900225-0a00-0000-0000-000000000001) ] coclass CB { [default] interface IY; [source] interface IZ; }; };
这里需要产生四个GUID,分别对应两个接口ID(IID),类型库ID(LIBID)和CoClassID(CLSID)。
右键点击该文件,选择编译,生成三个文件,
将 除了划红线的文件, 剩下两个文件加入编译器中,创建Generated文件夹。
注: 若加入划红线的文件,将会产生 很多关于DLL的编译错误。
在自动生成的文件CB_i.c 中,我们可以看到系统已经帮我们生成了组件和接口的CSLID。
在CB_h.h 中可以看到,已经发布的接口和抽象方法
接下来我们需要实现组件的相关方法和类工厂:
组件类: 实现Fy,Fz,QueryInterface,AddRef,Release方法
class CB: public IY, public IZ{ public: // IUnknown implementation virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release(); virtual HRESULT __stdcall Fy(); virtual HRESULT __stdcall Fz(); CB(); ~CB(); private: long m_cRef; };
类工厂: 需要继承IClassFactory
class CFactory : public IClassFactory { public: virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release(); virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& IID, void** ppv); virtual HRESULT __stdcall LockServer(BOOL block); CFactory() : m_cRef(1) {} ~CFactory() { trace("class CFactory: destroyed..."); } private: long m_cRef; };
然而,我们仍然需要将其称为合法的DLL COM服务器,要实现的函数包括
DllMain,DllGetClassObject,DllCanUnloadNow,DllRegisterServer,DllUnRegisterServer,
这些都是API式函数,是直接调用,而不是通过COM接口调用。
实现如下:
STDAPI DllCanUnloadNow() { if((g_cComponents == 0) && (g_cServerLock == 0)) { return S_OK; }else { return S_FALSE; } } STDAPI DllGetClassObject(const IID &clsid, const IID &riid, void** ppv) { trace("DllGetClassObject: create class factory"); if(clsid != CLSID_CB) { return CLASS_E_CLASSNOTAVAILABLE; } CFactory* pFactory = new CFactory; if(pFactory == NULL) { return E_OUTOFMEMORY; } HRESULT hr = pFactory->QueryInterface(riid, ppv); pFactory->Release(); return hr; } STDAPI DllRegisterServer() { return S_OK; } STDAPI DllUnregisterServer() { return S_OK; } BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved) { myHinstance = (HINSTANCE)hinstDLL; return TRUE; }
值得注意的是DllGetClassObject 这个函数,有了这个方法才能在客户端与服务器端的通讯中得到类工厂。
并在CB.DEF中,定义DLL输出:
LIBRARY "ComTestOne" EXPORTS DllCanUnloadNow PRIVATE DllGetClassObject PRIVATE DllRegisterServer PRIVATE DllUnregisterServer PRIVATE
最后,我们可以手动添加注册表,在注册表编辑器中我们添加组件的CSLID和dll 文件名:
至此,我们完成了服务器端。
在客户端,我们仅仅需要做的就是连接组件,在组件中得到接口进行调用即可:
int main() { CoInitialize(NULL); IY* iy = NULL; HRESULT hr = CoCreateInstance(CLSID_CB, NULL, CLSCTX_INPROC_SERVER, IID_IY, (void**)&iy); if(FAILED(hr)) { MessageBox(NULL, L"could not create instance", L"hello", MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2); } else { iy->Fy(); iy->Release(); } cout << "--------------------------------------------------" << endl; IY* iy1 = NULL; HRESULT hr2 = CoCreateInstance(CLSID_CB, NULL, CLSCTX_INPROC_SERVER, IID_IY, (void**)&iy1); if(FAILED(hr2)) { MessageBox(NULL, L"could not create instance", L"hello", MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2); } else { iy1->Fy(); iy1->Release(); } CoUninitialize(); getchar(); return 0; }
CoCreateInstance(CLSID_CB, NULL, CLSCTX_INPROC_SERVER, IID_IY, (void**)&iy);
是最为重要的,我们必须了解其内部的工作原理,请参考:
http://benworld.iteye.com/blog/1988445
另外,需要把生成的ComTestOne.dll文件 拷贝到 客户端的DEBUG目录下。
运行效果如下:
在构建第一个实例时,会有划红线的重复现象出现,而构建第二个时则没有。
上传了 项目实例代码供参考。
附代码:CB.cpp
#include "CB.h" #include <iostream> static long g_cServerLock = 0; static long g_cComponents = 0; static HINSTANCE myHinstance; CB::CB() : m_cRef(1){ InterlockedIncrement(&g_cComponents); } CB::~CB() { InterlockedDecrement(&g_cComponents); trace("class CB: destroyed..."); } HRESULT __stdcall CB::QueryInterface(const IID& iid, void** ppv) { if(iid == IID_IUnknown){ trace("QueryInterface: Return pointer to IUnknown"); *ppv = static_cast<IY *>(this); } else if(iid == IID_IY) { trace("QueryInterface: Return pointer to IY"); *ppv = static_cast<IY *>(this); } else if(iid == IID_IZ) { trace("QueryInterface: Return pointer to IZ"); *ppv = static_cast<IZ *>(this); } else { trace("QueryInterface: Return pointer to IUnknown"); *ppv = NULL; return E_NOINTERFACE; } reinterpret_cast<IUnknown *> (*ppv)->AddRef(); return S_OK; } ULONG __stdcall CB::AddRef() { std::cout << "CB: m_Ref + 1 " << std::endl; return InterlockedIncrement(&m_cRef); } ULONG __stdcall CB::Release() { std::cout << "CB: m_Ref - 1 " << std::endl; if(InterlockedDecrement(&m_cRef) == 0) { delete this; return 0; } return m_cRef; } HRESULT __stdcall CB::Fy() { std::cout << "Fy" << std::endl; return S_OK; } HRESULT __stdcall CB::Fz() { std::cout << "Fz" << std::endl; return S_OK; } // class Factory IUnknown implementation HRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv) { if((iid == IID_IUnknown) || (iid == IID_IClassFactory)) { // 将CFactory 转化为 ClassFactory *ppv = static_cast<IClassFactory*>(this); } else { *ppv = NULL; return E_NOINTERFACE; } reinterpret_cast<IUnknown*>(*ppv)->AddRef(); return S_OK; } ULONG __stdcall CFactory::AddRef() { std::cout << "CFactory: m_Ref + 1 " << std::endl; return InterlockedIncrement(&m_cRef); } ULONG __stdcall CFactory::Release() { std::cout << "CFactory: m_Ref - 1 " << std::endl; if(InterlockedDecrement(&m_cRef) == 0) { delete this; return 0; } return m_cRef; } HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter, const IID& IID, void** ppv) { trace("class factory : create component"); if(pUnknownOuter != NULL) { return CLASS_E_NOAGGREGATION; } CB * pa = new CB; if(pa == NULL) { return E_OUTOFMEMORY; } HRESULT hr = pa->QueryInterface(IID, ppv); pa->Release(); return hr; } HRESULT __stdcall CFactory::LockServer(BOOL bLock) { if(bLock) { InterlockedIncrement(&g_cServerLock); } else { InterlockedDecrement(&g_cServerLock); } return S_OK; } STDAPI DllCanUnloadNow() { if((g_cComponents == 0) && (g_cServerLock == 0)) { return S_OK; }else { return S_FALSE; } } STDAPI DllGetClassObject(const IID &clsid, const IID &riid, void** ppv) { trace("DllGetClassObject: create class factory"); if(clsid != CLSID_CB) { return CLASS_E_CLASSNOTAVAILABLE; } CFactory* pFactory = new CFactory; if(pFactory == NULL) { return E_OUTOFMEMORY; } HRESULT hr = pFactory->QueryInterface(riid, ppv); pFactory->Release(); return hr; } STDAPI DllRegisterServer() { return S_OK; } STDAPI DllUnregisterServer() { return S_OK; } BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved) { myHinstance = (HINSTANCE)hinstDLL; return TRUE; }