STL 跨模块 调用 异常 解决

百度了一天,现在把结论放上边:

1、不要用STL(std::string属于STL)来跨模块传输数据,例如:dll(so)之间,dll(so)和exe(elf)之间。

解决方法:使用基本类型、数组、结构体,或者使用下面文章中的方法。

2、不要跨模块申请和释放内存。

解决方法:可以实现一个接口来释放,其他方法参考下面。

今天用个测试exe调用了个dll,有个接口返回std::string,经调试发现挂在该函数return之后,怀疑是string不适合作为返回值,百度一番发现下面这篇解释的很详细。

用了很久的dll也会出问题,而且他们用没事,他们用的是vs2010未升级,我怀疑是vs2010升级sp1后和之前的stl已经有所不同。看来stl的版本和编译选项的不同会导致crash。

 

转自http://blog.163.com/cp7618@yeah/blog/static/70234777201241154218989/

STL跨平台调用会出现很多异常,你可以试试.
STL使用模板生成,当我们使用模板的时候,每一个EXE,和DLL都在编译器产生了自己的代码,导致模板所使用的静态成员不同步,所以出现数据传递的各种问题,下面是详细解释。
原因分析:
一句话-----如果任何STL类使用了静态变量(无论是直接还是间接使用),那么就不要再写出跨执行单元访问它的代码。 除非你能够确定两个动态库使用的都是同样的STL实现,比如都使用VC同一版本的STL,编译选项也一样。强烈建议,不要在动态库接口中传递STL容器!!
STL不一定不能在DLL间传递,但你必须彻底搞懂它的内部实现,并懂得为何会出问题。
微软的解释:
http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b172396
微软给的解决办法:
http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b168958
1、微软的解释:
大部分C++标准库里提供的类直接或间接地使用了静态变量。由于这些类是通过模板扩展而来的,因此每个可执行映像(通常是.dll或.exe文件)就会存在一份只属于自己的、给定类的静态数据成员。当一个需要访问这些静态成员的类方法执行时,它使用的是“这个方法的代码当前所在的那份可执行映像”里的静态成员变量。由于两份可执行映像各自的静态数据成员并未同步,这个行为就可能导致访问违例,或者数据看起来似乎丢失或被破坏了。
可能不太好懂,我举个例子:假如类A<T>有个静态变量m_s,那么当1.exe使用了2.dll中提供的某个A<int>对象时,由于模板扩展机制,1.exe和2.dll中会分别存在自己的一份类静态变量A<int>.m_s。
这样,假如1.exe中从2.dll中取得了一个的类A<int>的实例对象a,那么当在1.exe中直接访问a.m_s时,其实访问的是 1.exe中的对应拷贝(正确情况应该是访问了2.dll中的a.m_s)。这样就可能导致非法访问、应当改变的数据没有改变、不应改变的数据被错误地更改等异常情形。
原文:
Most classes in the Standard C++ Libraries use static data members directly or indirectly. Since these classes are generated through template instantiation, each executable image (usually with DLL or EXE file name extensions) will contain its own copy of the static data member for a given class. When a method of the class that requires the static data member is executed, it uses the static data member in the executable image in which the method code resides. Since the static data members in the executable images are not in sync, this action could result in an access violation or data may appear to be lost or corrupted.
1、保证资源的分配/删除操作对等并处于同一个执行单元;
比如,可以把这些操作(包括构造/析构函数、某些容器自动扩容{这个需要特别注意}时的内存再分配等)隐藏到接口函数里面。换句话说:尽量不要直接从dll中输出stl对象;如果一定要输出,给它加上一层包装,然后输出这个包装接口而不是原始接口。
2、保证所有的执行单元使用同样版本的STL运行库。
比如,全部使用release库或debug库,否则两个执行单元扩展出来的STL类的内存布局就可能会不一样。
只要记住关键就是:如果任何STL类使用了静态变量(无论是直接还是间接使用),那么就不要再写出跨执行单元访问它的代码。
解决方法:
1. 一个可以考虑的方案
比如有两个动态库L1和L2,L2需要修改L1中的一个map,那么我在L1中设置如下接口
int modify_map(int key, int new_value);
如果需要指定“某一个map”,则可以考虑实现一种类似于句柄的方式,比如可以传递一个DWORD
不过这个DWORD放的是一个地址
那么modify_map就可以这样实现:
int modify_map(DWORD map_handle, int key, int new_value)
{
std::map<int, int>& themap = *(std::map<int, int>*)map_handle;
themap[key] = new_value;
}
map_handle的值也首先由L1“告诉”L2:
DWORD get_map_handle();
L2可以这样调用:
DWORD h = get_map_handle();
modify_map(h, 1, 2);
2. 加入一个额外的层,就可以解决问题。所以,你需要将你的Map包装在dll内部,而不是让它出现在接口当中。动态库的接口越简单越好,不好去传太过复杂的东东是至理名言:)

下面这篇文章也不错,收藏之。

 

http://www.hellocpp.net/Articles/Article/714.aspx

跨dll使用template/STL需要注意的问题

template 是个好东西啊 . 经典的 stl . 强悍的boost. 还有我自己写的那个 ------- 该死的 ------- 资源管理器.
dynamic link也是个好东西啊. 在windows下叫dll, 在unix下叫so (share object) . 它能省下很多重新发布软件带来的麻烦.

但是当template 遭遇到dynamic link 时候, 很多时候却是一场恶梦.
现在来说说一部分我已经碰到过的问题. 问题主要集中在内存分配上.
1> 
拿STL来说, 自己写模板的时候,很难免就用到stl. stl的代码都在头文件里. 那么表示着内存分配的代码.只有包含了它的cpp 编译的时候才会被决定是使用什么样的内存分配代码. 考虑一下: 当你声明了一个vector<> . 并把这个vector<>交给一个 dll里的代码来用. 用完后, 在你的程序里被释放了. 那么如果你 在dll里往vector里insert了一些东西. 那么这个时候insert 发生的内存分配的代码是属于dll的. 你不知道这个dll的内存分配是什么. 是分配在哪里的. 而这个时候.释放那促的动作却不在dll里.....同时. 你甚至无法保证编译dll的那个家伙使用的stl版本和你是完全一样的..>
如此说来, 程序crash掉是天经地义的.... 
对策: 千万别别把你的stl 容器,模板容器在 dll 间传来传去 . 记住string也是....

2> 
你在dll的某个类里声明了一个vector之类的容器. 而没有显式的写这个类的构造和析构函数. 那么问题又来了.
你这个类肯定有操作这vector的函数. 那么这些函数会让vecoter<>生成代码. 这些代码在这个dll里都是一致的. 但是别忘了.你没有写析构函数...... 如果这个时候, 别人在外面声明了一个这样的类.然后调用这个类的函数操作了这个vector( 当然使用者并不知道什么时候操作了vector) . 它用完了这个类以后. 类被释放掉了. 编译器很负责的为它生成了一份析构函数的代码...... 听好了.这份代码并不是在 dll里 ... . 事情于是又和1>里的一样了.... crash ......(可能还会伴随着迷茫.....)
对策: 记得dll里每个类,哪怕式构造析构函数式空的. 也要写到cpp里去. 什么都不写也式很糟糕的.....同时,更要把任何和内存操作有关的函数写到 .cpp 里...

3> 
以上两个问题似乎都是比较容易的-----只要把代码都写到cpp里去, 不要用stl容器传来传去就可以了.
那么第三个问题就要麻烦的多.
如果你自己写了一个模板, 这个模板用了stl 容器..........
这个时候你该怎么办呢?
 显然你无法把和内存分配相关的函数都写到.cpp里去 . template的代码都必须放到header file里.....
对策: 解决这个问题的基本做法是做一个stl 内存分配器 , 强制把这个模板里和内存分配相关的放到一个.cpp里去.这个时候编译这个cpp就会把内存分配代码固定在一个地方: 要么是dll. 要么是exe里...

模板+动态链接库的使用问题还很多. 要千万留心这个陷阱遍地的东西啊

 不要在dll或lib的导出函数以string(cstring)作返回值

http://blog.sina.com.cn/s/blog_5119a7f90100ygzi.html

这是因为string和csting采用了Copy-On-Write技术,Copy-On-Write使用了“引用计数”,这是一种内存共享机制。

假设有一个动态链接库(叫myNet.dll或myNet.so)中有这样一个函数返回的是string类:

string GetIPAddress(string hostname)
{
static string ip;
……
……
return ip;
}

而你的主程序中动态地载入这个动态链接库,并调用其中的这个函数:

main()
{
//载入动态链接库中的函数
hDll = LoadLibraray(…..);
pFun = GetModule(hDll, “GetIPAddress”);

//调用动态链接库中的函数
string ip = (*pFun)(“host1”);
……
……
//释放动态链接库
FreeLibrary(hDll);
……
cout << ip << endl;
}

根 据函数的定义,我们知道函数是“值返回”的,所以,函数返回时,一定会调用拷贝构造函数,又根据string类的内存共享机制,在主程序中变量ip是和函数内部的那个静态string变量共享内存(这块内存区是在动态链接库的地址空间的)。而我们假设在整个主程序中都没有对ip的值进行修改过。那么在当主程序释放了动态链接库后,那个共享的内存区也随之释放。所以,以后对ip的访问,必然做造成内存地址访问非法,造成程序crash。即使你在以后没有使用到ip这个变量,那么在主程序退出时也会发生内存访问异常,因为程序退出时,ip会析构,在析构时就会发生内存访问异常。

时间: 2025-01-21 18:52:39

STL 跨模块 调用 异常 解决的相关文章

Thinkphp入门 二 —空操作、空模块、模块分组、前置操作、后置操作、跨模块调用(46)

原文:Thinkphp入门 二 -空操作.空模块.模块分组.前置操作.后置操作.跨模块调用(46) [空操作处理] 看下列图: 实际情况:我们的User控制器没有hello()这个方法 一个对象去访问这个类不存在的方法,那么它会去访问"魔术方法__call()" 用户访问一个不存在的操作->解决:给每个控制器都定义个_empty()方法来处理   第二个解决方法:定义一个空操作 [空模块处理] 我们使用一个类,但是现在这个类还没有被include进来. 我们可以通过自动加载机制处

ThinkPHP 跨模块调用与找不到方法问题

在做php开发的时候,有时候我在一个模块里面写了一个方法,但是我要在另外的一个模块内调用,此时就需要跨模块调用了,比如现在我有一个usersAction这个模块 内有一个test()方法 我在company内要调用 此时如何进行呢?有2种办法  代码如下 复制代码 一:$User = A("User"); // 实例化UserAction控制器对象 $User->importUser(); // 调用User模块的importUser操作方法 这里的A("User&qu

ThinkPHP跨控制器调用方法

跨控制器调用方法 1. 先造对象,再调用里面的方法 $sc=new \Home\Controller\IndexController();  用绝对路径找echo $sc->ShuChu(); 2. $sc=new IndexController();  用相对路径 echo $sc->ShuChu(); 还可以使用thingkphp中的快捷方法 1. $sc=A("Index");      使用TP框架的快捷方法A来创建控制器对象  ()内直接写控制器名 [跨模块调用]

ASP.NET配合jQuery解决跨域调用的问题_实用技巧

一. 使用JSONp方式调用 不做详细讲解,可以参考jq文档<jQuery 1.10.3 在线手册> 二. 服务端配置 修改Web.config 文件 <system.webServer> <modules runAllManagedModulesForAllRequests="true"></modules> <httpProtocol> <customHeaders> <add name="Ac

连接池和 Timeout expired异常解决方法

连接池和 timeout expired异常解决方法 你需要保证你每次调用连接的同时都在使用过后通过close()或dispose()对其执行了关闭.最简单的办法就是使用using,将你的连接泄漏方法修改成如下面的代码样式: public void doesnotleakconnections()     {            using (sqlconnection sqlconnection1 = new sqlconnection("server=.sqlexpress ;integr

JavaScript跨域总结与解决办法

什么是跨域 JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象.但在安全限制的同时也给注入iframe或是ajax应用上带来了不少麻烦.这里把涉及到跨域的一些问题简单地整理一下: 首先什么是跨域,简单地理解就是因为JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象.更详细的说明可以看下表: 特别注意两点: 第一,如果是协议和端口造成的跨域问题"前台"是无能为力的, 第二:在跨域问题上,域仅仅是通过"U

AsyncHttpClient 中的重定向和 setEnableRedirects 方法异常解决

今天使用 AsyncHttpClient  开源库,遇到个很崩溃的问题: 方法  setEnableRedirects(false); 从名称上看应该是重定向开关的方法,设置为 false 后则普通请求正常,但是遇到重定向则停止请求,还算正常(文章末尾会谈到这里会遇到另一个 BUG 的情况) 但当设置为  setEnableRedirects(true);  按理说应该是对于 301.302 重定向将进行跟随重定向进行请求的,但却出现各种异常和乱七八糟的问题,不论是普通请求还是重定向的请求全是 

ajax-js的问题 是关于跨页面调用方法的问题

问题描述 js的问题 是关于跨页面调用方法的问题 js的问题 是关于跨页面调用方法的问题 前台A页面 掉用B页面后台的一个方法 怎么调用呢 解决方案 ajax $.get('b页面的连接',funcition(){}); 解决方案二: 关于asp.net前台js调用后台方法的问题解决js jq跨子域无权限问题方法关于调用JS文件乱码问题处理方法 解决方案三: ajax $.get('b页面的连接',funcition(){}); 解决方案四: ajax $.get('b页面的连接',funcit

pyopengl-Python3.5.1 PyopenGL模块调用出错

问题描述 Python3.5.1 PyopenGL模块调用出错 测试PyopenGL模块的源代码来自网上: from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * def drawFunc(): glClear(GL_COLOR_BUFFER_BIT) #glRotatef(1, 0, 1, 0) glutWireTeapot(0.5) glFlush() glutInit() glutInitDisp