`

走进COM组件系列(二)

    博客分类:
  • 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 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;
}

 

  • 大小: 11.1 KB
  • 大小: 1.6 KB
  • 大小: 5.8 KB
  • 大小: 3 KB
  • 大小: 33.5 KB
  • 大小: 44.6 KB
0
0
分享到:
评论

相关推荐

    组件编程系列组件编程系列

    组件编程系列组件编程系列组件编程系列组件编程系列

    COM组件开发COM组件开发

    COM组件开发COM组件开发

    com组件学习com组件学习

    com组件学习com组件学习

    COM 组件设计与应用

    COM 组件设计与应用(二)——GUID 和 接口 COM 组件设计与应用(三)——数据类型 COM 组件设计与应用(四)——简单调用组件 COM 组件设计与应用(五)——用 ATL 写第一个组件 COM 组件设计与应用(六)——用 ...

    VC++ COM组件开发相关实例

    2.1 组件 - 实际是一些可以执行的二进 制程序,它可以给其他的应用程序、操 作系统或其他组件提供功能 2.2 优点 2.2.1 可以方便的提供软件定制机制 2.2.2 可以很灵活的提供功能 2.2.3 可以很方便的实现程序的...

    免注册调用COM组件方法二-Assembly Manifests

    众所周知,通过Assembly Manifests,我们可以免注册调用各种进程内的COM组件,包括DLL组件和OCX控件等。这里提供一个用于快速创建Assembly Manifest文件的工具。这个工具本身就是通过Assembly Manifests来免注册调用...

    WPF调用COM组件

    本实例包括一个c++写的COM组件,一个WPF工程,其中WPF调用该COM组件中的接口函数,从而实现C#调用C++。

    Linux下Com组件例子

    Linux 下C++ Com组件demo

    C语言实现COM组件

    简单使用纯C语言实现COM组件,帮助理解COM实现机制

    CB调用COM组件

    CB调用COM组件 COM BCB 调用CB调用COM组件 COM BCB 调用

    com组件注册查看器

    查看器 查看电脑中注册了那些组件 枚举了所有的组件注册信息

    com组件简单实现和调用

    com组件简单实现和调用 com组件实例 VC6.0先创建com组件

    COM组件(详细讲解)

    COM组件的详解集合。 包括:VC6.0 创建COM组件,.NET(C#)创建的COM组件,ATL编写COM组件 使用VB6.0调用, 使用VC6.0调用, 使用.Net(VB)调用, 使用.Net(C#)调用, COM和DLL的区别。 您一定可以通过它学会COM...

    com组件转易语言

    com组件转易语言

    COM组件编写和使用示例

    vs2008包含两个工程:Com工程,创建Com组件; Client工程,使用Com组件 Client目录: 示例如何使用COM组件 Com目录: 示例如何创建COM组件 Debug目录: REGISTER.BAT 注册com组件 UnREGISTER.BAT 反注册Com...

    COM 组件注册方法

    有两种方式注册组件: 一种是调用regsvr32.exe: ... 逆@风@者 ...MTS是值得推荐的,因为...1、动态卸载平衡,提高组件和基于组件的应用程序的升级性。 2、包含公布和提交事件和队列组件的能力,使得更容易与多个组件联合。

    COM组件调用.rar

    COM组件调用.rarCOM组件调用.rarCOM组件调用.rar

    光伏组件技术系列报告之一:组件技术方兴未艾,谁能破茧成蝶?.pdf

    光伏组件技术系列报告之一:组件技术方兴未艾,谁能破茧成蝶?.pdf

    COM组件设计与应用

    COM组件设计与应用 COM组件设计与应用 COM组件设计与应用

Global site tag (gtag.js) - Google Analytics