OVH Cloud OVH Cloud

[C++] std::string entre dlls

3 réponses
Avatar
Michaël Monerau
Bonjour,

J'ai récemment rencontré un problème assez gênant avec std::string, de la
STL C++. Dans une dll, j'ai :

std::string GetExePath ()
{
// blah...
}

Cependant, si j'appelle cette fonction depuis un autre module (l'exe
appelant ou une autre dll), j'ai immanquablement une erreur de heap (dans
deallocate de l'appelant, l'espace de l'autre module tente d'être désalloué,
ce qui provoque une erreur "mauvais heap pour résumer" dans crtdebug.c.

Donc, d'après ce que j'ai compris, le pointeur vers la chaîne de caractère
reste le même quand le std::string est copié pour aller dans un autre module
(le COW (Copy On Write)), et comme la portée de la copie arrive à sa fin
dans le module appelant, le destructeur est appelé qui essaye de désallouer
une zone mémoire qui ne lui appartient pas.

Déjà, est-ce que j'ai bon ?
Ensuite, comment corriger ce problème ? (c'est très gênant pour les
lancements d'exceptions, etc...) Faut-il que je réécrive une class string
module-safe ?

Merci
--
<=- Michaël "Cortex" Monerau -=>

3 réponses

Avatar
Arnaud Debaene
Michaël Monerau wrote:
Bonjour,


Bonjour.

J'ai récemment rencontré un problème assez gênant avec std::string,
de la STL C++. Dans une dll, j'ai :

std::string GetExePath ()
{
// blah...
}

Cependant, si j'appelle cette fonction depuis un autre module (l'exe
appelant ou une autre dll), j'ai immanquablement une erreur de heap
(dans deallocate de l'appelant, l'espace de l'autre module tente
d'être désalloué, ce qui provoque une erreur "mauvais heap pour
résumer" dans crtdebug.c.

Donc, d'après ce que j'ai compris, le pointeur vers la chaîne de
caractère reste le même quand le std::string est copié pour aller
dans un autre module (le COW (Copy On Write))


Seul VC6 utilise le COW, mais ce n'est pas la raison de ton erreur : Le COW
est parfaitement fiable tant qu'on ne fait pas de multithreading (là c'est
un autre paire de manches).

, et comme la portée de
la copie arrive à sa fin dans le module appelant, le destructeur est
appelé qui essaye de désallouer une zone mémoire qui ne lui
appartient pas.

Déjà, est-ce que j'ai bon ?


Oui.

Ensuite, comment corriger ce problème ? (c'est très gênant pour les
lancements d'exceptions, etc...) Faut-il que je réécrive une class
string module-safe ?



En fait, le message d'erreur est très clair : "mauvais heap pour résumer".
Autrement dit, ta chaine a allouée sa mémoire dans un tas, et c'est le
delete[] d'un autre tas qui tente de la libérer. Pourquoi cela? Tout
simplement parce que ta DLL et ton exe ont été linké statiqueemnt avec la
CRT, autrement dit ils ont chacun leur propre version du gestionnaire
d'allocation (new / delete), chacun tenant ses comptes de ce qu'il a alloué
séparément. Donc forcément, quand tu files au delete de l'executable un
pointeur verz une zonemémoire qui a été allouée par le new de la DLL, il ne
reconnait pas cette zone de mémoire car il ne l'a pas allouée, et il
s'arrête en erreur.

La solution est que les différents modules utilisent la même version de la
CRT, avec un allocateur mémoire commun : Pour cela, les différents modules
doivent êtres liés avec la même version DLL de la CRT (la CRT est dans
msvcrt.dll, qui est chargée une seule fois pour tous les modules du
programme) : Cela se fait avec les flags /MD (debug) ou /MDd (release) du
compilateur : Attention de ne pas mélanger les deux ou tu retomberas sur le
même problème.

Merci


De rien.

Arnaud
MVP - VC
Avatar
Michaël Monerau
Arnaud Debaene wrote:
Michaël Monerau wrote:
Donc, d'après ce que j'ai compris, le pointeur vers la chaîne de
caractère reste le même quand le std::string est copié pour aller
dans un autre module (le COW (Copy On Write))


Seul VC6 utilise le COW, mais ce n'est pas la raison de ton erreur :
Le COW est parfaitement fiable tant qu'on ne fait pas de
multithreading (là c'est un autre paire de manches).



OK, je ne savais pas pour le COW. Mais, n'étant plus présent dans VC 7+, je
me doute qu'il n'y a plus de problèmes avec std::string et le multithreading
dans VC 7+ ?

Déjà, est-ce que j'ai bon ?


Oui.



Cool :-)

Ensuite, comment corriger ce problème ? (c'est très gênant pour les
lancements d'exceptions, etc...) Faut-il que je réécrive une class
string module-safe ?



En fait, le message d'erreur est très clair : "mauvais heap pour
résumer". Autrement dit, ta chaine a allouée sa mémoire dans un tas,
et c'est le delete[] d'un autre tas qui tente de la libérer. Pourquoi
cela? Tout simplement parce que ta DLL et ton exe ont été linké
statiqueemnt avec la CRT, autrement dit ils ont chacun leur propre
version du gestionnaire d'allocation (new / delete), chacun tenant
ses comptes de ce qu'il a alloué séparément. Donc forcément, quand tu
files au delete de l'executable un pointeur verz une zonemémoire qui
a été allouée par le new de la DLL, il ne reconnait pas cette zone de
mémoire car il ne l'a pas allouée, et il s'arrête en erreur.



OK, c'est bien ce que j'avais compris, en mieux expliqué :)

La solution est que les différents modules utilisent la même version
de la CRT, avec un allocateur mémoire commun : Pour cela, les
différents modules doivent êtres liés avec la même version DLL de la
CRT (la CRT est dans msvcrt.dll, qui est chargée une seule fois pour
tous les modules du programme) : Cela se fait avec les flags /MD
(debug) ou /MDd (release) du compilateur : Attention de ne pas
mélanger les deux ou tu retomberas sur le même problème.



OK, je vais voir ça. J'ai en effet eu pas mal de problèmes avec ces options
de CRT : si je mettais toutes en Multithreading Debug, mon prog ne se
lançait pas (enfin, un truc bizarre du genre), etc... Je vais rebidouiller
là-dedans et si j'ai un problème, je reposte :)

Merci bien.
--
<=- Michaël "Cortex" Monerau -=>
Avatar
Arnaud Debaene
Michaël Monerau wrote:
Arnaud Debaene wrote:
Michaël Monerau wrote:
Donc, d'après ce que j'ai compris, le pointeur vers la chaîne de
caractère reste le même quand le std::string est copié pour aller
dans un autre module (le COW (Copy On Write))


Seul VC6 utilise le COW, mais ce n'est pas la raison de ton erreur :
Le COW est parfaitement fiable tant qu'on ne fait pas de
multithreading (là c'est un autre paire de manches).



OK, je ne savais pas pour le COW. Mais, n'étant plus présent dans VC
7+, je me doute qu'il n'y a plus de problèmes avec std::string et le
multithreading dans VC 7+ ?



Exact.

<snip>
OK, je vais voir ça. J'ai en effet eu pas mal de problèmes avec ces
options de CRT : si je mettais toutes en Multithreading Debug, mon
prog ne se lançait pas (enfin, un truc bizarre du genre)



en multithreading debug *DLL* ? Sans doute un module non recompilé qui
n'était pas lié à cette version de la CRT.


Arnaud