走进COM组件系列(二)
建立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组件系列(二)相关推荐
- 走进COM组件系列(三)
前面介绍了,走进COM组件系列(二),在此基础上,我们构建参数在模块之间的传递. 其他内容请浏览COM+分类 首先修改idl文件,在接口方法中加入参数 interface IY : IUnknown ...
- 走进COM组件系列(一)
说明: COM组件的接口,都需实现IUnknown接口,实现其中的AddRef,Release,QueryInterface等方法. 每一个接口都拥有自己的唯一标识符,使得其通过这个标识符能够唯一的找 ...
- 《走进git时代系列一》 你该怎么玩?
首先,这篇分享不是git命令操作大全,不是某代码托管服务的硬广, 只是希望激发仍然在使用中世纪时期版本管理系统的同学们,能够放弃你手里的SVN,转向更先进的思路. 所以,大家不会看到非常多的Comma ...
- JS组件系列——Bootstrap Table 表格行拖拽(二:多行拖拽)
原文:JS组件系列--Bootstrap Table 表格行拖拽(二:多行拖拽) 前言:前天刚写了篇JS组件系列--Bootstrap Table 表格行拖拽,今天接到新的需要,需要在之前表格行拖拽的 ...
- bootstrapr表格父子框_JS组件系列——表格组件神器:bootstrap table(二:父子表和行列调序)...
前言:上篇 JS组件系列--表格组件神器:bootstrap table 简单介绍了下Bootstrap Table的基础用法,没想到讨论还挺热烈的.有园友在评论中提到了父子表的用法,今天就结合Boo ...
- bootstrapr表格父子框_JS组件系列之Bootstrap table表格组件神器【二、父子表和行列调序】...
Bootstrap Table是轻量级的和功能丰富的以表格的形式显示的数据,支持单选,复选框,排序,分页,显示/隐藏列,固定标题滚动表,响应式设计,Ajax加载JSON数据,点击排序的列,卡片视图等. ...
- 【JS组件系列】——表格组件神器:bootstrap table(二:父子表和行列调序)
前言:上篇 JS组件系列--表格组件神器:bootstrap table 简单介绍了下Bootstrap Table的基础用法,没想到讨论还挺热烈的.有园友在评论中提到了父子表的用法,今天就结合Boo ...
- C#组件系列——又一款Excel处理神器Spire.XLS(二)
阅读目录 一.基础入门 1.新建Workbook 2.读写Workbook 3.保存Workbook 二.样式 1.文本样式 2.单元格样式 3.表格样式 4.富文本编辑框 三.冻结行列 1.冻结行 ...
- JS组件系列——Bootstrap组件福利篇:几款好用的组件推荐(二)
阅读目录 七.多值输入组件manifest 1.效果展示 2.源码说明 3.代码示例 八.文本框搜索组件bootstrap-typeahead 1.效果展示 2.源码说明 3.代码示例 九.boots ...
最新文章
- linux 检测添加磁盘空间,Linux构造磁盘空间满的测试环境
- JSONObject和JSONArray的关系
- Redis Monitor命令 - 实时打印出Redis服务器接收到的命令,调试用
- ARM Linux启动过程分析
- 光驱怎么挂载第二个光驱_重装系统下侦测不到光驱怎么解决?
- 6 MM配置-企业结构-分配-给公司代码分配采购组织
- 关于印象笔记“本笔记只能查看。它是从另一笔记创建的”
- swagger 扫描java文档_使用Javadocs生成Swagger文档
- [AHOI2006]Editor文本编辑器Splay Pascal
- Mysql 慢查询和慢查询日志分析
- 【活动报名】1024,一起过节,一起品网易/美团/贝壳/PingCAP/爱奇艺云原生实践干货!
- php免费利用飞信发送验证码,PHP 使用飞信API发送免费短信示例
- centos7 查看内存使用
- Python|报错解决|os.symlink: FileExistsError
- PTA 单链表(流浪狗收养所)
- 庖丁解牛——深入解析委托和事件
- Windows下Qt+minGW+CMake+opencv配置--方法总结、文件分享与排错分享
- 慎用Java Collection的contains函数
- excel2016中绘制多条折线的散点图
- proteus仿真micropython_用Python让单片机“行动”起来——MicroPython实战入门篇