锐英源软件
第一信赖

精通

英语

开源

擅长

开发

培训

胸怀四海 

第一信赖

当前位置:锐英源 / 视频介绍 / COM组件开发引导系列 / DB_VTBLDLL: C++ Object in a DLL Exposing "Pure" Abstract Base Class
服务方向
人工智能数据处理
人工智能培训
kaldi数据准备
小语种语音识别
语音识别标注
语音识别系统
语音识别转文字
kaldi开发技术服务
软件开发
运动控制卡上位机
机械加工软件
软件开发培训
Java 安卓移动开发
VC++
C#软件
汇编和破解
驱动开发
联系方式
固话:0371-63888850
手机:138-0381-0136
Q Q:396806883
微信:ryysoft

1.1.6 DB_VTBLDLL: C++ Object in a DLL Exposing "Pure" Abstract Base Class

      使在DLL里的C++对象暴露“纯”虚基类

It is even more beneficial to apply the technique of abstract base classes to the scenario of binary packaging in DLLs. In the previous DLL sample, we had to export each function of the object, and we depended on the name-mangling scheme of the compiler. There is no standard for name-mangling, so you have to provide objects for each compiler that a potential client might want to use for their implementation.把抽象基类技术应用到DLL里的二进制包情况下也是有益的。在以前的DLL例子里,我们必须导出对象里每个函数,且我们依赖于编译器的命名排编。不存在标准的命名排编,所以你不得不提供为每个编译器服务的对象,潜在的客户端需要用什么编译器实现,你就要对应处理一下。

Remember that pure abstract base classes provide little more than a table of function entry points, and the entry points are allocated by the compiler of the object. They are initialized at load time and not freed until the module is unloaded. If we create object instances indirectly, memory management is not a problem. Code generated by a client's compiler can freely access all these tables through the vtable pointer, which is always located in the first 4 bytes of the memory image of the object. All C++ compilers use the same memory layout for their implementations of vtables, so vtables are actually a de facto binary standard in the C++ world.记住纯抽象基类只是提供了函数入口点的表,入口点是由编译器分配的。他们在加载时初始化且直到模块卸载时才释放。如果我们直接创建个对象实例,内存管理没有问题。客户端编译生成的代码能够自由存取vtable指针里的所有表,vtable始终在对象的内存映象的前4个字节里。所有C++的编译器使用相同的内存布局,所以vtables实际上是C++世界里的标准。

Thus, this technique lets you export C++ classes without exporting any function name. (16-bit Windows still requires the _export tag or compilation with a special compiler option, because we need prologue/epilogue code to switch between different data segments of DLL and EXE.) All that the client and the object have to agree on is the layout of the vtable—that is, the order of the functions in the vtable, the number and kind of parameters for each function, and the way the parameters are passed (in most cases on the stack: calling convention!).这个技术让你导出C++类而不用导出任何函数名称。客户端和对象在使用vtable时必须一致—也就是说,在vtable里的顺序,每个函数里参数的个数和类型,参数传递的类型(通常是调用栈:调用约定)

The layout of the vtable and the function parameters is completely defined by an abstract base class, so that a C++ header file can server as a complete description of the syntax of an interface contract.vtable的布局和函数参数完全由抽象基类定义,所以C++头文件能够把接口描述的很清晰。

1.1.6.1 Changes: Step by Step逐步修改

This sample is based on the first DLL subdirectory, DB_CPPDLL. The changes are almost identical to the ones for DB_VTBL, except that we already implemented the indirect instantiation of CDB for other reasons (memory management!). 这个例子是基于第一个DLL子目录下的例子DB_CPPDLL。修改几乎和DB_VTBL一样,除了出于其它原因我们已经实现的非直接实例化CDB对象。

  • Copy the interface\dbsrv.h header file to object\dbsrvimp.h. The first file will contain the abstract base class, which is the only code the client needs. The second file will be the header for the actual implementation.拷贝interface\dbsrv.h头文件到object\dbsrvimp.h。第一个文件包含了抽象基类,它也是客户端只需要的代码。第2个文件会是实际实现的头文件。
  • Make all functions in interface\dbsrv.h (both CDB and CDBSrvFactory) pure virtual functions and remove the data members. Remove the export on all member functions. Change the name of the class from CDB to IDB (Interface to DB), and CDBSrvFactory to IDBSrvFactory.让在interface\dbsrv.h(CDB和CDBrvFactory)里的所有函数变为纯虚函数,且删除数据成员。删除导出标志。修改CDB类名到IDB(DB的接口),且CDBSrvFactory到IDBSrvFactory。
  • Derive CDB in object\dbsrvimp.h from IDB and include ...\interface\bdsrv.h. (Don't forget to change the #ifndef at the beginning of the file to something like _DBSRVIMP_INCLUDE.) Derive CDBSrvFactory from IDBSrvFactory.在object\dbsrvimp.h里派生出CDB类,基类是IDB且包含..\inerface\dbsrv.h(不要忘记修改文件开始的#ifndef用的宏比如_DBSRVIMP_INCLUDE。)从IDBSrvFactory派生CDBSrvFactory。
  • Change the parameter of IDBSrvFactory::CreateDB to IDB** instead of CDB**. Change the parameter of DllGetClassFactoryObject to IDBSrvFactory**. 修改IDBSrvFactory::CreateDB的参数为IDB**来代替CDB**。修改DllGetClassFactoryObject的参数为IDBSrvFactory**。

The following code illustrates the resulting Interface\dbsrv.h header file:下面代码演示了Interface\dbsrv.h文件的修改结果

class IDB {
// Interfaces
public:
// Interface for data access.
virtual HRESULT Read(short nTable, short nRow, LPWSTR lpszData) =0;
(...)
};
class IDBSrvFactory {
// Interface
public:
virtual HRESULT CreateDB(IDB** ppObject) =0;
virtual ULONG  Release() =0;
};
HRESULT DEF_EXPORT DllGetClassFactoryObject(IDBSrvFactory ** ppObject);

The new Object\dbsrvimp.h header:新的Object\dbsrvimp.h头文件如下:

#include "..\Interface\dbsrv.h"
class CDB : public IDB {
// Interfaces
public:
// Interface for data access.
HRESULT Read(short nTable, short nRow, LPWSTR lpszData);
};
class CDBSrvFactory : public IDBSrvFactory {
// Interface
public:
HRESULT CreateDB(IDB** ppObject);
ULONG  Release();
};
  • Make object\dbsrv.cpp and object\dbsrvfact.cpp include dbsrvimp.h instead of dbsrv.h. 让object\dbsrv.cpp和object\dbsrvfact.cpp文件包含dbsrvimp.h,来代替dbsrv.h

The resulting DBSrvFact.CPP (no changes, just adjusting the instantiation function): DBSrvFact.CPP结果如下(没有修改,只是调整了实例化函数):

HRESULT CDBSrvFactory::CreateDB(IDB** ppvDBObject) {
*ppvDBObject=(IDB*) new CDB;
return NO_ERROR;
}
(...)
HRESULT DEF_EXPORT DllGetClassFactoryObject(IDBSrvFactory ** ppObject) {
  *ppObject=(IDBSrvFactory*) new CDBSrvFactory;
  return NO_ERROR;
}
  • Now we will adjust the client: Change CDB::m_pDB to IDB*. In CDBDoc::OnNewDocument, change CDBSrvFactory* to IDBSrvFactory*. 现在,我们要调整客户端:修改CDB::m_pDB到IDB*。在CDBDoc::OnNewDocument里,修改CDBSrvFactory*为IDBSrvFactory*。

Everything works just as before. The performance is identical to that of the prior version of the code where everything is compiled into one executable! The compiler does the same indirect function call through the vtable.性能和上版本一样。编译器通过vtable实现了同样的非直接函数调用。

Run DumpBin.EXE /exports db.dll and you will see that the only exported entry point is DllGetClassFactoryObject! 调用DumpBin.EXE /exports db.dll,你会看到只有一个DllGetClassFactoryObject导出函数。

All the rest of the dangerous, mangled names have disappeared. All member functions are now cleanly accessed through vtables. We could even dare to load the DLL dynamically (through LoadLibrary/GetProcAddress), because there is only one exported entry point.其它危险,命名排编没有了。所有成员函数通过vtables清晰地访问了。我们甚至能够大胆地动态加载DLL(通过LoadLibrary/GetProcAddress),因为只有一个导出入口点了。

友情链接
版权所有 Copyright(c)2004-2021 锐英源软件
公司注册号:410105000449586 豫ICP备08007559号 最佳分辨率 1024*768
地址:郑州市金水区郑州大学北校区院(文化路97号院)内