DLL导入导出STL类对象导致崩溃的问题
问题:
从某DLL中导出一个接口函数GetDirFileIdSet用于获取目录下所有文件的ID集合。函数声明如下,该函数传入一个std::set<UINT>类对象:
void GetDirFileIdSet(std::set<UINT>& rFileIdSet);
当在VS2008中编译的“数据文件编辑器”加载了在VC6中编译的DLL,并调用到GetDirFileIdSet时发生了崩溃。
原因:
在不同的DLL或EXE中通过指针或引用操作另一个DLL或EXE中的STL类对象时,会遇到严重的程序错误,包括数据错乱或丢失。
标准C++库的多数类直接或间接的使用了静态数据成员。由于这些类是通过模板创建实例的,所以每个可执行模块(DLL或EXE)包含了有关类静态数据成员的一份Copy。当STL类中的函数成员要求操作静态数据成员时,这个类操作的静态数据是此函数成员代码所在的执行模块中的数据。由于静态成员数据在可执行模块间不能保证同步,所以前面提到的操作会导致读取失败或数据混乱和丢失。
解决方法:
1. 在创建STL类对象的模块中导出读取函数。这些函数包装了STL类对象要求的功能。这样,STL类对象只在原模块中被读取。例如:假设Program.exe需要得到Library.dll内std::deque<UINT>类对象中的一个元素,Library.dll则要导出读取函数:
__declspec(dllexport) UINT GetItem(UINT unIndex);
Program.exe就可以调用此函数来得到队列的一个元素了。
2. 从一个模块中导出模板实例,在另一个模块中导入此实例。例如:Library.dll将std::map<int, MyClass>指针传给Program.exe中的一个函数,需要在Library.dll中导出MyClass类定义和std::map<int, MyClass>类定义。然后就可以类似这样调用:
std::map<int, MyClass> *pTheMap = GiveMeTheMap();
const MyClass* pszItem = GetTheMapItemX(pTheMap, x);