
cobra
striver
-
个人空间
相册
- 性别:
- 来自:拼吾爱
- 积分:6811
- 帖子:6723
- 注册:
2007-04-09
|
回复:COM组件开发实践(四)---From C++ to COM :Part 1
二,将C++对象打包到DLL中 第一节中的标准重用方法有一个大毛病:类的实现代码被泄露了,而这想必不是我们想要的结果。要解决这个问题,我们可以使用DLL将类的代码打包成一个DLL,并提供一个用于说明函数和结构的头文件,这样实现代码就封装起来了。基于上一节的代码,我们修改如下: 一)先修改接口文件:1)为每个成员函数添加_declspec(dllexport)声明;2)为CDB类添加成员函数Release(),用于在对象不再被使用时删除自己;3)声明类工厂CDBSrvFactory;4)声明返回类工厂对象的引出函数DllGetClassFactoryObject,用于创建对应的类工厂  Code
typedef long HRESULT;
#define DEF_EXPORT _declspec(dllexport)
class CDB { // Interfaces public: // Interface for data access HRESULT DEF_EXPORT Read(short nTable, short nRow, LPWSTR lpszData); HRESULT DEF_EXPORT Write(short nTable, short nRow, LPCWSTR lpszData); // Interface for database management HRESULT DEF_EXPORT Create(short &nTable, LPCWSTR lpszName); HRESULT DEF_EXPORT Delete(short nTable); // Interfase para obtenber informacion sobre la base de datos HRESULT DEF_EXPORT GetNumTables(short &nNumTables); HRESULT DEF_EXPORT GetTableName(short nTable, LPWSTR lpszName); HRESULT DEF_EXPORT GetNumRows(short nTable, short &nRows); ULONG DEF_EXPORT Release(); //CPPTOCOM: need to free an object in the DLL, since it was allocated here // Implementation private: CPtrArray m_arrTables; // Array of pointers to CStringArray (the "database") CStringArray m_arrNames; // Array of table names public: ~CDB(); };
class CDBSrvFactory { // Interface public: HRESULT DEF_EXPORT CreateDB(CDB** ppObject); ULONG DEF_EXPORT Release(); };
HRESULT DEF_EXPORT DllGetClassFactoryObject(CDBSrvFactory ** ppObject);
二)修改对象程序。在上一节中,重用的对象是以DBSRV.h和DBSRV.cpp这两个文件形式存在的。这一次我们要将其封装为一个DLL供客户程序调用。 新建一个Win32 DLL项目,在其中加入两个cpp文件,一个用于实现CDB类,代码如下  Code
#include "..\interface\DBsrv.h" //注意:接口头文件是DLL项目和客户程序共享的
// Database object HRESULT CDB::Read(short nTable, short nRow, LPWSTR lpszData) { CStringArray *pTable; pTable=(CStringArray*) m_arrTables[nTable]; #ifndef UNICODE MultiByteToWideChar(CP_ACP, 0, (*pTable)[nRow], -1, lpszData, 80); #else lstrcpy (lpszData, (*pTable)[nRow]); #endif return NO_ERROR; }
HRESULT CDB::Write(short nTable, short nRow, LPCWSTR lpszData) { CStringArray *pTable; pTable=(CStringArray*) m_arrTables[nTable]; #ifdef UNICODE 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; } HRESULT CDB::Create(short &nTable, LPCWSTR lpszName) { CStringArray *pTable=new CStringArray; nTable=m_arrTables.Add(pTable); #ifdef UNICODE 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; }
HRESULT CDB::Delete(short nTable) { CStringArray *pTable; pTable=(CStringArray*) m_arrTables[nTable]; delete pTable; m_arrTables[nTable]=NULL; m_arrNames[nTable]=""; if (nTable==m_arrTables.GetSize()-1) { m_arrTables.RemoveAt(nTable); m_arrNames.RemoveAt(nTable); } return NO_ERROR; }
HRESULT CDB::GetNumTables(short &nNumTables) { nNumTables=m_arrTables.GetSize(); return NOERROR; }
HRESULT CDB::GetTableName(short nTable, LPWSTR lpszName) { #ifndef UNICODE MultiByteToWideChar(CP_ACP, 0, m_arrNames[nTable], -1, lpszName, 80); #else lstrcpy(lpszName, m_arrNames[nTable]); #endif return NO_ERROR; }
HRESULT CDB::GetNumRows(short nTable, short &nRows) { CStringArray *pTable; pTable=(CStringArray*) m_arrTables[nTable]; return pTable->GetSize(); }
ULONG CDB::Release() { delete this; return 0; }
CDB::~CDB() { short nNumTables; for (GetNumTables(nNumTables);nNumTables>0; GetNumTables(nNumTables)) { Delete(nNumTables-1); } }
在另一个DBSrvFact.cpp文件中实现类工厂:  Code
#include "..\interface\dbsrv.h" //注意:接口头文件是DLL项目和客户程序共享的
// Create a new database object and return a pointer to it HRESULT CDBSrvFactory::CreateDB(CDB** ppvDBObject) { *ppvDBObject=new CDB; return NO_ERROR; }
ULONG CDBSrvFactory::Release() { delete this; return 0; }
HRESULT DEF_EXPORT DllGetClassFactoryObject(CDBSrvFactory ** ppObject) { *ppObject=new CDBSrvFactory; return NO_ERROR; }
编译后生成引入库文件(.LIB)和动态链接库文件(.DLL)。 三)修改客户程序 1)由于前面我们已经为CDB类添加了删除自己的函数Release(),因此在CDBDoc的析构函数中修改我们使用的CDB对象的删除方式如下:  Code
CDBDoc::~CDBDoc() { if (m_pDB) { m_pDB->Release();//不再是delete m_pDB m_pDB=NULL; } }
2)创建CDB类对象的方式改变了,我们通过对应的类工厂对象来创建CDB对象,而不再是直接地new一个CDB对象出来了。  Code
BOOL CDBDoc::OnNewDocument() { if (!CDocument::OnNewDocument()) return FALSE; //新建数据库对象 //m_pDB=new CDB; CDBSrvFactory *pDBFactory=NULL; //对应的类工厂对象 DllGetClassFactoryObject(&pDBFactory); //获取对应的类工厂 pDBFactory->CreateDB(&m_pDB); //由类工厂负责创建所请求的对象 pDBFactory->Release(); // do not need the factory anymore // 初始化数据成员变量 … }
3)将传入/传出DLL中的参数标准化为Unicode编码。若不是以Unicode方式编译(##ifndef UNICODE),则使用MultiByteToWideChar将输出参数由ASCII转换为Unicode,用WideCharToMultiByte将输入参数由Unicode转换为ASCII。  Code
void CDBDoc::OnReadTable() { #ifdef UNICODE m_pDB->Read(m_nTable, 0, m_csData.GetBuffer(80)); #else WCHAR szuData[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++; CString csText; csText.Format(_T("Test data #%d in table %d, row 0!"), m_nCount, (int) m_nTable); #ifdef UNICODE m_pDB->Write(m_nTable, 0, csText); #else WCHAR szuText[80]; // special treatment for ASCII client MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, csText, -1, szuText, sizeof(szuText)); m_pDB->Write(m_nTable, 0, szuText); #endif }
4)连接DLL,创建客户程序。现在我们使用DLL,因此不再需要被重用对象的源代码,那么先将DBsrv.cpp和DBsrv.h两个文件从工程中删除。与DLL连接的方式采用隐式链接:在”链接器à输入à附加依赖项“中输入:.."object"Debug"db.lib。最后将DB.dll拷贝到客户程序目录下,运行客户程序。 源码下载:  附件: 您所在的用户组无法下载或查看附件
|