Domanda

Ho un file EXE che utilizza un file DLL che utilizza un altro file DLL. Questa situazione è sorta:

Nel file DLL 1:

class abc
{
    static bool FindSubFolders(const std::string & sFolderToCheck, 
                               std::vector< std::string > & vecSubFoldersFound);
}

Nel file DLL 2:

void aFunction()
{
    std::vector<std::string> folders;
    std::string sLocation;
    ...
    abc::FindSubFolders(sLocation, folders)
}

In modalità di rilascio, tutto funziona bene. Ma in modalità debug, mi viene in mente un errore di asserzione nel distruttore di uno degli std :: stringhe nel vettore delle cartelle (quando le cartelle non rientrano nell'ambito alla fine di aFunction):

dbgheap.c: linea 1274

/*
 * If this ASSERT fails, a bad pointer has been passed in. It may be
 * totally bogus, or it may have been allocated from another heap.
 * The pointer MUST come from the 'local' heap.
 */
_ASSERTE(_CrtIsValidHeapPointer(pUserData));

Suppongo che ciò sia dovuto al fatto che la memoria è stata allocata nell'heap del file DLL 1, ma viene liberata nel file DLL 2.

Il commento in dbgheap.c sembra abbastanza insistente sul fatto che questo sia un problema.

Perché questo è un problema del genere, quando sembra funzionare bene se lo ignoro? Esiste un modo per non far fallire l'asserzione?

È stato utile?

Soluzione

Come sean ha già detto, la build di rilascio ignora semplicemente quella dichiarazione di eliminazione, quindi il meglio che puoi sperare è una perdita di memoria.

Se si ha il controllo su come vengono compilati entrambi i file DLL, assicurarsi di utilizzare le impostazioni DLL di debug multi-thread (/ MDd) o DLL multi-thread (/ MD) per la libreria di runtime. In questo modo, entrambi i file DLL utilizzeranno lo stesso sistema di runtime e condivideranno lo stesso heap.

Il rovescio della medaglia è che è necessario installare il sistema di runtime insieme all'applicazione (Microsoft offre un programma di installazione per questo). Funzionerà perfettamente sulla tua macchina di sviluppo poiché Visual Studio installa anche quel sistema di runtime, ma su una macchina appena installata segnalerà i file DLL mancanti.

Altri suggerimenti

Molto probabilmente, la build di rilascio presenta lo stesso problema, ma le build di rilascio non lo affermano. Ignorano semplicemente il problema. Potresti non vedere mai un problema. Oppure potresti vedere il danneggiamento dei dati. O potresti vedere un incidente. Forse solo i tuoi utenti sperimenteranno bug che semplicemente non sei in grado di riprodurre.

Non ignorare le asserzioni CRT.

Dovresti sempre usare il deallocatore appropriato (quello che corrisponde all'allocatore usato per iniziare). Se si utilizzano librerie CRT statiche nei file DLL, i file DLL utilizzano heap diversi. Non è possibile distribuire la memoria su heap. Allocare e deallocare un blocco di memoria utilizzando lo stesso heap.

Se stai usando librerie CRT condivise nei tuoi file DLL, allora dovrebbero usare lo stesso heap e puoi allocare in un file DLL e deallocare in un altro.

Come altri dicono, il problema può essere risolto assicurandosi che il CRT sia condiviso tra i due moduli. Ma ci sono scenari comuni in cui questo contratto è difficile da applicare.

Il motivo è che assicurarsi di collegarsi a un CRT condiviso non funzionerà se EXE e DLL non si collegano alla stessa versione di CRT (come in 6.0, 7.0, 8.0). Ad esempio, se prendi una DLL che è stata creata in VC6.0 e provi a usarla con una build EXE in VS2010, otterrai lo stesso problema di prima. Le due versioni di CRT verranno caricate nel processo e ognuna utilizzerà il proprio heap per l'allocazione, indipendentemente dal fatto che EXE e DLL utilizzino CRT "condiviso", non saranno le stesse.

Una procedura migliore per un'API sarebbe quella di assicurarsi che anche gli oggetti allocati su un lato vengano distrutti sullo stesso lato. So che sembra brutto ma è l'unico modo per garantire che una DLL rimanga compatibile binaria.

Questo è solo un problema se l'applicazione o uno (o più) file DLL è collegato alla versione statica della libreria standard. Ciò è stato risolto circa un decennio fa da MS rilasciando la versione della libreria condivisa della libreria standard. Questo perché ogni versione della libreria standard creerà il proprio heap interno e quindi con più heap è necessario rilasciare la memoria nell'heap corretto. Usando la versione condivisa della libreria standard usano tutti lo stesso heap.

Oggigiorno è pratica standard per l'applicazione e tutti i file DLL dovrebbero essere creati per utilizzare la versione dinamica della libreria standard.

L'unica avvertenza a quanto sopra è se si crea il proprio heap e si alloca memoria da questo heap. Ma questa è una procedura molto specializzata eseguita solo in rare situazioni (e se capisci abbastanza per usarla, non ti troverai nella situazione di porre questa domanda).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top