随笔-5  评论-3  文章-2  trackbacks-0

 

 

一、 创建用以发布的接口文件 .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)  编辑 收藏 引用 所属分类: 读书笔记

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理