Usare il Run-Time C

Questo articolo descrive come usare il runtime C.

Versione originale del prodotto: Visual C++
Numero KB originale: 94248

Sezione 1: Sono disponibili tre forme di librerie C Run-Time (CRT)

Esistono tre forme della libreria di runtime C fornite con Win32 SDK:

  • LIBC. LIB è una libreria collegata in modo statico per i programmi a thread singolo.

  • LIBCMT. LIB è una libreria collegata in modo statico che supporta programmi multithreading.

  • CRTDLL. LIB è una libreria di importazione per CRTDLL.DLL che supporta anche programmi multithreading. CRTDLL.DLL fa parte di Windows NT.

Microsoft Visual C++'edizione a 32 bit contiene anche questi tre moduli, tuttavia, il CRT in una DLL è denominato MSVCRT. LIB. La DLL è ridistribuibile. Il nome dipende dalla versione di VC++ (ovvero MSVCRT10.DLL o MSVCRT20.DLL). Si noti tuttavia che MSVCRT10.DLL non è supportato in Win32s, mentre CRTDLL. LIB è supportato in Win32s. MSVCRT20.DLL è disponibile in due versioni: una per Windows NT e l'altra per Win32s.

Sezione 2: Uso delle librerie CRT durante la compilazione di una DLL

Quando si compila una DLL che usa una qualsiasi delle librerie di runtime C, per assicurarsi che il CRT sia inizializzato correttamente,

  1. La funzione di inizializzazione deve essere denominata DllMain() e il punto di ingresso deve essere specificato con l'opzione linker -entry:_DllMainCRTStartup@12

    oppure

  2. Il punto di ingresso della DLL deve chiamare CRT_INIT() in modo esplicito il collegamento al processo ed elaborare lo scollegamento.

Ciò consente alle librerie di runtime C di allocare e inizializzare correttamente i dati di runtime C quando un processo o un thread si connette alla DLL, di pulire correttamente i dati di runtime C quando un processo si sta scollegando dalla DLL e di costruire e distruggere correttamente gli oggetti C++ globali nella DLL.

Gli esempi di Win32 SDK usano tutti il primo metodo. Usarli come esempio. Fare anche riferimento a Win32 Programmer's Reference for DllEntryPoint() e alla documentazione di Visual C++ per DllMain(). Si noti che DllMainCRTStartup() chiama CRT_INIT() e CRT_INIT() chiamerà dllMain(), se esistente, dell'applicazione.

Se si vuole usare il secondo metodo e chiamare manualmente il codice di inizializzazione CRT, invece di usare DllMainCRTStartup() e DllMain(), sono disponibili due tecniche:

  1. Se non è presente alcuna funzione di voce che esegue il codice di inizializzazione, specificare CRT_INIT() come punto di ingresso della DLL. Supponendo di aver incluso NTWIN32. Mak, che definisce DLLENTRY come @12, aggiungere l'opzione alla riga di collegamento della DLL:-entry:_CRT_INIT$(DLLENTRY).

    oppure

  2. Se si dispone di un punto di ingresso DLL personalizzato, eseguire le operazioni seguenti nel punto di ingresso:

    1. Usare questo prototipo per CRT_INIT(): BOOL WINAPI _CRT_INIT(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);

      Per informazioni sui CRT_INIT() valori restituiti, vedere la documentazione DllEntryPoint. Vengono restituiti gli stessi valori.

    2. In DLL_PROCESS_ATTACH e DLL_THREAD_ATTACH (vedere DllEntryPoint nelle informazioni di riferimento sull'API Win32 per altre informazioni su questi flag), chiamare CRT_INIT(), prima di chiamare qualsiasi funzione di runtime C o eseguire operazioni a virgola mobile.

    3. Chiamare il proprio codice di processo/inizializzazione del thread/terminazione.

    4. On DLL_PROCESS_DETACH e DLL_THREAD_DETACH, chiamare CRT_INIT() last, dopo che tutte le funzioni di runtime C sono state chiamate e tutte le operazioni a virgola mobile sono state completate.

Assicurarsi di passare a CRT_INIT() tutti i parametri del punto di ingresso; CRT_INIT() prevede che tali parametri non funzionino in modo affidabile se vengono omessi (in particolare, è necessario fdwReason per determinare se è necessaria l'inizializzazione o la terminazione del processo).

Di seguito è riportata una funzione di punto di ingresso di esempio di base che mostra quando e come effettuare queste chiamate a CRT_INIT() nel punto di ingresso DLL:

BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason,
LPVOID lpReserved)
{
    if (fdwReason == DLL_PROCESS_ATTACH || fdwReason == DLL_THREAD_ATTACH)
    if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
    return(FALSE);

    if (fdwReason == DLL_PROCESS_DETACH || fdwReason == DLL_THREAD_DETACH)
    if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
    return(FALSE);
    return(TRUE);
}

Nota

Questa operazione non è necessaria se si usano DllMain() e -entry:_DllMainCRTStartup@12.

Sezione 3: Uso di NTWIN32. Mak per semplificare il processo di compilazione

Sono presenti macro definite in NTWIN32. Mak che può essere usato per semplificare i makefile e per assicurarsi che siano compilati correttamente per evitare conflitti. Per questo motivo, Microsoft consiglia vivamente di usare NTWIN32. MAK e le macro in tale codice.

Per la compilazione, usare: $(cvarsdll) for apps/DLLs using CRT in a DLL.

Per il collegamento, usare una delle opzioni seguenti:

  • $(conlibsdll) for console apps/DLLs using CRT in a DLL
  • $(guilibsdll) for GUI apps using CRT in a DLL

Sezione 4: Problemi riscontrati durante l'uso di più librerie CRT

Se un'applicazione che effettua chiamate di runtime C si collega a una DLL che esegue anche chiamate di runtime C, tenere presente che se sono entrambe collegate a una delle librerie di runtime C collegate in modo statico (LIBC, Statically Linked C Run-Time Library). LIB o LIBCMT. LIB), il .EXE e la DLL avranno copie separate di tutte le funzioni di runtime C e le variabili globali. Ciò significa che i dati di runtime C non possono essere condivisi tra il .EXE e la DLL. Alcuni dei problemi che possono verificarsi di conseguenza sono:

  • Passaggio degli handle di flusso memorizzati nel buffer dall'.EXE/DLL all'altro modulo

  • Allocazione della memoria con una chiamata di runtime C nel .EXE/DLL e riassegnazione o liberazione nell'altro modulo

  • Verifica o impostazione del valore della variabile globale errno nel .EXE/DLL e prevede che sia lo stesso nell'altro modulo. Un problema correlato è la chiamata perror() nel modulo opposto da cui si è verificato l'errore di runtime C, poiché perror() usa errno.

Per evitare questi problemi, collegare sia il .EXE che la DLL con CRTDLL. LIB o MSVCRT. LIB, che consente sia al .EXE che alla DLL di usare il set comune di funzioni e dati contenuti in CRT in una DLL e i dati di runtime C, ad esempio gli handle di flusso, possono quindi essere condivisi sia dal .EXE che dalla DLL.

Sezione 5: Combinazione di tipi di libreria

È possibile collegare la DLL con CRTDLL. LIB/MSVCRT. LIB indipendentemente da ciò a cui è collegato il .EXE se si evita di combinare strutture di dati CRT e passare handle di file CRT o puntatori CRT FILE* ad altri moduli.

Quando si combinano i tipi di libreria, rispettare quanto segue:

  • Gli handle di file CRT possono essere gestiti solo dal modulo CRT che li ha creati.

  • I puntatori CRT FILE* possono essere gestiti solo dal modulo CRT che li ha creati.

  • La memoria allocata con la funzione malloc() CRT può essere liberata o riallocata solo dal modulo CRT che l'ha allocata.

Per illustrare questo aspetto, si consideri l'esempio seguente:

  • .EXE è collegato a MSVCRT. LIB
  • LA DLL A è collegata a LIBCMT. LIB
  • LA DLL B è collegata a CRTDLL. LIB

Se il .EXE crea un handle di file CRT usando _create() o _open(), questo handle di file può essere passato solo a _lseek(), _read(), _write(), e _close()così via nel file .EXE. Non passare questo handle di file CRT a una dll. Non passare un handle di file CRT ottenuto dalla DLL all'altra DLL o al .EXE.

Se la DLL A alloca un blocco di memoria con malloc(), solo la DLL A può chiamare free(), _expand()o realloc() per operare su tale blocco. Non è possibile chiamare malloc() dalla DLL A e provare a liberare tale blocco dal .EXE o dalla DLL B.

Nota

Se tutti e tre i moduli sono stati collegati con CRTDLL. LIB o tutti e tre erano collegati a MSVCRT. LIb, queste restrizioni non si applicano.

Quando si collegano DLL con LIBC. LIB, tenere presente che se esiste la possibilità che tale DLL venga chiamata da un programma multithreading, la DLL non supporterà più thread in esecuzione nella DLL contemporaneamente, il che può causare problemi importanti. Se è possibile che la DLL venga chiamata da programmi multithreading, assicurarsi di collegarla a una delle librerie che supportano programmi multithreading (LIBCMT). LIB, CRTDLL. LIB o MSVCRT. LIB).