一、
创建用以发布的接口文件
.H
1
、用
GUIDGEN
创建
CLSID
和
IID
// {96ECE846-90A6-43e2-8060-7A6A91C3D900}
DEFINE_GUID(CLSID_DBSAMPLE,
0x96ece846, 0x90a6, 0x43e2, 0x80, 0x60, 0x7a, 0x6a, 0x91, 0xc3, 0xd9, 0x0);
// {673C20AD-6B0E-4e5a-9D3D-1A1625FEC336}
DEFINE_GUID(IID_IDB,
0x673c20ad, 0x6b0e, 0x4e5a, 0x9d, 0x3d, 0x1a, 0x16, 0x25, 0xfe, 0xc3, 0x36);
// {A14C7FDE-15F0-4acb-9F2E-D021AA1E7CA2}
DEFINE_GUID(IID_IDBAccess,
0xa14c7fde, 0x15f0, 0x4acb, 0x9f, 0x2e, 0xd0, 0x21, 0xaa, 0x1e, 0x7c, 0xa2);
2
、声明接口
a)
每个接口都从
IUnknown
派生
b)
为所有的成员函数添加
_stdcall
(
COM
对象在
WIN32
下采用的标准调用约定)
class IDB : public IUnknown
{
//Interfaces
public:
//Interface for data access
virtual HRESULT _stdcall Read(short nTable,short nRow,LPWSTR lpszData)=0;
virtual HRESULT _stdcall Write(short nTable,short nRow,LPCWSTR lpszData)=0;
//Interface for database management
virtual HRESULT _stdcall Create(short &nTable,LPCWSTR lpszName)=0;
virtual HRESULT _stdcall Delete(short nTable)=0;
//Interface for database information
virtual HRESULT _stdcall GetNumTables(short &nNumTables)=0;
virtual HRESULT _stdcall GetTableName(short nTable,LPWSTR lpszName)=0;
virtual HRESULT _stdcall GetNumRows(short nTable,short &nRows)=0;
};
class IDBAccess : public IUnknown
{
public:
//Interface for data access
virtual HRESULT _stdcall Read(short nTable, short nRow, LPWSTR lpszData)=0;
virtual HRESULT _stdcall Write(short nTable, short nRow, LPCWSTR lpszData)=0;
};
3、
为了避免
GUIDs
的重复定义,使用
DEFINE_GUID
宏。该宏在
OBJBASE.H
的定义为:
#ifndef INITGUID
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
EXTERN_C const GUID FAR name
#else
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
EXTERN_C const GUID name \
= { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
#endif // INITGUID
源文件在包含接口文件
.H
文件之前必须包含
#define INITGUID,
这需要该源文件禁止预编译头文件工作,否则编译器将使用预编译头文件中的错误的宏展开。
在接口文件
.H
处,创建一个
.cpp
文件,在此文件定义
INITGUID
,并包含
OLE2.H
和接口文件
.H
#define INITGUID
#include "ole2.h"
#include "DBsrv.h"
将其加入对象工程,并禁止该文件的预编译头文件。
二、
创建对象程序
1、
创建对象程序头文件
.H
a)
包含接口文件
.H
b)
实现接口的类的声明。
i.
该类声明使用多继承,实现多接口
ii.
为了在隐式连接
DLL
并使用
COM
库加载
DLL
时,能在适当的时候卸载
DLL
。就应该让
DLL
的卸载由
COM
负责,
COM
查询
DLL
看它是否是在使用。调用
DllCanUnloadNow()
,根据该函数返回值是
S_OK
或
S_FALSE
,来判断是否可以卸载
DLL
。所以在类厂和对象都增加一个计数变量
m_dwRefCount
,同时增加一个全局变量
g_dwRefCount
,无论什么时候引用了对象指针,不管是类厂还是对象,都调用
AddRef()
增加对象的引用计数和一个记录所有的引用指针的全局变量。
DllCanUnloadNow()
只需要检查全局引用计数变量的值是否为
0
,如果为
0
就返回
S_OK
,
COM
就可以安全卸载
DLL
。
c)
类厂声明从标准类厂接口
IClassFactory
继承
d)
程序代码
#ifndef _DBSERVERIMP_INCLUDE
#define _DBSERVERIMP_INCLUDE
#include "..\Interface\DBsrv.h"
typedef long HRESULT;
class CDB : public IDB,public IDBAccess,public IDBManage,public IDBInfo
{
//Interfaces
public:
//Interface for data access
HRESULT _stdcall Read(short nTable,short nRow,LPWSTR lpszData);
HRESULT _stdcall Write(short nTable,short nRow,LPCWSTR lpszData);
//Interface for database management
HRESULT _stdcall Create(short &nTable,LPCWSTR lpszName);
HRESULT _stdcall Delete(short nTable);
//Interface for database information
HRESULT _stdcall GetNumTables(short &nNumTables);
HRESULT _stdcall GetTableName(short nTable,LPWSTR lpszName);
HRESULT _stdcall GetNumRows(short nTable,short &nRows);
HRESULT _stdcall QueryInterface(REFIID riid,void** ppObject);
ULONG _stdcall AddRef();
ULONG _stdcall Release();
//Implementation
private:
CPtrArray m_arrTables;//Array of pointers to CStringArray(the "database")
CStringArray m_arrNames;//Array of table names
ULONG m_dwRefCount;
public:
CDB();
~CDB();
};
extern ULONG g_dwRefCount;
class CDBSrvFactory : public IClassFactory
{
//Interface
public:
HRESULT _stdcall QueryInterface(REFIID riid,void** ppObject);
ULONG _stdcall AddRef();
ULONG _stdcall Release();
HRESULT _stdcall CreateInstance(IUnknown *pUnkOuter,REFIID riid,void** ppObject);
HRESULT _stdcall LockServer(BOOL fLock);
//Implementaiton
private:
ULONG m_dwRefCount;
public:
CDBSrvFactory();
};
#endif
2、
创建对象实现文件
.CPP
a)
加入
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
用来查明内存泄漏源(非必须)
b)
在对象的构造函数中,将
m_dwRefCount
初始化为
0
c)
实现
QueryInterface()
函数中,根据不同的
IID
,返回不同的接口指针
HRESULT CDB::QueryInterface(REFIID riid,void **ppObject)
{
if(riid==IID_IUnknown || riid==IID_IDB)
{
*ppObject=(IDB*)this;
}
else if(riid==IID_IDBAccess)
{
*ppObject=(IDBAccess*)this;
}
else if(riid==IID_IDBManage)
{
*ppObject=(IDBManage*)this;
}
else if(riid==IID_IDBInfo)
{
*ppObject=(IDBInfo*)this;
}
else
{
return E_NOINTERFACE;
}
AddRef();
return NO_ERROR;
}
d)
AddRef
和
Release
函数
ULONG CDB::AddRef()
{
g_dwRefCount++;
m_dwRefCount++;
return m_dwRefCount;
}
ULONG CDB::Release()
{
g_dwRefCount--;
m_dwRefCount--;
if(m_dwRefCount==0)
{
delete this;
return 0;
}
return m_dwRefCount;
}
3、
创建类厂的实现文件
.CPP
a)
初始化
g_dwRefCount
b)
定义类厂的实现函数
c)
定义被引出的几个函数
DllGetClassObject
、
DllCanUnloadNow
、
DllRegisterServer
、
DllUnregisterServer
WINDOWS
系统提供的用于注册的实用工具
RegSvr32
调用
DLL
的
DllRegisterServer
或
DllUnregisterServer
进行对象的注册。
#include "stdafx.h"
#include "DBSrvImp.h"
ULONG g_dwRefCount=0;
HRESULT CDBSrvFactory::CreateInstance(IUnknown *pUnkOuter,REFIID riid,void **ppObject)
{
if(pUnkOuter!=NULL)
{
return CLASS_E_NOAGGREGATION;
}
CDB *pDB=new CDB;
if(FAILED(pDB->QueryInterface(riid,ppObject)))
{
delete pDB;
*ppObject=NULL;
return E_NOINTERFACE;
}
return NO_ERROR;
}
ULONG CDBSrvFactory::Release()
{
g_dwRefCount--;
m_dwRefCount--;
if(m_dwRefCount==0)
{
delete this;
return 0;
}
return m_dwRefCount;
}
STDAPI DllGetClassObject(REFCLSID rclsid,REFIID riid,void** ppObject)
{
if(rclsid==CLSID_DBSAMPLE)
{
CDBSrvFactory *pFactory= new CDBSrvFactory;
if(FAILED(pFactory->QueryInterface(riid,ppObject)))
{
delete pFactory;
*ppObject=NULL;
return E_INVALIDARG;
}
}
else
{
return CLASS_E_CLASSNOTAVAILABLE;
}
return NO_ERROR;
}
HRESULT CDBSrvFactory::LockServer(BOOL fLock)
{
if(fLock)
{
g_dwRefCount++;
}
else
{
g_dwRefCount--;
}
return NO_ERROR;
}
CDBSrvFactory::CDBSrvFactory()
{
m_dwRefCount=0;
}
HRESULT CDBSrvFactory::QueryInterface(REFIID riid,void **ppObject)
{
if(riid==IID_IUnknown || riid==IID_IClassFactory)
{
*ppObject=(IDB*)this;
}
else
{
return E_NOINTERFACE;
}
AddRef();
return NO_ERROR;
}
ULONG CDBSrvFactory::AddRef()
{
g_dwRefCount++;
m_dwRefCount++;
return m_dwRefCount;
}
HRESULT _stdcall DllCanUnloadNow()
{
if(g_dwRefCount)
{
return S_FALSE;
}
else
{
return S_OK;
}
}
STDAPI DllRegisterServer(void)
{
HKEY hKeyCLSID,hKeyInproc32;
DWORD dwDisposition;
if(RegCreateKeyEx(HKEY_CLASSES_ROOT,_T("CLSID\\{96ECE846-90A6-43e2-8060-7A6A91C3D900}"),NULL,_T(""),REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,
&hKeyCLSID,&dwDisposition)!=ERROR_SUCCESS)
{
return E_UNEXPECTED;
}
if(RegSetValueEx(hKeyCLSID,_T(""),NULL,REG_SZ,(BYTE*)_T("DB Sample Server"),
sizeof(_T("DB Sample Server")))!=ERROR_SUCCESS)
{
RegCloseKey(hKeyCLSID);
return E_UNEXPECTED;
}
if(RegCreateKeyEx(hKeyCLSID,_T("InprocServer32"),NULL,_T(""),REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,NULL,&hKeyInproc32,&dwDisposition)!=ERROR_SUCCESS)
{
RegCloseKey(hKeyCLSID);
return E_UNEXPECTED;
}
HMODULE hModule=GetModuleHandle(_T("DB.DLL"));
if(!hModule)
{
RegCloseKey(hKeyInproc32);
RegCloseKey(hKeyCLSID);
return E_UNEXPECTED;
}
TCHAR szName[MAX_PATH+1];
if(GetModuleFileName(hModule,szName,sizeof(szName))==0)
{
RegCloseKey(hKeyCLSID);
return E_UNEXPECTED;
}
if(RegSetValueEx(hKeyInproc32,_T(""),NULL,REG_SZ,(BYTE*)szName,sizeof(TCHAR)*(lstrlen(szName)+1))!=ERROR_SUCCESS)
{
RegCloseKey(hKeyInproc32);
RegCloseKey(hKeyCLSID);
return E_UNEXPECTED;
}
RegCloseKey(hKeyInproc32);
RegCloseKey(hKeyCLSID);
return NO_ERROR;
}
STDAPI DllUnregisterServer(void)
{
if(RegDeleteKey(HKEY_CLASSES_ROOT,
_T("CLSID\\{96ECE846-90A6-43e2-8060-7A6A91C3D900}\\InprocServer32"))!=ERROR_SUCCESS)
{
return E_UNEXPECTED;
}
if (RegDeleteKey(HKEY_CLASSES_ROOT,
_T("CLSID\\{96ECE846-90A6-43e2-8060-7A6A91C3D900}"))!=ERROR_SUCCESS)
{
return E_UNEXPECTED;
}
return NO_ERROR;
}
4、
创建模块定义文件
.DEF
a)
引出在类厂的实现文件
.CPP
中定义的几个被引出函数
b)
文件内容
EXPORTS
;WEP @1 RESIDENTNAME
DllGetClassObject
DllCanUnloadNow
DllRegisterServer
DllUnregisterServer
c)
加入工程
5、
修改
StdAfx.h
文件
a)
在所有
include
前加入
#define _AFX_NO_BSTR_SUPPORT
b)
#include <ole2.h>
6、
将定义
INITGUID
的
.CPP
文件加入工程,禁止其预编译头文件
7、
编译工程,并用
REGSVR32
注册
三、
客户端程序使用
COM
1、
Include
发布的接口文件
.H
2、
将伴随着接口文件
.H
的
GUIDS.CPP
文件添加入工程
3、
在头文件定义
IUnknown
类型的接口指针变量,用于访问对象
IUnknown* m_pDB;
4、
调用
COM
库函数
CoGetClassObject
装载
DLL
,返回一个类厂的指针
pDBFactory
5、
通过
pDBFactory
调用
CreateInstance
返回
IUnknown
的对象指针
m_pDB
6、
pDBFactory->Release()
7、
通过
m_pDB->QueryInterface
访问各个接口,返回该接口的指针,可以通过该指针使用该接口提高的功能。使用完毕后
Release
该指针
8、
初始化
COM
,在
App
的
InitInstance
函数开始处调用
CoInitialize
,在
ExitInstance
调用
CoUninitialize
9、
用
ClassWizard
为
App
重载
OnIdle
函数,调用
CoFreeUnusedLibraries
,卸载无用的对象
10、
编译运行
11、
部分程序
BOOL CDBDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
//create database object
IClassFactory *pDBFactory=NULL;
HRESULT hRes;
hRes=CoGetClassObject(CLSID_DBSAMPLE,CLSCTX_SERVER,NULL,IID_IClassFactory,(void**)&pDBFactory);
if(FAILED(hRes))
{
CString csError;
csError.Format(_T("Error %x obtaining class factory for DB Object!"),hRes);
AfxMessageBox(csError);
return FALSE;
}
hRes=pDBFactory->CreateInstance(NULL,IID_IUnknown,(void**)&m_pDB);
if(FAILED(hRes))
{
CString csError;
csError.Format(_T("Error %x creating DB Object!"),hRes);
AfxMessageBox(csError);
return FALSE;
}
pDBFactory->Release();
//initialization
m_csData="No data yet!";
m_nCount=0;
m_nTable=-1;
return TRUE;
}
void CDBDoc::OnDbCreate()
{
IDBManage* pManage=NULL;
if(FAILED(m_pDB->QueryInterface(IID_IDBManage,(void**)&pManage)))
{
AfxMessageBox(_T("Error in QueryInterface for IDBManage!"));
return;
}
pManage->Create(m_nTable,L"Testing");
m_nCount=0;//set number of writes to 0
pManage->Release();
}
BOOL CDBApp::InitInstance()
{
if(FAILED(CoInitialize(NULL)))
{
AfxMessageBox(_T("Could not initialize COM Libraries!"));
return FALSE;
}
……….
}
int CDBApp::ExitInstance()
{
CoUninitialize();
return CWinApp::ExitInstance();
}
BOOL CDBApp::OnIdle(LONG lCount)
{
return CWinApp::OnIdle(lCount);
CoFreeUnusedLibraries();
return FALSE;
}
点击下载代码
posted on 2006-03-16 15:48
苍羽 阅读(574)
评论(0) 编辑 收藏 引用 所属分类:
读书笔记