[C++] std::string doorgeven aan DLL; heap corrupt?

Pagina: 1
Acties:

  • Rowwan
  • Registratie: November 2000
  • Laatst online: 22:15
Onlangs heb ik geprobeert een dll te schrijven in C++ om enkele protocol decodaties te doen. Deze moet uiteindelijk gaan communiceren met labview.. Nu heb ik het volgende probleem.

DLL header (uiteraard met __declspec):
code:
1
void drukAf(const std::string message);


DLL functie:
code:
1
2
3
4
void drukAf(const std::string message)
{
    printf("%s", message.c_str());
}


Main code:
code:
1
2
3
std::string test = "BLAAT";
drukAf(test);         // OK
drukAf("BLAAT"); // Crash!


De laatste variant crash met een exceptie in dbgheap.c (_CrtIsValidHeapPointer)

Wat doe ik nu verkeerd? Ik heb vanalles geprobeert maar kom er echt niet ui. (BTW. bij beide aanroepen wordt er goed afgedrukt, het gaat mis bij het retourneren uit de functie.)

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 17-05 17:19
Als je je test variabele gebruikt in de dll call gaat het ook fout? Misschien gebruikt je een buggie (VC6) stl?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Verwijderd

code:
1
2
3
4
void blaat(const std::string[b]&[/b] message )
{
   printf("message: %s\n", message.c_str());
}


vooral dat & zou een oplossing kunnen bieden...

verder ben je verplicht met DLL's om je buildsettings op "Multi-threaded Debug DLL (/MDd)" te zetten. dit vindt je in visual studio.net op de volgende plek:
Project Properties / C/C++ / Code Generation / Runtime Library

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Ik kan me verder nog voorstellen dat je twee verschillende runtime libraries gebruikt, bv "debug" in je dll, en "release" in je test code. Dan krijg je in ieder geval incompatibiliteit tussen de twee heaps, en kan het voorkomen dat je programma crashed bij bv het vrijgeven van geheugen. Als ik het zo zie denk ik dat die std::string gemaakt wordt in je client app, en gewist in je dll... Het gaat mis bij de functie return, blijkbaar gaat er dus iets mis met het verwijderen van die std::string van de stack. (stack corruption) Bovenstaande is het enige dat ik kan bedenken...

(maw, die string alloceert heap geheugen in je client, en je DLL geeft dat vrij bij stack unwinding en string dtor. Mix nooit geheugen calls in client/dll paar. Maak dan een createObject() en een deleteThis(Object*) functie paar in je dll (3ds max plugins bv). een van de twee moet het geheugen dat gemixed wordt beheren)

[ Voor 27% gewijzigd door Zoijar op 04-01-2005 23:11 ]


  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Vreemd, ik zou juist eerder verwachten door de parameter by reference (dwz met '&') door te geven problemen te krijgen. De 'test'-parameter staat nl. op de heap, terwijl de constante "BLAAT" dat niet is. In dat geval zou je twee verschillende 'soorten' pointers doorgeven, wat evt. voor vreemde effecten zou kunnen zorgen (en dan eigenlijk ook alleen nog maar als je het gepassde object probeerde te deleten vanuit de DLL).

Nu (zonder '&') wordt van beide een locale kopie gemaakt, die vervolgens aan de DLL wordt doorgegeven: beide functies hebben nu precies hetzelfde effect op de DLL-functie. Ik denk dan ook dat dit niet het probleem kan zijn (ook al zou door gebruik van '&' het effect wel kunnen verdwijnen).

Probeer eens de twee functie-aanroepen om te wisselen (dus eerst met een constante "BLAAT", daarna met 'test'), of door 1 van beide aanroepen weg te laten, en kijk of de error nog steeds na de laatste aanroep gebeurt. Ik verwacht van wel, nl.

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

MrBucket schreef op dinsdag 04 januari 2005 @ 23:19:
Vreemd, ik zou juist eerder verwachten door de parameter by reference (dwz met '&') door te geven problemen te krijgen. De 'test'-parameter staat nl. op de heap, terwijl de constante "BLAAT" dat niet is. In dat geval zou je twee verschillende 'soorten' pointers doorgeven, wat evt. voor vreemde effecten zou kunnen zorgen (en dan eigenlijk ook alleen nog maar als je het gepassde object probeerde te deleten vanuit de DLL).
Nee, een DLL wordt in je applicatie address-space geladen; pointers naar heap geheugen kunnen dus vrij worden uitgewisseld, want je zit feitelijk in hetzelfde geheugen. Net als met threads.

Verwijderd

MrBucket schreef op dinsdag 04 januari 2005 @ 23:19:
Nu (zonder '&') wordt van beide een locale kopie gemaakt, die vervolgens aan de DLL wordt doorgegeven: beide functies hebben nu precies hetzelfde effect op de DLL-functie. Ik denk dan ook dat dit niet het probleem kan zijn (ook al zou door gebruik van '&' het effect wel kunnen verdwijnen).
nou nee, de kopie wordt gemaakt aan de aanroepende kant... dat is dus nog steeds buiten de DLL, deze kopie wordt vervolgens op de stack gepushed, (overigens gewoon als pointer), en vervolgens wordt pas de DLL functie aangeroepen....

by-value, by-ref of by-pointer, het maakt geen kloot uit als het om classes gaat, er wordt altijd een pointer op de stack gepushed.

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Zoijar schreef op dinsdag 04 januari 2005 @ 23:21:
[...]

Nee, een DLL wordt in je applicatie address-space geladen; pointers naar heap geheugen kunnen dus vrij worden uitgewisseld, want je zit feitelijk in hetzelfde geheugen. Net als met threads.
Klopt, maar als je DLL release-built is en je aanroepende code debug (of vice versa), en je probeert vanuit de DLL een object te deleten wat in je aanroepende code is aangemaakt, dan krijg je dus wel een puinhoop op je heap, omdat de layout ervan verschilt tussen debug en release code.

Desalniettemin, kijk naar het type build (debug of release) van zowel je aanroepende code als van de DLL, en (omdat std::string een templated type is) kijk ook naar welk concreet type std::string herleid wordt voordat het gelinkt wordt. Goede kans dat de fout in een van deze 2 punten zit.

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

MrBucket schreef op dinsdag 04 januari 2005 @ 23:29:
Klopt, maar als je DLL release-built is en je aanroepende code debug (of vice versa), en je probeert vanuit de DLL een object te deleten wat in je aanroepende code is aangemaakt, dan krijg je dus wel een puinhoop op je heap, omdat de layout ervan verschilt tussen debug en release code.
Dat poste ik vlak daarvoor al ja ;)

Verwijderd

MrBucket schreef op dinsdag 04 januari 2005 @ 23:29:
[...]

Klopt, maar als je DLL release-built is en je aanroepende code debug (of vice versa), en je probeert vanuit de DLL een object te deleten wat in je aanroepende code is aangemaakt, dan krijg je dus wel een puinhoop op je heap, omdat de layout ervan verschilt tussen debug en release code.

Desalniettemin, kijk naar het type build (debug of release) van zowel je aanroepende code als van de DLL, en (omdat std::string een templated type is) kijk ook naar welk concreet type std::string herleid wordt voordat het gelinkt wordt. Goede kans dat de fout in een van deze 2 punten zit.
das inderdaad een vaak voorkomende fout, dit is overigens op te lossen door een memory manager DLL te schrijven, en al het geheugen door deze dll te laten alloceren. Dit is wel veel werk, new/delete operators overloaden, malloc/free wrappen, allocater templates maken voor STL, e.t.c. maar als je dat allemaal eenmaal op orde hebt...

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Verwijderd schreef op dinsdag 04 januari 2005 @ 23:26:
[...]


nou nee, de kopie wordt gemaakt aan de aanroepende kant... dat is dus nog steeds buiten de DLL, deze kopie wordt vervolgens op de stack gepushed, (overigens gewoon als pointer), en vervolgens wordt pas de DLL functie aangeroepen....

by-value, by-ref of by-pointer, het maakt geen kloot uit als het om classes gaat, er wordt altijd een pointer op de stack gepushed.
Het gaat erom naar welk stuk geheugen die gepushde pointer verwijst, niet waar die pointer staat (want die staat in alle gevallen op de stack, of je moet de fastcall-conventie gebruiken).

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
MrBucket schreef op dinsdag 04 januari 2005 @ 23:29:
Desalniettemin, ...(omdat std::string een templated type is) kijk ook naar welk concreet type std::string herleid wordt voordat het gelinkt wordt.
Eh, nee. std::string is geen templated type, maar een typedef. En bovendien is het altijd dezelfde typedef, namelijk std::basic_string<char, std::allocator<char> >.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Verwijderd schreef op dinsdag 04 januari 2005 @ 22:54:
verder ben je verplicht met DLL's om je buildsettings op "Multi-threaded Debug DLL (/MDd)" te zetten.
Dit is vrijwel altijd het goede antwoord (nou ja, release is /MD Multi-threaded DLL)

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
MSalters schreef op dinsdag 04 januari 2005 @ 23:44:
[...]

Dit is vrijwel altijd het goede antwoord (nou ja, release is /MD Multi-threaded DLL)
Ook als je aanroepende code release built is?
Niet om je af te troeven ofzo, dit is een serieuze vraag

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

MrBucket schreef op dinsdag 04 januari 2005 @ 23:51:
Ook als je aanroepende code release built is?
Niet om je af te troeven ofzo, dit is een serieuze vraag
Het moet matchen, omdat je anders een debug met een release heap mixed; dat kan niet. Sterker nog, je kan niet eens verschillende compilers gebruiken als je geheugen op die manier deelt. Bv vc6 dll met vc7 code zou ook crashen. Daarom moet je dus _nooit_ allocatie en free operaties mixen...

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Zoijar schreef op dinsdag 04 januari 2005 @ 23:58:
[...]

Het moet matchen, omdat je anders een debug met een release heap mixed; dat kan niet. Sterker nog, je kan niet eens verschillende compilers gebruiken als je geheugen op die manier deelt. Bv vc6 dll met vc7 code zou ook crashen. Daarom moet je dus _nooit_ allocatie en free operaties mixen...
Ah, ik snap 'm. Met "je moet altijd in multithreaded debug mode linken" werd bedoeld "...als je aanroepende code ook debug mode is".

Ik dacht even dat er werd bedoeld "ongeacht of de aanroepende code debug of release mode is". Ik vond 't al zo vreemd ;)

  • Rowwan
  • Registratie: November 2000
  • Laatst online: 22:15
Thanx voor jullie antwoorden... _/-\o_ .. De optie /MDd werkt inderdaad...

Verdere info:
  • De '&' toevoegen maakt niets uit.
  • Ik bouwde beide files in debug mode
  • Als ik ze beide in release mode bouwde, ging het wel goed
Trouwens wel vreemd dat ik default de optie /MDd moet bijvoegen :?. Waarom doet visual studio dit niet als ik een DLL project aanmaak...
Pagina: 1