建立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 implementationvirtual 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"EXPORTSDllCanUnloadNow        PRIVATEDllGetClassObject    PRIVATEDllRegisterServer    PRIVATEDllUnregisterServer  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组件系列(二)相关推荐

  1. 走进COM组件系列(三)

    前面介绍了,走进COM组件系列(二),在此基础上,我们构建参数在模块之间的传递. 其他内容请浏览COM+分类 首先修改idl文件,在接口方法中加入参数 interface IY : IUnknown ...

  2. 走进COM组件系列(一)

    说明: COM组件的接口,都需实现IUnknown接口,实现其中的AddRef,Release,QueryInterface等方法. 每一个接口都拥有自己的唯一标识符,使得其通过这个标识符能够唯一的找 ...

  3. 《走进git时代系列一》 你该怎么玩?

    首先,这篇分享不是git命令操作大全,不是某代码托管服务的硬广, 只是希望激发仍然在使用中世纪时期版本管理系统的同学们,能够放弃你手里的SVN,转向更先进的思路. 所以,大家不会看到非常多的Comma ...

  4. JS组件系列——Bootstrap Table 表格行拖拽(二:多行拖拽)

    原文:JS组件系列--Bootstrap Table 表格行拖拽(二:多行拖拽) 前言:前天刚写了篇JS组件系列--Bootstrap Table 表格行拖拽,今天接到新的需要,需要在之前表格行拖拽的 ...

  5. bootstrapr表格父子框_JS组件系列——表格组件神器:bootstrap table(二:父子表和行列调序)...

    前言:上篇 JS组件系列--表格组件神器:bootstrap table 简单介绍了下Bootstrap Table的基础用法,没想到讨论还挺热烈的.有园友在评论中提到了父子表的用法,今天就结合Boo ...

  6. bootstrapr表格父子框_JS组件系列之Bootstrap table表格组件神器【二、父子表和行列调序】...

    Bootstrap Table是轻量级的和功能丰富的以表格的形式显示的数据,支持单选,复选框,排序,分页,显示/隐藏列,固定标题滚动表,响应式设计,Ajax加载JSON数据,点击排序的列,卡片视图等. ...

  7. 【JS组件系列】——表格组件神器:bootstrap table(二:父子表和行列调序)

    前言:上篇 JS组件系列--表格组件神器:bootstrap table 简单介绍了下Bootstrap Table的基础用法,没想到讨论还挺热烈的.有园友在评论中提到了父子表的用法,今天就结合Boo ...

  8. C#组件系列——又一款Excel处理神器Spire.XLS(二)

    阅读目录 一.基础入门 1.新建Workbook 2.读写Workbook 3.保存Workbook 二.样式 1.文本样式 2.单元格样式 3.表格样式 4.富文本编辑框 三.冻结行列 1.冻结行 ...

  9. JS组件系列——Bootstrap组件福利篇:几款好用的组件推荐(二)

    阅读目录 七.多值输入组件manifest 1.效果展示 2.源码说明 3.代码示例 八.文本框搜索组件bootstrap-typeahead 1.效果展示 2.源码说明 3.代码示例 九.boots ...

最新文章

  1. linux 检测添加磁盘空间,Linux构造磁盘空间满的测试环境
  2. JSONObject和JSONArray的关系
  3. Redis Monitor命令 - 实时打印出Redis服务器接收到的命令,调试用
  4. ARM Linux启动过程分析
  5. 光驱怎么挂载第二个光驱_重装系统下侦测不到光驱怎么解决?
  6. 6 MM配置-企业结构-分配-给公司代码分配采购组织
  7. 关于印象笔记“本笔记只能查看。它是从另一笔记创建的”
  8. swagger 扫描java文档_使用Javadocs生成Swagger文档
  9. [AHOI2006]Editor文本编辑器Splay Pascal
  10. Mysql 慢查询和慢查询日志分析
  11. 【活动报名】1024,一起过节,一起品网易/美团/贝壳/PingCAP/爱奇艺云原生实践干货!
  12. php免费利用飞信发送验证码,PHP 使用飞信API发送免费短信示例
  13. centos7 查看内存使用
  14. Python|报错解决|os.symlink: FileExistsError
  15. PTA 单链表(流浪狗收养所)
  16. 庖丁解牛——深入解析委托和事件
  17. Windows下Qt+minGW+CMake+opencv配置--方法总结、文件分享与排错分享
  18. 慎用Java Collection的contains函数
  19. excel2016中绘制多条折线的散点图
  20. proteus仿真micropython_用Python让单片机“行动”起来——MicroPython实战入门篇

热门文章

  1. 安装配置 TensorFlow on Android
  2. 2021高通人工智能创新大赛垃圾分类赛题第五次研讨会
  3. pytorch中的卷积操作详解
  4. 欧洲与北美5G开战,最后的赢家却是高通?
  5. 移动端:判断是否微信端、判断手机操作系统(ios或android)
  6. iOS10 CAAnimationDelegate适配引申到条件编译
  7. Lync 小技巧-56-检查你用的公网证书是否正确
  8. openssl,加密,解密,https
  9. Tomato多拨脚本
  10. 实现企业员工外出登记(二)