Use o Run-Time C

Este artigo descreve como usar o tempo de execução C.

Versão original do produto: Visual C++
Número original do KB: 94248

Seção 1: Três formas de bibliotecas de C Run-Time (CRT) estão disponíveis

Há três formas da biblioteca de tempo de execução C fornecidas com o SDK do Win32:

  • LIBC. O LIB é uma biblioteca vinculada estaticamente para programas com thread único.

  • LIBCMT. O LIB é uma biblioteca estaticamente vinculada que dá suporte a programas multithread.

  • CRTDLL. O LIB é uma biblioteca de importação para CRTDLL.DLL que também dá suporte a programas multithread. CRTDLL.DLL em si faz parte do Windows NT.

Microsoft Visual C++ edição de 32 bits também contém esses três formulários, no entanto, o CRT em uma DLL é chamado MSVCRT. LIB. A DLL é redistribuível. Seu nome depende da versão do VC++ (ou seja, MSVCRT10.DLL ou MSVCRT20.DLL). Observe, no entanto, que não há suporte para MSVCRT10.DLL no Win32s, enquanto CRTDLL. O LIB tem suporte no Win32s. MSVCRT20.DLL vem em duas versões: uma para Windows NT e outra para Win32s.

Seção 2: Usando as bibliotecas CRT ao criar uma DLL

Ao criar uma DLL que usa qualquer uma das bibliotecas de tempo de execução C, a fim de garantir que o CRT seja inicializado corretamente,

  1. A função de inicialização deve ser nomeada DllMain() e o ponto de entrada deve ser especificado com a opção do vinculador -entry:_DllMainCRTStartup@12

    ou

  2. O ponto de entrada da DLL deve chamar CRT_INIT() explicitamente a anexação do processo e o desanexamento do processo.

Isso permite que as bibliotecas em tempo de execução C aloquem e inicializem adequadamente os dados em tempo de execução C quando um processo ou thread estiver se anexando à DLL, para limpo corretamente dados em tempo de execução C quando um processo estiver se desanexando da DLL e para objetos C++ globais na DLL serem construídos e destruídos corretamente.

Todos os exemplos de SDK do Win32 usam o primeiro método. Use-os como exemplo. Consulte também a Referência do Programador Win32 para DllEntryPoint() e a documentação do Visual C++ para DllMain(). Observe que DllMainCRTStartup() chama CRT_INIT() e CRT_INIT() chamará o DllMain() do aplicativo, se ele existir.

Se você quiser usar o segundo método e chamar o código de inicialização crt por conta própria, em vez de usar DllMainCRTStartup() e DllMain(), há duas técnicas:

  1. Se não houver nenhuma função de entrada que execute o código de inicialização, especifique CRT_INIT() como o ponto de entrada da DLL. Supondo que você tenha incluído NTWIN32. MAK, que define DLLENTRY como @12, adicione a opção à linha de link da DLL:-entry:_CRT_INIT$(DLLENTRY).

    ou

  2. Se você tiver seu próprio ponto de entrada DLL, faça o seguinte no ponto de entrada:

    1. Use este protótipo para CRT_INIT(): BOOL WINAPI _CRT_INIT(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);

      Para obter informações sobre CRT_INIT() valores retornados, consulte a documentação DllEntryPoint; os mesmos valores são retornados.

    2. Ativado DLL_PROCESS_ATTACH e DLL_THREAD_ATTACH (consulte DllEntryPoint na referência de API do Win32 para obter mais informações sobre esses sinalizadores), chame CRT_INIT(), primeiro, antes que qualquer função em tempo de execução C seja chamada ou qualquer operação de ponto flutuante seja executada.

    3. Chame seu próprio código de inicialização/término de processo/thread.

    4. On DLL_PROCESS_DETACH e DLL_THREAD_DETACH, call CRT_INIT() last, after all C Run-time functions have been called and all floating-point operations are completed.

Certifique-se de passar para CRT_INIT() todos os parâmetros do ponto de entrada; CRT_INIT() espera esses parâmetros, portanto, as coisas podem não funcionar de forma confiável se forem omitidos (em particular, fdwReason é necessário para determinar se a inicialização ou a rescisão do processo são necessárias).

Abaixo está uma função de ponto de entrada de exemplo de esqueleto que mostra quando e como fazer essas chamadas CRT_INIT() no ponto de entrada 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);
}

Observação

Isso não é necessário se você estiver usando DllMain() e -entry:_DllMainCRTStartup@12.

Seção 3: Usando NTWIN32. MAK para simplificar o processo de build

Há macros definidas em NTWIN32. MAK que pode ser usado para simplificar seus makefiles e para garantir que eles sejam construídos corretamente para evitar conflitos. Por esse motivo, a Microsoft recomenda muito o uso de NTWIN32. MAK e as macros nela.

Para compilação, use: $(cvarsdll) for apps/DLLs using CRT in a DLL.

Para vincular, use um dos seguintes:

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

Seção 4: Problemas encontrados ao usar várias bibliotecas de CRT

Se um aplicativo que faz chamadas em tempo de execução C links para uma DLL que também faz chamadas em tempo de execução C, esteja ciente de que, se ambos estiverem vinculados a uma das bibliotecas de tempo de execução C vinculadas estaticamente (LIBC). LIB ou LIBCMT. LIB), o .EXE e a DLL terão cópias separadas de todas as funções em tempo de execução C e variáveis globais. Isso significa que os dados em tempo de execução C não podem ser compartilhados entre o .EXE e a DLL. Alguns dos problemas que podem ocorrer como resultado são:

  • Passando identificadores de fluxo em buffer do .EXE/DLL para o outro módulo

  • Alocar memória com uma chamada em tempo de execução C no .EXE/DLL e realocalá-la ou liberá-la no outro módulo

  • Verificando ou definindo o valor da variável errno global no .EXE/DLL e esperando que ele seja o mesmo no outro módulo. Um problema relacionado é chamar perror() no módulo oposto de onde ocorreu o erro de tempo de execução C, já que perror() usa errno.

Para evitar esses problemas, vincule o .EXE e a DLL ao CRTDLL. LIB ou MSVCRT. O LIB, que permite que o .EXE e a DLL usem o conjunto comum de funções e dados contidos no CRT em uma DLL, e dados em tempo de execução C, como identificadores de fluxo, podem ser compartilhados pelo .EXE e pela DLL.

Seção 5: Misturando tipos de biblioteca

Você pode vincular sua DLL ao CRTDLL. LIB/MSVCRT. LIB independentemente do que seu .EXE esteja vinculado se você evitar misturar estruturas de dados CRT e passar identificadores de arquivo CRT ou ponteiros CRT FILE* para outros módulos.

Ao misturar tipos de biblioteca, siga o seguinte:

  • Os identificadores de arquivo CRT só podem ser operados pelo módulo CRT que os criou.

  • Os ponteiros CRT FILE* só podem ser operados pelo módulo CRT que os criou.

  • A memória alocada com a função malloc() CRT só pode ser liberada ou realocada pelo módulo CRT que a alocou.

Para ilustrar isso, considere o seguinte exemplo:

  • .EXE está vinculado ao MSVCRT. LIB
  • A DLL A está vinculada ao LIBCMT. LIB
  • A DLL B está vinculada ao CRTDLL. LIB

Se o .EXE criar um identificador de arquivo CRT usando _create() ou _open(), esse identificador de arquivo só poderá ser passado para _lseek(), _read(), _write(), etc _close(). no arquivo .EXE. Não passe esse identificador de arquivo CRT para nenhuma das DLL. Não passe um identificador de arquivo CRT obtido da DLL para a outra DLL ou para o .EXE.

Se a DLL A alocar um bloco de memória com malloc(), somente a DLL A poderá chamar free(), _expand()ou realloc() operar nesse bloco. Você não pode chamar malloc() da DLL A e tentar liberar esse bloco do .EXE ou da DLL B.

Observação

Se todos os três módulos estiverem vinculados ao CRTDLL. LIB ou todos os três estavam vinculados ao MSVCRT. LIb, essas restrições não se aplicariam.

Ao vincular DLLs ao LIBC. LIB, esteja ciente de que, se houver a possibilidade de que essa DLL seja chamada por um programa multithread, a DLL não dará suporte a vários threads em execução na DLL ao mesmo tempo, o que pode causar grandes problemas. Se houver a possibilidade de que a DLL seja chamada por programas multithreaded, não deixe de vinculá-la a uma das bibliotecas que dão suporte a programas multithread (LIBCMT). LIB, CRTDLL. LIB ou MSVCRT. LIB).