建立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;
}