Verwenden des C-Run-Time

In diesem Artikel wird die Verwendung der C-Laufzeit beschrieben.

Ursprüngliche Produktversion: Visual C++
Ursprüngliche KB-Nummer: 94248

Abschnitt 1: Drei Arten von C-Run-Time-Bibliotheken (CRT) sind verfügbar

Es gibt drei Formen der C-Laufzeitbibliothek, die mit dem Win32 SDK bereitgestellt wird:

  • LIBC. LIB ist eine statisch verknüpfte Bibliothek für Singlethreadprogramme.

  • LIBCMT. LIB ist eine statisch verknüpfte Bibliothek, die Multithreadprogramme unterstützt.

  • CRTDLL. LIB ist eine Importbibliothek für CRTDLL.DLL, die auch Multithreadprogramme unterstützt. CRTDLL.DLL selbst ist Teil von Windows NT.

Microsoft Visual C++ 32-Bit-Edition diese drei Formulare enthält, heißt die CRT in einer DLL jedoch MSVCRT. LIB. Die DLL ist verteilbar. Der Name hängt von der Version von VC++ ab (d. b. MSVCRT10.DLL oder MSVCRT20.DLL). Beachten Sie jedoch, dass MSVCRT10.DLL unter Win32s nicht unterstützt wird, während CRTDLL. LIB wird unter Win32s unterstützt. MSVCRT20.DLL gibt es in zwei Versionen: eine für Windows NT und die andere für Win32s.

Abschnitt 2: Verwenden der CRT-Bibliotheken beim Erstellen einer DLL

Wenn Sie eine DLL erstellen, die eine der C-Laufzeitbibliotheken verwendet, um sicherzustellen, dass die CRT ordnungsgemäß initialisiert ist,

  1. Die Initialisierungsfunktion muss benannt DllMain() werden, und der Einstiegspunkt muss mit der Linkeroption angegeben werden. -entry:_DllMainCRTStartup@12

    oder

  2. Der Einstiegspunkt der DLL muss explizit beim Anfügen und Trennen des Prozesses aufrufen CRT_INIT() .

Dadurch können die C-Laufzeitbibliotheken C-Laufzeitdaten ordnungsgemäß zuordnen und initialisieren, wenn ein Prozess oder Thread an die DLL angefügt wird, C-Laufzeitdaten ordnungsgemäß sauber, wenn ein Prozess von der DLL getrennt wird, und globale C++-Objekte in der DLL ordnungsgemäß erstellt und zerstört werden.

Die Win32 SDK-Beispiele verwenden alle die erste Methode. Verwenden Sie sie als Beispiel. Lesen Sie auch die Win32-Programmierreferenz für DllEntryPoint() und die Visual C++-Dokumentation für DllMain(). Beachten Sie, dass DllMainCRTStartup() dllMain() Ihrer Anwendung aufgerufen CRT_INIT() und CRT_INIT() aufgerufen wird, sofern vorhanden.

Wenn Sie die zweite Methode verwenden und den CRT-Initialisierungscode selbst aufrufen möchten, gibt es anstelle von DllMainCRTStartup() und DllMain()zwei Techniken:

  1. Wenn keine Eingabefunktion vorhanden ist, die Initialisierungscode ausführt, geben Sie als Einstiegspunkt der DLL an CRT_INIT() . Angenommen, Sie haben NTWIN32 eingeschlossen. MAK, das als @12 definiert DLLENTRY wird, fügen Sie die Option zur Linkzeile der DLL hinzu:-entry:_CRT_INIT$(DLLENTRY) .

    oder

  2. Wenn Sie über einen eigenen DLL-Einstiegspunkt verfügen, führen Sie im Einstiegspunkt die folgenden Schritte aus:

    1. Verwenden Sie diesen Prototyp für CRT_INIT(): BOOL WINAPI _CRT_INIT(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);

      Informationen zu CRT_INIT() Rückgabewerten finden Sie in der Dokumentation DllEntryPoint. Die gleichen Werte werden zurückgegeben.

    2. Rufen DLL_PROCESS_ATTACHCRT_INIT()Sie auf und DLL_THREAD_ATTACH (weitere Informationen zu diesen Flags finden Sie unter DllEntryPoint in der Win32-API-Referenz) zuerst auf, bevor C-Laufzeitfunktionen aufgerufen oder Gleitkommavorgänge ausgeführt werden.

    3. Rufen Sie Ihren eigenen Prozess-/Threadinitialisierungs-/Beendigungscode auf.

    4. Rufen DLL_PROCESS_DETACH Sie auf und DLL_THREAD_DETACHzuletzt auf CRT_INIT() , nachdem alle C-Laufzeitfunktionen aufgerufen und alle Gleitkommavorgänge abgeschlossen wurden.

Stellen Sie sicher, dass Sie an CRT_INIT() alle Parameter des Einstiegspunkts übergeben. CRT_INIT() Erwartet diese Parameter, sodass dinge möglicherweise nicht zuverlässig funktionieren, wenn sie nicht angegeben werden (insbesondere ist fdwReason erforderlich, um zu bestimmen, ob eine Prozessinitialisierung oder -beendigung erforderlich ist).

Im Folgenden finden Sie eine Gerüstbeispiel-Einstiegspunktfunktion, die zeigt, wann und wie diese Aufrufe CRT_INIT() von im DLL-Einstiegspunkt ausgeführt werden:

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);
}

Hinweis

Dies ist nicht erforderlich, wenn Sie und -entry:_DllMainCRTStartup@12verwendenDllMain().

Abschnitt 3: Verwenden von NTWIN32. MAK zur Vereinfachung des Buildprozesses

In NTWIN32 sind Makros definiert. MAK, der verwendet werden kann, um Ihre Makefiles zu vereinfachen und sicherzustellen, dass sie ordnungsgemäß erstellt sind, um Konflikte zu vermeiden. Aus diesem Grund empfiehlt Microsoft dringend, NTWIN32 zu verwenden. MAK und die darin enthaltenen Makros.

Verwenden Sie für die Kompilierung Folgendes: $(cvarsdll) for apps/DLLs using CRT in a DLL.

Verwenden Sie zum Verknüpfen eine der folgenden Optionen:

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

Abschnitt 4: Probleme bei der Verwendung mehrerer CRT-Bibliotheken

Wenn eine Anwendung, die C-Laufzeitaufrufe durchführt, mit einer DLL verknüpft ist, die auch C-Laufzeitaufrufe vornimmt, beachten Sie Folgendes, wenn beide mit einer der statisch verknüpften C-Laufzeitbibliotheken (LIBC) verknüpft sind. LIB oder LIBCMT. LIB), die .EXE und DLL verfügen über separate Kopien aller C-Laufzeitfunktionen und globalen Variablen. Dies bedeutet, dass C-Laufzeitdaten nicht zwischen dem .EXE und der DLL freigegeben werden können. Einige der Probleme, die als Folge auftreten können, sind:

  • Übergeben gepufferter Datenstromhandles vom .EXE/DLL an das andere Modul

  • Zuweisung von Arbeitsspeicher mit einem C-Laufzeitaufruf im .EXE/DLL und Neuzuordnung oder Freigabe im anderen Modul

  • Überprüfen oder Festlegen des Werts der globalen errno-Variablen im .EXE/DLL und Erwartung, dass er im anderen Modul identisch ist. Ein verwandtes Problem ist das Aufrufen perror() des gegenüberliegenden Moduls, von dem der C-Laufzeitfehler aufgetreten ist, da perror() errno verwendet.

Um diese Probleme zu vermeiden, verknüpfen Sie sowohl die .EXE als auch die DLL mit CRTDLL. LIB oder MSVCRT. LIB, das es sowohl dem .EXE als auch der DLL ermöglicht, den gemeinsamen Satz von Funktionen und Daten, die in CRT in einer DLL enthalten sind, zu verwenden, und C-Laufzeitdaten wie Streamhandles können dann sowohl von der .EXE als auch von der DLL freigegeben werden.

Abschnitt 5: Mischen von Bibliothekstypen

Sie können Ihre DLL mit CRTDLL verknüpfen. LIB/MSVCRT. LIB unabhängig davon, mit was Ihre .EXE verknüpft ist, wenn Sie crt-Datenstrukturen nicht mischen und CRT-Dateihandles oder CRT FILE*-Zeiger an andere Module nicht übergeben.

Beim Mischen von Bibliothekstypen gelten folgende Punkte:

  • CRT-Dateihandles dürfen nur von dem CRT-Modul betrieben werden, das sie erstellt hat.

  • CRT FILE*-Zeiger dürfen nur von dem CRT-Modul betrieben werden, das sie erstellt hat.

  • Der mit der CRT-Funktion malloc() zugeordnete Speicher darf nur durch das CRT-Modul freigegeben oder neu zugewiesen werden.

Um dies zu veranschaulichen, betrachten Sie das folgende Beispiel:

  • .EXE ist mit MSVCRT verknüpft. LIB
  • DLL A ist mit LIBCMT verknüpft. LIB
  • DLL B ist mit CRTDLL verknüpft. LIB

Wenn die .EXE ein CRT-Dateihandle mit _create() oder _open()erstellt, kann dieses Dateihandle nur an _lseek(), _read(), _write(), _close()usw. in der .EXE-Datei übergeben werden. Übergeben Sie dieses CRT-Dateihandle nicht an eine der dll-Dateien. Übergeben Sie kein CRT-Dateihandle, das von einer DLL an die andere DLL oder an die .EXE abgerufen wurde.

Wenn DLL A einen Speicherblock mit malloc()zuweist, kann nur DLL A , _expand()oder realloc() aufrufenfree(), um für diesen Block zu arbeiten. Sie können nicht über DLL A aufrufen malloc() und versuchen, diesen Block aus dem .EXE oder von DLL B freizusprechen.

Hinweis

Wenn alle drei Module mit CRTDLL verknüpft wurden. LIB oder alle drei waren mit MSVCRT verknüpft. LIb, diese Einschränkungen würden nicht gelten.

Beim Verknüpfen von DLLs mit LIBC. LIB: Wenn es die Möglichkeit gibt, dass eine solche DLL von einem Multithreadprogramm aufgerufen wird, unterstützt die DLL nicht mehrere Threads, die gleichzeitig in der DLL ausgeführt werden, was zu großen Problemen führen kann. Wenn die Möglichkeit besteht, dass die DLL von Multithreadprogrammen aufgerufen wird, stellen Sie sicher, dass Sie sie mit einer der Bibliotheken verknüpfen, die Multithreadprogramme (LIBCMT. LIB, CRTDLL. LIB oder MSVCRT. LIB).