COM组件开发实践(四)---From C++ to COM :Part 1

源代码下载 – 74kb

,C++客户重用C++对象

假设已经有一个可以重用的类,我们就可以在自己的程序中去重用它,只需要将其定义和实现文件加入到我们自己的工程中,并且在使用它的文件中包含此类的定义文件就可以了,这也是我们最常用的C++标准重用方法。就拿我自己来说,在CodeProject上遇到比较好的控件代码,都是这样直接用到自己的项目中来的。

下面就给出我这个系列的第一个代码示例,在接下来的几篇文章中,将基于此代码不断进行改进,一步步从C++走向COM.

简单介绍下我们要重用的C++对象,它是一个简单的类似数据库的对象,用来管理内存中的数据,它包含一个指向数据库中所有表的指针数组,表实际是一个字符串数组,每个数组元素表示表格的一行。另外这个类还包含有一个数据表表名的数组。

DBSRV.h文件:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> typedef long HRESULT; // 模拟COM中的HRESULT

// 内存数据库类
class CDB
{
// Interfaces
public :
// Interfacefordataaccess
HRESULTRead( short nTable, short nRow,LPTSTRlpszData); // 读数据,nTable指定数据表,nRow指定数据行
HRESULTWrite( short nTable, short nRow,LPCTSTRlpszData); // 写数据,nTable指定数据表,nRow指定数据行
// Interfacefordatabasemanagement
HRESULTCreate( short & nTable,LPCTSTRlpszName); // 创建数据表,表名为lpszName
HRESULTDelete( short nTable); // 删除数据表
// Interfacefordatabaseinformation
HRESULTGetNumTables( short & nNumTables); // 获取数据表个数
HRESULTGetTableName( short nTable,LPTSTRlpszName); // 获取指定数据表表名,nTable为数据表索引号
HRESULTGetNumRows( short nTable, short & nRows); // 获取指定数据表的数据行数,nTable为数据表索引号,nRows保存返回的行数
// Implementation
private :
CPtrArraym_arrTables;
// 指向“数据库”中所有表的指针数组
CStringArraym_arrNames; // 数据表名称数组
public :
~ CDB();
};

DBSRV.cpp文件:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> #include " stdafx.h "
#include
" ../Interface/DBsrv.h "
// Databaseobject
HRESULTCDB::Read( short nTable, short nRow,LPTSTRlpszData)
{
CStringArray
* pTable;
pTable
= (CStringArray * )m_arrTables[nTable];
lstrcpy(lpszData,(
* pTable)[nRow]);
return NO_ERROR;
}

HRESULTCDB::Write(
short nTable, short nRow,LPCTSTRlpszData)
{
CStringArray
* pTable;
pTable
= (CStringArray * )m_arrTables[nTable];
pTable
-> SetAtGrow(nRow,lpszData);
return NO_ERROR;
}
HRESULTCDB::Create(
short & nTable,LPCTSTRlpszName)
{
CStringArray
* pTable = new CStringArray;
nTable
= m_arrTables.Add(pTable);
m_arrNames.SetAtGrow(nTable,lpszName);
return NO_ERROR;
}
HRESULTCDB::Delete(
short nTable)
{
CStringArray
* pTable;
pTable
= (CStringArray * )m_arrTables[nTable];
deletepTable;
m_arrTables[nTable]
= NULL;
m_arrNames[nTable]
= "" ;
if (nTable == m_arrTables.GetSize() - 1 )
{
m_arrTables.RemoveAt(nTable);
m_arrNames.RemoveAt(nTable);
}
return NO_ERROR;
}
HRESULTCDB::GetNumTables(
short & nNumTables)
{
nNumTables
= m_arrTables.GetSize();
return NOERROR;
}

HRESULTCDB::GetTableName(
short nTable,LPTSTRlpszName)
{
lstrcpy(lpszName,m_arrNames[nTable]);
return NO_ERROR;
}
HRESULTCDB::GetNumRows(
short nTable, short & nRows)
{
CStringArray
* pTable;
pTable
= (CStringArray * )m_arrTables[nTable];
return pTable -> GetSize();
}
CDB::
~ CDB()
{
short nNumTables;
for (GetNumTables(nNumTables);nNumTables > 0 ;GetNumTables(nNumTables))
{
Delete(nNumTables
- 1 );
}
}

客户程序是一个简单的MFC单文档程序,为程序添加三个菜单项建表写表读表,对应的处理函数在CDBDoc中实现。

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> public :
CDB
* m_pDB; // pointertodatabaseobject
CStringm_csData; // lastdatareadfromdatabase
int m_nCount; // numberofwritestodatabase
short m_nTable; // numberoflasttablecreated
CDBDoc::CDBDoc()
{
m_pDB
= NULL;
}
CDBDoc::
~ CDBDoc()
{
if (m_pDB)
{
deletem_pDB;
// 释放对象
m_pDB = NULL;
}
}
BOOLCDBDoc::OnNewDocument()
{
if ( ! CDocument::OnNewDocument())
return FALSE;
// 新建数据库对象
m_pDB = new CDB;
// 初始化数据成员变量
m_csData = " Nodatayet! " ;
m_nCount
= 0 ;
m_nTable
=- 1 ;
return TRUE;
}
// 菜单项处理函数区
void CDBDoc::OnCreateTable()
{
// 建表
m_pDB -> Create(m_nTable,_T( " Testing " ));
m_nCount
= 0 ; // setnumberofwritesto0
}
void CDBDoc::OnReadTable()
{
// 读表
m_pDB -> Read(m_nTable, 0 ,m_csData.GetBuffer( 80 ));
m_csData.ReleaseBuffer();
UpdateAllViews(NULL);
}
void CDBDoc::OnWriteTable()
{
// 写表
m_nCount ++ ;
CStringcsText;
csText.Format(_T(
" Testdata#%dintable%d,row0! " ),m_nCount,( int )m_nTable);
m_pDB
-> Write(m_nTable, 0 ,csText);
}

最后在CDBViewOnDraw函数中添加如下语句来显示读表读取到的内容:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> pDC -> TextOut( 10 , 10 ,pDoc -> m_csData);

二,将C++对象打包到DLL

第一节中的标准重用方法有一个大毛病:类的实现代码被泄露了,而这想必不是我们想要的结果。要解决这个问题,我们可以使用DLL将类的代码打包成一个DLL,并提供一个用于说明函数和结构的头文件,这样实现代码就封装起来了。基于上一节的代码,我们修改如下:

一)先修改接口文件:1)为每个成员函数添加_declspec(dllexport)声明;2)CDB类添加成员函数Release(),用于在对象不再被使用时删除自己;3)声明类工厂CDBSrvFactory;4)声明返回类工厂对象的引出函数DllGetClassFactoryObject,用于创建对应的类工厂

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> typedef long HRESULT;

#define DEF_EXPORT_declspec(dllexport)

class CDB
{
// Interfaces
public :
// Interfacefordataaccess
HRESULTDEF_EXPORTRead( short nTable, short nRow,LPWSTRlpszData);
HRESULTDEF_EXPORTWrite(
short nTable, short nRow,LPCWSTRlpszData);
// Interfacefordatabasemanagement
HRESULTDEF_EXPORTCreate( short & nTable,LPCWSTRlpszName);
HRESULTDEF_EXPORTDelete(
short nTable);
// Interfaseparaobtenberinformacionsobrelabasededatos
HRESULTDEF_EXPORTGetNumTables( short & nNumTables);
HRESULTDEF_EXPORTGetTableName(
short nTable,LPWSTRlpszName);
HRESULTDEF_EXPORTGetNumRows(
short nTable, short & nRows);
ULONGDEF_EXPORTRelease();
// CPPTOCOM:needtofreeanobjectintheDLL,sinceitwasallocatedhere
// Implementation
private :
CPtrArraym_arrTables;
// ArrayofpointerstoCStringArray(the"database")
CStringArraym_arrNames; // Arrayoftablenames
public :
~ CDB();
};

class CDBSrvFactory
{
// Interface
public :
HRESULTDEF_EXPORTCreateDB(CDB
** ppObject);
ULONGDEF_EXPORTRelease();
};

HRESULTDEF_EXPORTDllGetClassFactoryObject(CDBSrvFactory
** ppObject);
二)修改对象程序。在上一节中,重用的对象是以 DBSRV.hDBSRV.cpp 这两个文件形式存在的。这一次我们要将其封装为一个 DLL 供客户程序调用。

新建一个Win32 DLL项目,在其中加入两个cpp文件,一个用于实现CDB类,代码如下

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> #include " ../interface/DBsrv.h " // 注意:接口头文件是DLL项目和客户程序共享的

// Databaseobject
HRESULTCDB::Read( short nTable, short nRow,LPWSTRlpszData)
{
CStringArray
* pTable;
pTable
= (CStringArray * )m_arrTables[nTable];
#ifndefUNICODE
MultiByteToWideChar(CP_ACP,
0 ,( * pTable)[nRow], - 1 ,lpszData, 80 );
#else
lstrcpy(lpszData,(
* pTable)[nRow]);
#endif
return NO_ERROR;
}

HRESULTCDB::Write(
short nTable, short nRow,LPCWSTRlpszData)
{
CStringArray
* pTable;
pTable
= (CStringArray * )m_arrTables[nTable];
#ifdefUNICODE
pTable
-> SetAtGrow(nRow,lpszData);
#else
char szData[ 80 ];
WideCharToMultiByte(CP_ACP,
0 ,lpszData, - 1 ,szData, 80 ,NULL,NULL);
pTable
-> SetAtGrow(nRow,szData);
#endif
return NO_ERROR;
}
HRESULTCDB::Create(
short & nTable,LPCWSTRlpszName)
{
CStringArray
* pTable = new CStringArray;
nTable
= m_arrTables.Add(pTable);
#ifdefUNICODE
m_arrNames.SetAtGrow(nTable,lpszName);
#else
char szName[ 80 ];
WideCharToMultiByte(CP_ACP,
0 ,lpszName, - 1 ,szName, 80 ,NULL,NULL);
m_arrNames.SetAtGrow(nTable,szName);
#endif
return NO_ERROR;
}

HRESULTCDB::Delete(
short nTable)
{
CStringArray
* pTable;
pTable
= (CStringArray * )m_arrTables[nTable];
deletepTable;
m_arrTables[nTable]
= NULL;
m_arrNames[nTable]
= "" ;
if (nTable == m_arrTables.GetSize() - 1 )
{
m_arrTables.RemoveAt(nTable);
m_arrNames.RemoveAt(nTable);
}
return NO_ERROR;
}

HRESULTCDB::GetNumTables(
short & nNumTables)
{
nNumTables
= m_arrTables.GetSize();
return NOERROR;
}

HRESULTCDB::GetTableName(
short nTable,LPWSTRlpszName)
{
#ifndefUNICODE
MultiByteToWideChar(CP_ACP,
0 ,m_arrNames[nTable], - 1 ,lpszName, 80 );
#else
lstrcpy(lpszName,m_arrNames[nTable]);
#endif
return NO_ERROR;
}

HRESULTCDB::GetNumRows(
short nTable, short & nRows)
{
CStringArray
* pTable;
pTable
= (CStringArray * )m_arrTables[nTable];
return pTable -> GetSize();
}

ULONGCDB::Release()
{
delete
this ;
return 0 ;
}

CDB::
~ CDB()
{
short nNumTables;
for (GetNumTables(nNumTables);nNumTables > 0 ;GetNumTables(nNumTables))
{
Delete(nNumTables
- 1 );
}
}

在另一个DBSrvFact.cpp文件中实现类工厂:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> #include " ../interface/dbsrv.h " // 注意:接口头文件是DLL项目和客户程序共享的

// Createanewdatabaseobjectandreturnapointertoit
HRESULTCDBSrvFactory::CreateDB(CDB ** ppvDBObject)
{
* ppvDBObject = new CDB;
return NO_ERROR;
}

ULONGCDBSrvFactory::Release()
{
delete
this ;
return 0 ;
}

HRESULTDEF_EXPORTDllGetClassFactoryObject(CDBSrvFactory
** ppObject)
{
* ppObject = new CDBSrvFactory;
return NO_ERROR;
}

编译后生成引入库文件(.LIB)和动态链接库文件(.DLL)

三)修改客户程序

1)由于前面我们已经为CDB类添加了删除自己的函数Release(),因此在CDBDoc的析构函数中修改我们使用的CDB对象的删除方式如下:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> CDBDoc:: ~ CDBDoc()
{
if (m_pDB)
{
m_pDB
-> Release(); // 不再是deletem_pDB
m_pDB = NULL;
}
}

2 )创建 CDB 类对象的方式改变了,我们通过对应的类工厂对象来创建 CDB 对象,而不再是直接地 new 一个 CDB 对象出来了。
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> BOOLCDBDoc::OnNewDocument()
{
if ( ! CDocument::OnNewDocument())
return FALSE;
// 新建数据库对象
// m_pDB=newCDB;
CDBSrvFactory * pDBFactory = NULL; // 对应的类工厂对象
DllGetClassFactoryObject( & pDBFactory); // 获取对应的类工厂
pDBFactory -> CreateDB( & m_pDB); // 由类工厂负责创建所请求的对象
pDBFactory -> Release(); // donotneedthefactoryanymore
// 初始化数据成员变量

}

3)将传入/传出DLL中的参数标准化为Unicode编码。若不是以Unicode方式编译(##ifndef UNICODE,则使用MultiByteToWideChar将输出参数由ASCII转换为Unicode,用WideCharToMultiByte将输入参数由Unicode转换为ASCII

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> void CDBDoc::OnReadTable()
{
#ifdefUNICODE
m_pDB
-> Read(m_nTable, 0 ,m_csData.GetBuffer( 80 ));
#else
WCHARszuData[
80 ];
m_pDB
-> Read(m_nTable, 0 ,szuData);
WideCharToMultiByte(CP_ACP,
0 ,szuData, - 1 ,m_csData.GetBuffer( 80 ), 80 ,NULL,NULL);
#endif
m_csData.ReleaseBuffer();
UpdateAllViews(NULL);
}

void CDBDoc::OnWriteTable()
{
m_nCount
++ ;
CStringcsText;
csText.Format(_T(
" Testdata#%dintable%d,row0! " ),m_nCount,( int )m_nTable);
#ifdefUNICODE
m_pDB
-> Write(m_nTable, 0 ,csText);
#else
WCHARszuText[
80 ]; // specialtreatmentforASCIIclient
MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,csText, - 1 ,szuText, sizeof (szuText));
m_pDB
-> Write(m_nTable, 0 ,szuText);
#endif
}

4) 连接 DLL ,创建客户程序。现在我们使用 DLL, 因此不再需要被重用对象的源代码,那么先将 DBsrv.cppDBsrv.h 两个文件从工程中删除。与 DLL 连接的方式采用隐式链接:在 链接器 à 输入 à 附加依赖项 中输入: .."object"Debug"db.lib 。最后将 DB.dll 拷贝到客户程序目录下,运行客户程序。

Ok,万里长征迈出了第一步

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值