Unter VC++ 6.0/Windows XP SP2 habe ich folgendes Problem in dem angegebenen
Szenario:
- In einer DLL wird mit calloc() Speicher alloziert
- In einer EXE wird eine Funktion der DLL aufgerufen, über die dieser
Speicher (in einer Struktur) zurückgegeben wird
Der Speicher wird korrekt alloziert und kann in der EXE verwendet werden.
Wird der Speicher dann jedoch mit free() in der EXE freigegeben, kommt es
>nur in der Debug-Version< zu folgendem Fehler:
Debug Assertion Failed
...
File: dbgheap.c
Line: 1044
...
Expression: _CrtIsValidHeap(pUserData)
...
Die DLL wird mit "Multithreaded DLL debuggen", die EXE mit "Multithreaded
debuggen" kompiliert.
Schematisch(!) dargestellt mache ich folgendes:
----- DLL
Struktur.{LPCSTR}lpszFile = calloc(...)
----- EXE
{LPCSTR}lpszFile = Struktur.lpszFile
Struktur.lpszFile = NULL
...
free((LPSTR)lpszFile)
Hat jemand eine Idee, wo mein Fehler liegen könnte? Ich habe mich zwar
etwas in die unterschiedliche Verfahrensweise bei malloc() etc. in Release
und Debug eingelesen, aber Schlüsse auf meinen Fehler konnte ich nicht
ziehen. Google liefert viele Treffer, aber auch dort finde ich keinen
passender Hinweis.
Für Hilfe wäre wie immer dankbar,
Thorsten Albers
--
Thorsten Albers
albers (a) uni-freiburg.de
Thorsten Albers wrote:
> Hi, Folks!
> <snip>
> Hat jemand eine Idee, wo mein Fehler liegen könnte? Ich habe mich zwar
> etwas in die unterschiedliche Verfahrensweise bei malloc() etc. in Release
> und Debug eingelesen, aber Schlüsse auf meinen Fehler konnte ich nicht
> ziehen. Google liefert viele Treffer, aber auch dort finde ich keinen
> passender Hinweis.
>
Das ist der Suendenfall schlechthin, einen runtime-Allokator zu
verwenden und den Eigentuemer in Form der Module zu wechseln. Nimm einen
runtime-unabhaengigen Allokator wie etwa LocalAlloc und das Problem geht
vorbei.
--
S
Aber das wäre ja extrem besch... Ich meine, die DLL und die EXE gehören zum
selben Prozess und verwenden denselben virtuellen Speicher, und trotzdem
sollte sich über malloc() allozierter Speicher nicht zwischen beiden
austauschen lassen? Und wo liegt der Unterschied zwischen malloc() und
LocalAlloc(), wenn beide Heap-Speicher allozieren?
Da ich in der Release-Version keinen Speicherverlust beobachten kann,
vermutete ich zuletzt, daß es an der besonderen Verwaltungsmethode des
Heap-Speichers im Debug-Modus liegen könnte. Aber selbst da fände ich es
unlogisch, da DLL und EXE dienselbe Speichverwaltung verwenden.
Na ja, ich werde 'mal Deinem Rat folgen und auf LocalAlloc() umstellen -
aber die Logik bei MS kann ich da nicht erkennen...
O.k., mir ist jetzt klar, daß es mit der Speicherverwaltung von Windows NT
zusammenhängt. Bisher habe ich immer unter Windows 98 SE entwickelt und
dann die Release-Version unter Windows XP getestet - da trat der Fehler
dann natürlich nie auf....
> Aber das wäre ja extrem besch... Ich meine, die DLL und die EXE gehören zum
> selben Prozess und verwenden denselben virtuellen Speicher, und trotzdem
> sollte sich über malloc() allozierter Speicher nicht zwischen beiden
> austauschen lassen?
Du kannst das wu8nderbar machen, *solange* Du genau die selber CRT
verwendest!!! Also die "DLL-Version der gleichen CRT-Version"!
Dann gibt es keine Probleme.
Jede CRT verwendet Ihren eigenen Heap; deshalb gibt es Probleme, wenn Du
unterschiedliche CRTs verwendest (oder gar statisch linkst).
> Und wo liegt der Unterschied zwischen malloc() und
> LocalAlloc(), wenn beide Heap-Speicher allozieren?
malloc gehört zur CRT; somit musst Du die selbe CRT verwenden, wenn Du
malloc/free verwenden willst.
LocalAlloc gehört zur WinAPI. Somit musst Du die selbe WinAPI verwenden,
wenn Du dies benutzen willst (und die selbe WinAPI verwendest Du ja
schon implizit; somit ist dieser Fall einfacher ;) ).
> Da ich in der Release-Version keinen Speicherverlust beobachten kann,
> vermutete ich zuletzt, daß es an der besonderen Verwaltungsmethode des
> Heap-Speichers im Debug-Modus liegen könnte.
Mit Debug/Release hat das nix zu tun. Nur im Debug-Mode wird halt die
Fehlermeldung ausgegeben.
> Aber selbst da fände ich es
> unlogisch, da DLL und EXE dienselbe Speichverwaltung verwenden.
Wenn dies so wäre, dann hättest Du kein Problem.
> Na ja, ich werde 'mal Deinem Rat folgen und auf LocalAlloc() umstellen -
> aber die Logik bei MS kann ich da nicht erkennen...
Stelle Deine beiden Projekte auf die selbe DLL-CRT um, dann gibt es auch
keine Probleme.
--
Greetings
Jochen
My blog about Win32 and .NET
http://blog.kalmbachnet.de/
> Die DLL wird mit "Multithreaded DLL debuggen", die EXE mit "Multithreaded
> debuggen" kompiliert.
Somit verwendest Du *nicht* die selbe CRT und Dein Vorhaben muss
schiefgehen.
Stelle beides auf das gleiche "* DLL *" um.
Jochen Kalmbach [MVP] wrote:
> <snip>
> Stelle Deine beiden Projekte auf die selbe DLL-CRT um, dann gibt es auch
> keine Probleme.
>
Es wird auch dann Probleme geben, wenn man diesen Ratschlag von Jochen
befolgt, aber die EXE von der DLL unabhaengig updated (oder umgekehrt)
und dabei fuer eine der Komponenten eine neuere oder aeltere
Compilerversion einsetzt. Daher halte ich es fuer sinnvoller, beim
"Besitzertausch" von Speicher zwischen Modulen immer
runtime-unabhaengige Allokatoren zu verwenden. Wenn Thorsten natuerlich
sicherstellen kann, dass er DLL und EXE immer paarweise ausliefert und
alles immer mit derselben Compilerversion und derselben runtime-Variante
gebuildet wurde, kann man auch runtime-basierte Allokatoren verwenden.
--
S
> Es wird auch dann Probleme geben, wenn man diesen Ratschlag von Jochen
> befolgt, aber die EXE von der DLL unabhaengig updated (oder umgekehrt)
> und dabei fuer eine der Komponenten eine neuere oder aeltere
> Compilerversion einsetzt. Daher halte ich es fuer sinnvoller, beim
> "Besitzertausch" von Speicher zwischen Modulen immer
> runtime-unabhaengige Allokatoren zu verwenden. Wenn Thorsten natuerlich
> sicherstellen kann, dass er DLL und EXE immer paarweise ausliefert und
> alles immer mit derselben Compilerversion und derselben runtime-Variante
> gebuildet wurde, kann man auch runtime-basierte Allokatoren verwenden.
Mir persönlich erscheitn es am Sinnvollsten, wenn der übergebene
Parameter bereits Speicher allokiert und nur befüllt wird. Somit kann er
problemlos nacher wierder befreit werden.
--
MfG Alex
> Mir persönlich erscheitn es am Sinnvollsten, wenn der übergebene Parameter
> bereits Speicher allokiert und nur befüllt wird. Somit kann er problemlos
> nacher wierder befreit werden.
Und wenn Du die Größe gar nicht vorher weißt? Dann müsstest Du immer
einen Call zum Größenbestimmen vorwegschicken. Auch lästig, oder?
--
SvenC
> Und wenn Du die Größe gar nicht vorher weißt? Dann müsstest Du immer
> einen Call zum Größenbestimmen vorwegschicken. Auch lästig, oder?
Das ist wahr, aber auf jeden Fall sicherer als die Ursprungsvariante:)
--
Just my 0,02 €
Alex
Aha! Ich dachte nicht, daß in der Beziehung zwischen "Multithreaded DLL
debuggen" und "Multithreaded debuggen" noch ein Unterschied wäre.
Was mich aber wundert: Warum funktioniert das unter Windows 9x, unter
Windows NT aber nicht? Denn an einer unterschiedlichen Speicherverwaltung
des Betriebssystemes, wie ich zwischenzeitlich gedacht hatte, liegt es
offenbar ja nicht.
Kann man Vorteile und/oder Nachteile benennen, wenn ich auch die EXE auf
"Multithreaded DLL debuggen" umstelle?
Und kann man Vorteile und/oder Nachteile einer Umstellung auf LocalAlloc()
benennen?
Vielen Dank für alle Antworten hier im Thread an alle!
> Was mich aber wundert: Warum funktioniert das unter Windows 9x, unter
> Windows NT aber nicht?
Es funktioniert dort genausowenig. Vermutlich hast Du nur die
Release-Version getestet, welche den Fehler nicht meldet.
> Kann man Vorteile und/oder Nachteile benennen, wenn ich auch die EXE auf
> "Multithreaded DLL debuggen" umstelle?
Wenn Du eh schon ein Projekt mit "DLL" hast, dann hat es keine
Nachteile, sondern nur vorteile.
> Und kann man Vorteile und/oder Nachteile einer Umstellung auf LocalAlloc()
> benennen?
Aufwand?
> Mir persönlich erscheitn es am Sinnvollsten, wenn der übergebene
> Parameter bereits Speicher allokiert und nur befüllt wird. Somit kann er
> problemlos nacher wierder befreit werden.
Ich halte die Verursacher Variante immer für am sinnvollsten:
Nur das Modul, das den Speicher allokiert hat darf ihn auch freigeben...
--
Martin Richter [MVP] WWJD http://blog.m-ri.de
"A well-written program is its own heaven; a poorly written
program is its own hell!" The Tao of Programming
FAQ: http://www.mpdvc.de Samples: http://www.codeproject.com
> Ich halte die Verursacher Variante immer für am sinnvollsten:
> Nur das Modul, das den Speicher allokiert hat darf ihn auch freigeben...
Genau das sagte, ...äh schrieb, ich doch eben.
--
Alex
>> Ich halte die Verursacher Variante immer für am sinnvollsten:
>> Nur das Modul, das den Speicher allokiert hat darf ihn auch freigeben...
>
> Genau das sagte, ...äh schrieb, ich doch eben.
Nicht ganz. Martins Variante geht auch, wenn die dll selber ein
"Free"-Funktion anbietet. Dann kann sie Dir einen allozierten
Block rausreichen und zum Freigeben rufst Du die Free-Funktion
aus, die dann eben auch in der dll implementiert ist.
--
SvenC
Jupp! Das erlaubt jede Art von Mischung und Freiheit... ;)
Ein schönes Wochenende allerseits...
Nein. Dieselbe EXE/DLL-Kombination führt unter WinXP zum Fehler, unter
Win98SE nicht.
> Wenn Du eh schon ein Projekt mit "DLL" hast, dann hat es keine
> Nachteile, sondern nur vorteile.
> Aufwand?
Also nichts 'bewegendes'...
Na klar, da kann man geteilter Meinung sein. Ich wuerde aber niemandem
in sein Funktions- oder API-Design dreinreden wollen, wenn er's trotzdem
so macht. Ich finde es grundsaetzlich nicht verkehrt, wenn eine Funktion
allozierten Speicher zurueckgibt, denn das kann auch race conditions
vermeiden helfen: Ansonsten muss der Aufrufer eventuell erst ermitteln
wieviel Speicher er bereitstellen muss um ihn zu allozieren und der
Funktion zu uebergeben. Hat sich zwischenzeitlich der Speicherbedarf
fuer den Aufruf geaendert, muss die Funktion trotzdem scheitern. Das
altbekannte Paradigma im Win32-API, eine Funktion erst gezielt scheitern
zu lasssen um erforderliche Puffergroessen zu ermitteln, dann den
Speicher zu allozieren um schliesslich die Funktion mit einem Puffer der
richtigen Groesse aufzurufen geht eben nicht immer.
--
S
> Na klar, da kann man geteilter Meinung sein. Ich wuerde aber niemandem
> in sein Funktions- oder API-Design dreinreden wollen, wenn er's trotzdem
> so macht.
Dies war nie meine Absicht. Ich habe nur meine Meinung kundgetan. Ich
wollte in keinster Weise jmd. überreden dies nach dem Schema welches ich
benutze zu machen.
Wie gesagt: Nur ein Vorschlag!
--
MfG Alex