建立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; }
相关推荐
组件编程系列组件编程系列组件编程系列组件编程系列
COM组件开发COM组件开发
com组件学习com组件学习
COM 组件设计与应用(二)——GUID 和 接口 COM 组件设计与应用(三)——数据类型 COM 组件设计与应用(四)——简单调用组件 COM 组件设计与应用(五)——用 ATL 写第一个组件 COM 组件设计与应用(六)——用 ...
2.1 组件 - 实际是一些可以执行的二进 制程序,它可以给其他的应用程序、操 作系统或其他组件提供功能 2.2 优点 2.2.1 可以方便的提供软件定制机制 2.2.2 可以很灵活的提供功能 2.2.3 可以很方便的实现程序的...
众所周知,通过Assembly Manifests,我们可以免注册调用各种进程内的COM组件,包括DLL组件和OCX控件等。这里提供一个用于快速创建Assembly Manifest文件的工具。这个工具本身就是通过Assembly Manifests来免注册调用...
本实例包括一个c++写的COM组件,一个WPF工程,其中WPF调用该COM组件中的接口函数,从而实现C#调用C++。
Linux 下C++ Com组件demo
简单使用纯C语言实现COM组件,帮助理解COM实现机制
CB调用COM组件 COM BCB 调用CB调用COM组件 COM BCB 调用
查看器 查看电脑中注册了那些组件 枚举了所有的组件注册信息
com组件简单实现和调用 com组件实例 VC6.0先创建com组件
COM组件的详解集合。 包括:VC6.0 创建COM组件,.NET(C#)创建的COM组件,ATL编写COM组件 使用VB6.0调用, 使用VC6.0调用, 使用.Net(VB)调用, 使用.Net(C#)调用, COM和DLL的区别。 您一定可以通过它学会COM...
com组件转易语言
vs2008包含两个工程:Com工程,创建Com组件; Client工程,使用Com组件 Client目录: 示例如何使用COM组件 Com目录: 示例如何创建COM组件 Debug目录: REGISTER.BAT 注册com组件 UnREGISTER.BAT 反注册Com...
有两种方式注册组件: 一种是调用regsvr32.exe: ... 逆@风@者 ...MTS是值得推荐的,因为...1、动态卸载平衡,提高组件和基于组件的应用程序的升级性。 2、包含公布和提交事件和队列组件的能力,使得更容易与多个组件联合。
COM组件调用.rarCOM组件调用.rarCOM组件调用.rar
光伏组件技术系列报告之一:组件技术方兴未艾,谁能破茧成蝶?.pdf
COM组件设计与应用 COM组件设计与应用 COM组件设计与应用