我打算使用STL里面的map做为我的主应用程序与下层DLL的数据交换媒介。于是我遇到了让我发狂的问题:我在Exe和Dll工程都可以访问到的头文件中定义:
typedef std::map<CString, ULONG> CMyMap;在扩展Dll的导出类增加接口:
void STLTest(CMyMap* pMap);
void CSTLTest::STLTest(CMyMap* pMap)
{
CMyMap iter; (*pMap)[_T("S0")] = 0x2929;
}
然后在上层Exe的代码中调用:
CMyMap myMap;
CSTLTest test;test.STLTest(&myMap);程序立刻提示assert错误,然后崩溃。调试之后发现时STL的map在插入新element的时候,需要查找是否有相同的key,但是在我的程序中,STL一直查找到最后的end element,导致程序崩溃。这个操作如果不是在Exe中声明CMyMap myMap;然后在Dll中访问,是不会出现问题的。前面的代码,我试着map定义改为:
typedef std::map<int, ULONG> CMyMap;
插入代码改为:
(*pMap)[20] = 0x2233;还是崩溃。难道VC6就这么不能与STL兼容吗?还是我的程序有问题?我已经试着将程序简化到只有几行代码,问题依旧。而如果我把std::map更换成CMap,一切问题都没有了。但是糟糕的是CMap不能使用String做为Key,更让我发愁。请大家看看是否有解决方法。

解决方案 »

  1.   

    绝对可以使用std;map的
    http://www.csdn.net/develop/Read_Article.asp?Id=20062
    我也是dll中使用的
    不过和你不同的我是sdk下面
      

  2.   

    lsaturn(土星-站了一晚):哥们,我现在的问题是,在Exe中声明的Map对象,拿到Dll中使用,无法调用与比较功能相关的函数,比如插入、查找。这两个基本操作都会导致我的程序崩溃。
      

  3.   

    Oh, faint! I don't know...
      

  4.   

    可以用CString
    typedef CMap<CString, LPCTSTR, ULONG, ULONG> CMyMap;
      

  5.   

    flyelf(空谷清音):
    你真是天使啊,万分感谢。
      

  6.   

    我写过一个测试程序,能通过但结果不正确,警告一大堆。希望有高手出现
    //base.h
    #include <windows.h>
    #include <map>
    typedef std::map<int, long> CMyMap;
    extern "C" void WINAPI Test(CMyMap* _pmap);//mydll.cpp
    //cl /c mydll.cpp
    //link /dll /def:mydll.def mydll.obj >.\test_mydll
    #include "base.h"
    extern "C" void WINAPI Test(CMyMap* _pmap)
    {
    std::pair<int,long> value(0,12);
    // (*_pmap)[0] = 12;
    _pmap->insert(value);
    }//test_mydll.cpp
    #include <iostream>
    #include "base.h"
    #pragma comment(lib,"mydll.lib")
    void main()
    {
    CMyMap* _pmap = new CMyMap;
    Test(_pmap);

    // (*_pmap)[0] = 12;

    std::cout<<(*_pmap)[0]<<std::endl;
    }
    结果输出为0;
    H:\程序文件夹\test_my_dll\test_my_dll.cpp(14) : warning C4786: 'std::reverse_bidirectional_iterator<std::_Tree<int,std::pair<int const ,long>,std::map<int,long,std::less<int>,std::allocator<long> >::_Kfn,std::less<int>,std::allocator<long> >::iterat
    or,std::pair<int const ,long>,std::pair<int const ,long> &,std::pair<int const ,long> *,int>' : identifier was truncated to '255' characters in the debug information
    H:\程序文件夹\test_my_dll\test_my_dll.cpp(14) : warning C4786: 'std::reverse_bidirectional_iterator<std::_Tree<int,std::pair<int const ,long>,std::map<int,long,std::less<int>,std::allocator<long> >::_Kfn,std::less<int>,std::allocator<long> >::const_
    iterator,std::pair<int const ,long>,std::pair<int const ,long> const &,std::pair<int const ,long> const *,int>' : identifier was truncated to '255' characters in the debug information
    H:\程序文件夹\test_my_dll\test_my_dll.cpp(14) : warning C4786: 'std::pair<std::_Tree<int,std::pair<int const ,long>,std::map<int,long,std::less<int>,std::allocator<long> >::_Kfn,std::less<int>,std::allocator<long> >::iterator,std::_Tree<int,std::pai
    r<int const ,long>,std::map<int,long,std::less<int>,std::allocator<long> >::_Kfn,std::less<int>,std::allocator<long> >::iterator>' : identifier was truncated to '255' characters in the debug information
    H:\程序文件夹\test_my_dll\test_my_dll.cpp(14) : warning C4786: 'std::pair<std::_Tree<int,std::pair<int const ,long>,std::map<int,long,std::less<int>,std::allocator<long> >::_Kfn,std::less<int>,std::allocator<long> >::const_iterator,std::_Tree<int,st
    d::pair<int const ,long>,std::map<int,long,std::less<int>,std::allocator<long> >::_Kfn,std::less<int>,std::allocator<long> >::const_iterator>' : identifier was truncated to '255' characters in the debug information
    d:\microsoft visual studio\vc98\include\xtree(162) : warning C4786: 'std::_Tree<int,std::pair<int const ,long>,std::map<int,long,std::less<int>,std::allocator<long> >::_Kfn,std::less<int>,std::allocator<long> >::_Tree<int,std::pair<int const ,long>,
    std::map<int,long,std::less<int>,std::allocator<long> >::_Kfn,std::less<int>,std::allocator<long> >' : identifier was truncated to '255' characters in the debug information
    d:\microsoft visual studio\vc98\include\xtree(236) : warning C4786: '__ehhandler$?insert@?$_Tree@HU?$pair@$$CBHJ@std@@U_Kfn@?$map@HJU?$less@H@std@@V?$allocator@J@2@@2@U?$less@H@2@V?$allocator@J@2@@std@@QAE?AU?$pair@Viterator@?$_Tree@HU?$pair@$$CBHJ@
    std@@U_Kfn@?$map@HJU?$less@H@std@@V?$allocator@J@2@@2@U?$less@H@2@V?$allocator@J@2@@std@@_N@2@ABU?$pair@$$CBHJ@2@@Z' : identifier was truncated to '255' characters in the debug information
    d:\microsoft visual studio\vc98\include\xtree(236) : warning C4786: '__unwindfunclet$?insert@?$_Tree@HU?$pair@$$CBHJ@std@@U_Kfn@?$map@HJU?$less@H@std@@V?$allocator@J@2@@2@U?$less@H@2@V?$allocator@J@2@@std@@QAE?AU?$pair@Viterator@?$_Tree@HU?$pair@$$C
    BHJ@std@@U_Kfn@?$map@HJU?$less@H@std@@V?$allocator@J@2@@2@U?$less@H@2@V?$allocator@J@2@@std@@_N@2@ABU?$pair@$$CBHJ@2@@Z$0' : identifier was truncated to '255' characters in the debug information
    d:\microsoft visual studio\vc98\include\utility(21) : warning C4786: 'std::pair<std::_Tree<int,std::pair<int const ,long>,std::map<int,long,std::less<int>,std::allocator<long> >::_Kfn,std::less<int>,std::allocator<long> >::iterator,bool>::pair<std::
    _Tree<int,std::pair<int const ,long>,std::map<int,long,std::less<int>,std::allocator<long> >::_Kfn,std::less<int>,std::allocator<long> >::iterator,bool>' : identifier was truncated to '255' characters in the debug information
    Linking...test_my_dll.exe - 0 error(s), 8 warning(s)
      

  7.   

    不是的,好像STL用了自己内存分配机制,
    在动态库与程序之间的调用会有问题.
    其实我也不是很清楚,只是遇到了类似的问题
      

  8.   

    还是这个老问题:
    把dll的Settings的C/C++选项卡的Code Generation的Use Run-time liberary改成Debug Multithreaded DLL,在Release版本中改成Multithreaded DLL
    另外在包括之前要先去掉警告错误
    #pragma warning(disable:4786)
    #pragma warning(disable:4251)
    #pragma warning(disable:4273)#include <map>
      

  9.   

    enoloo(行者无疆):
    那些警告是没有办法的事情,只能在StdAfx.h中加入以下几句:
    #pragma warning(disable:4786)#pragma warning(disable:4251)#pragma warning(disable:4273)
    屏蔽这些警告即可。你的写法也不对,这样写:
    CMyMap::itertor iter = _pmap->find(0);
    std::cout << (*iter).second << std::endl;[]操作符只是插入功能。非常感谢你这么热心。^_^
      

  10.   

    bluebohe(薄荷) (MVP):
    我的Exe和Dll工程都是使用Multithreaded DLL的,这里不应该产生问题呀。
      

  11.   

    czylj(夭夭):你的话,我要好好考虑一下。
      

  12.   

    不能把STL的map指针当作参数传递到DLL中去.
    使用DLL时,DLL用到的内存空间(包括全局和局部)将在使用者的堆栈中申请,以VC的std::map为例,它使用红黑树实现,红黑树中定义了一个表示空结点的类静态变量_Nil,初使值为NULL.对树初使化_init()中加以初使化if (_Nil == 0)_Nil= _Buynode(0, _Black);_Init()将在树的构造函数中_Tree()中调用,它会初使化一个包含空的KEY/VALUE的pair.
    然而,用户自定义的DLL内部用到map时,它将在调用进程空间分配地址以存放_Nil,如果调用进程同时也用到了map,用户EXE中所用到的_Nil静态变量的地址和DLL中用到的_Nil是两个变量,互不影响.但这不是楼上所提问题的成因.
    事实上,仅将map指针传递给DLL,DLL使用它时,一切比较皆以DLL中的那个_Nil为依据----如果DLL中已经用过同模板参数的map,则_Nil不为空,但绝不等于EXE希望的那个_Nil,运气好的话会有ASSERT出现,不幸的时候只是内存访问违规!如果DLL中还没用过同模板参数的map,则因为_init()从未调,_Nil将保持为NULL.然后,当进行键值KEY的比较操作时,会将_Nil中的KEY拿来同用户提供的KEY参数比较,因为_Nil内部的KEY/VALUE对从没机会初使化,于是产生内存访问违规.
    如果VC的STL实现没有使用静态类变量,这种问题就不会有了,猜想MFC就没有用这种方式,所以MFC的CMap传递到DLL中时可用.
    VC的调试器不支持长度大于255的标识符,但STL的模板机制的最终形成的类名标识符通常大于这个数.一般情况下,通过将C4786号警告关闭,不让它出现在输出框中:在包含任何STL的头文件前,加上:#pragma warning( disable : 4786 ) ,如果需要恢复此警告时,加上#pragma warning( default : 4786 ) 就行,STL带来的良性警告还有不少,都可用这种方式消除.
      

  13.   

    bluebohe(薄荷) (MVP) 真是高人啊!
    偶为了追踪这个问题在STL源码里折腾了N个小时,自认为找到原因,并下了不能那样应用的断言,哪知转而使用多线程库就搞定了!谢谢
      

  14.   

    QunKangLi(李群康):我还没有搞定呢。
    我从一开始就使用了多线程库,还是崩溃啊。bluebohe(薄荷) (MVP):
    我还没有解决问题呢。
      

  15.   

    QunKangLi(李群康):多谢你那么认真的追踪这个问题。
      

  16.   

    enoloo(行者无疆):
    那些警告是没有办法的事情,只能在StdAfx.h中加入以下几句:
    #pragma warning(disable:4786)#pragma warning(disable:4251)#pragma warning(disable:4273)
    屏蔽这些警告即可。你的写法也不对,这样写:
    CMyMap::itertor iter = _pmap->find(0);
    std::cout << (*iter).second << std::endl;[]操作符只是插入功能。非常感谢你这么热心。^_^
    ////////////////////////////////
    谢谢。 -:)
      

  17.   

    o                    .  .- .-
                                                            ./    .--...
                                       o          .-------- $    .--
                                  o  分    o     /                 .......
                                                '                  ..
                                 o   o    o      '........            ----
                                           o 分           .          --..
                                o    o         o           .   .---..   -
                                           o               .  '     -.
                                        o   o             /   '
                                       o  分               / ' ' '
                                       \-----/....... . '  ' '  '
                                        \ __/ -      - -'  '  ' '
                                                        '  '  ' '
                                         o              '   ' ' '
                                                        '   '  '
                                                         -..'.----------.
                                                            I   ----------
                                                     /////////.经典,请支持http://expert.csdn.net/Expert/topic/2921/2921075.xml?temp=.9381983http://expert.csdn.net/Expert/topic/2921/2921075.xml?temp=.9381983http://expert.csdn.net/Expert/topic/2921/2921075.xml?temp=.9381983http://expert.csdn.net/Expert/topic/2921/2921075.xml?temp=.9381983http://expert.csdn.net/Expert/topic/2921/2921075.xml?temp=.9381983http://expert.csdn.net/Expert/topic/2921/2921075.xml?temp=.9381983http://expert.csdn.net/Expert/topic/2921/2921075.xml?temp=.9381983
      

  18.   

    --------------------Configuration: test_my_dll - Win32 Debug--------------------test_my_dll.exe - 0 error(s), 0 warning(s)不过结果不对。
      

  19.   

    问题我已经放在网络上,希望大家能够解决
    http://bluebohe.go.nease.net/dlltest.rar
      

  20.   

    检查了一下,我测试时这个问题时用的本来就是多线程库!看来相信别人的结论,不实际操作一下,始终不行.
    实在不行的话,先找个变通方案用到起吧,比如STL类限制成只在在DLL中或只在EXE中使用,当然可以都用,但不要混用,如相互操作就成.
      

  21.   

    bluebohe(薄荷) (MVP):
    多谢你。^_^
      

  22.   

    我分析是 QunKangLi(李群康)所说的。
    我跟踪了_Nil,发现在dll中和正常调用的地址并不一样。
    在dll中重新分配了rb-tree的节点空间,在dll中和exe中是两套独立的map空间。我想,可能是把dll地址映射到进程空间的时候,系统给出了某个偏移。当在dll中插入节点的时候,寻址是dll中的相对寻址,发现_Nil为空,就在dll中构造一个节点。所以实际上和exe中是两套rb-tree.同样,在exe中查看结果,用进程相对空间,所以找不到在dll中所构造的rb-tree.我发现地址差0x00100000.这是我的理解,不知道这样会不会内存泄露。内部机制是怎么样的。
    希望高手能进来讨论/
      

  23.   

    现在我很认真的考虑采用flyelf(空谷清音)的方案,用CMap代替stl::map,毕竟工作不等人。
    我已经测试过了,CMap一切正常。但是这样影响了我以前的一些工作成果,而且还要更改设计文档。当初做设计的时候真的没有想到stl::map会有这种问题。感觉大家好热心啊,让我们找出问题的实质吧。今天晚上我会使用VC7和C++ builder X测试一下,看看它们是否会有同样的问题。
      

  24.   

    又发现一个新东西:
    DLL和EXE都需要对模板类实例化,但虽然参数相同,但实例化出的结果却不是同一个类,也就是是DLL中的CMyMap和EXE中的CMyMap不是同一种类型的类.
    在DLL中:
    #include <typeinfo.h>
    void CSTLTest::STLTest( CMyMap * pMap)
    {
    BOOL b = typeid(pMap)==typeid(CMyMap);//结果是0,如果不是同一个类,把pMap当作CMyMap操作当然要出错.
      

  25.   

    给个代码吧.也许我可以解决.
    [email protected]
      

  26.   

    MSDN上关于模板类实例化的注意事项:
    Explicit Instantiation
    Explicit instantiation lets you create an instantiation of a templated class or function without actually using it in your code. Since this is useful when you are creating library (.LIB) files that use templates for distribution, uninstantiated template definitions are not put into object (.OBJ) files.The following explicitly instantiates MyStack for int variables and six items:template class MyStack<int, 6>;This statement creates an instantiation of MyStack without reserving any storage for an object; code is generated for all members.The following explicitly instantiates only the constructor member function:template MyStack<int, 6>::MyStack( void );Visual C++ 6.0 supports explicit instantiation of function templates. Versions prior to 5.0 supported the explicit instantiation of class templates only. For example, the following code is now legal:template<class T> void f(T) {...}//Instantiate f with the explicitly specified template
    //argument ‘int’
    //
    template void f<int> (int);//Instantiate f with the deduced template argument ‘char’
    //
    template void f(char);Microsoft Specific You can use the extern keyword to prevent the automatic instantiation of members. For example:extern template class MyStack<int, 6>;Similarly, you can  specific members as being external and not instantiated as follows:extern template MyStack<int, 6>::MyStack( void );
      

  27.   

    Differences from Other Implementations
    Microsoft Specific Templates are not officially standardized and, as a result, different C++ compiler vendors have implemented them differently. The following list shows some differences between this version of Visual C++ and other compilers. Note that this list will change in future versions of the compiler. The compiler cannot instantiate a template outside of the module in which it is defined.
    Templates cannot be used with functions declared with __declspec (dllimport) or __declspec (dllexport).
    All template arguments must be of an unambiguous type that exactly matches that of the template parameter list. For example:
    template< class T > T check( T );
    template< class S > void watch( int (*)(S) );
    watch( check );     //errorThe compiler should instantiate the check templated function in the form int check( int ), but the inference can not be followed.Friend functions must be declared before they are used in a templated class. You cannot have a friend function defined within a class definition. This is because the friend function could be a templated function, which would cause an illegal nested template definition. 
      

  28.   

    由这些资料看来,DLL中不能用传递过来的模板类参数的原因,正在于VC不支持在不同模块间共用一个模板类的实例.即使模板参数相同,DLL模块和EXE模块所使用的,其实是std::map的实例化出的不同的实现.类不同,里面的静态变量,如_Nil之类的当然占用不同的空间,且两类的对象间无法互相操作.
    资料中的一个重要结论:模板类只能用在一个程序模块中.
    按此逻辑,MFC的CMap在DLL和EXE中的实例也是不同的,但因没有用到类静态变量,所有操作都仅对成员变量或全局变量进行操作,因些,虽然传递了一个不相关的类对象的指针,但因为类虽不同,操作却是一样的---都是CMap,又造成可以在EXE/DLL间互操作的假象.这样的操作在VC的目前实现中算是对VC的模板实现不足的一个补充吧--虽然它不那么不被MS的标准规范支持,但却具有现实可行性.
      

  29.   

    我已经试过了.就是我在C++分坛讲的原因.把操作(new CMyMap,insert,clear)全委托到dll中就没有问题了.
      

  30.   

    QunKangLi(李群康):多谢你的分析,我想这就是最后的答案了。也给我敲响了警钟,我以后设计程序的时候必须考虑这种特殊情况。同时,使用CMap做为解决方案看来也是存在隐患的。我考虑在我的程序中增加一个专门导出类的dll,其他地方去调用dll里面的类,这样应该免同一组代码多处实现的问题。谢谢大家。
      

  31.   

    其实在头一次分析它时认识都还有点不全.只有当多个版本的DLL在EXE地址空间中共存时,同一个类的静态变量才会生成多个,每个版本的一个.但都是链接的DLL时,不存在多个变量.
    跟踪到的变量地址不同的原因仅仅因为它们属于不同类的原因.
      

  32.   

    QunKangLi(李群康):好像我的帖子结的太着急了。^_^我看懂你的意思了,非常感谢。
      

  33.   

    QunKangLi(李群康):得到你的帮助真好,你真的给我解答了大难题了。