Redirecionamento da biblioteca de vínculo dinâmico

O carregador de DLL é a parte do sistema operacional (SO) que resolve referências a DLLs e as carrega e vincula. O redirecionamento da biblioteca de vínculo dinâmico (DLL) é uma das técnicas pelas quais você pode influenciar o comportamento do carregador de DLL e controlar qual das várias DLLs candidatas ele realmente carrega.

Outros nomes para esse recurso incluem .local, Dot Local, DotLocal e Depuração de Dot Local.

Problemas de controle de versão de DLL

Se o aplicativo depender de uma versão específica de uma DLL compartilhada e outro aplicativo estiver instalado com uma versão mais recente ou mais antiga dessa DLL, isso poderá causar problemas de compatibilidade e instabilidade; isso pode fazer com que seu aplicativo comece a falhar.

O carregador de DLL examina a pasta da qual o processo de chamada foi carregado (a pasta do executável) antes de examinar outros locais do sistema de arquivos. Portanto, uma solução alternativa é instalar a DLL de que seu aplicativo precisa na pasta do executável. Isso efetivamente torna a DLL privada.

Mas isso não resolve o problema para COM. Duas versões incompatíveis de um servidor COM podem ser instaladas e registradas (mesmo em locais diferentes do sistema de arquivos), mas há apenas um local para registrar o servidor COM. Portanto, somente o servidor COM registrado mais recente será ativado.

Você pode usar o redirecionamento para resolver esses problemas.

Carregando e testando binários privados

As regras que o carregador de DLL segue garantem que as DLLs do sistema sejam carregadas dos locais do sistema Windows, por exemplo, a pasta do sistema (%SystemRoot%\system32). Essas regras evitam o plantio de ataques, em que um adversário coloca o código que escreveu em um local no qual pode escrever e, em seguida, convence algum processo a carregá-lo e executá-lo. Mas as regras do carregador também dificultam o trabalho em componentes do sistema operacional, pois executá-los requer a atualização do sistema; e isso é uma mudança muito impactante.

Mas você pode usar o redirecionamento para carregar cópias privadas de DLLs (para fins como teste ou medir o impacto no desempenho de uma alteração de código).

Se você quiser contribuir com o código-fonte no repositório público do GitHub WindowsAppSDK, você desejará testar suas alterações. E, novamente, esse é um cenário para o qual você pode usar o redirecionamento para carregar suas cópias privadas de DLLs em vez das versões que são enviadas com o SDK do Aplicativo Windows.

Suas opções

Na verdade, há duas maneiras de garantir que seu aplicativo use a versão da DLL que você deseja:

Dica

Se você for um desenvolvedor ou administrador, deverá usar o redirecionamento de DLL para aplicativos existentes. Isso ocorre porque ele não requer nenhuma alteração no próprio aplicativo. Mas se você estiver criando um novo aplicativo ou atualizando um aplicativo existente e quiser isolar seu aplicativo de possíveis problemas, crie um componente lado a lado.

Opcional: configurar o registro

Para habilitar o redirecionamento de DLL em todo o computador, você deve criar um novo valor de registro. Na chave HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options, crie um novo valor DWORD com o nome DevOverrideEnable. Defina o valor como 1 e reinicie o computador. Ou use o comando abaixo (e reinicie seu computador).

reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options" /v DevOverrideEnable /t REG_DWORD /d 1

Com esse conjunto de valores de registro, o redirecionamento da DLL do DotLocal é respeitado mesmo se o aplicativo tiver um manifesto do aplicativo.

Criar um arquivo ou pasta de redirecionamento

Para usar o redirecionamento de DLL, você criará um arquivo de redirecionamento ou uma pasta de redirecionamento (dependendo do tipo de aplicativo que você tem), como veremos em seções posteriores neste tópico.

Como redirecionar DLLs para aplicativos empacotados

Um aplicativo empacotado requer uma estrutura de pasta especial para redirecionamento de DLL. O caminho a seguir é onde o carregador será exibido quando o redirecionamento estiver habilitado:

<Drive>:\<path_to_package>\microsoft.system.package.metadata\application.local\

Se você conseguir editar seu arquivo .vcxproj, uma maneira conveniente de fazer com que essa pasta especial seja criada e implantada com seu pacote é adicionar algumas etapas ao build em seu .vcxproj:

<ItemDefinitionGroup>
    <PreBuildEvent>
        <Command>
            del $(FinalAppxManifestName) 2&gt;nul
            <!-- [[Using_.local_(DotLocal)_with_a_packaged_app]] This makes the extra DLL deployed via F5 get loaded instead of the system one. -->
            if NOT EXIST $(IntDir)\microsoft.system.package.metadata\application.local MKDIR $(IntDir)\microsoft.system.package.metadata\application.local
            if EXIST "&lt;A.dll&gt;" copy /y "&lt;A.dll&gt;" $(IntDir)\microsoft.system.package.metadata\application.local 2&gt;nul
            if EXIST "&lt;B.dll&gt;" copy /y "&lt;B.dll&gt;" $(IntDir)\microsoft.system.package.metadata\application.local 2&gt;nul
        </Command>
    </PreBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
    <!-- Include any locally built system experience -->
    <Media Include="$(IntDir)\microsoft.system.package.metadata\application.local\**">
        <Link>microsoft.system.package.metadata\application.local</Link>
    </Media>
</ItemGroup>

Vamos percorrer um pouco do que essa configuração faz.

  1. Configure um PreBuildEvent para sua experiência de Iniciar sem depurar (ou Iniciar depuração) do Visual Studio.

    <ItemDefinitionGroup>
      <PreBuildEvent>
    
  2. Certifique-se de que você tenha a estrutura de pasta correta em seu diretório intermediário.

    <!-- [[Using_.local_(DotLocal)_with_modern_apps]] This makes the extra DLL deployed via Start get loaded instead of the system one. -->
    if NOT EXIST $(IntDir)\microsoft.system.package.metadata\application.local MKDIR $(IntDir)\microsoft.system.package.metadata\application.local
    
  3. Copie todas as DLLs criadas localmente (e que você deseje usar preferencialmente em relação às DLLs implantadas pelo sistema) no diretório application.local. Você pode pegar DLLs em praticamente qualquer lugar (recomendamos que você use as macros disponíveis para o seu .vcxproj). Apenas certifique-se de que essas DLLs sejam compiladas antes deste projeto; caso contrário, eles estarão ausentes. Dois comandos de cópia de modelo são mostrados aqui; use quantos forem necessários e edite os espaços reservados <path-to-local-dll>.

      if EXIST "<path-to-local-dll>" copy /y "<path-to-local-dll>" $(IntDir)\microsoft.system.package.metadata\application.local 2&gt;nul
      if EXIST "<path-to-local-dll>" copy /y "<path-to-local-dll>" $(IntDir)\microsoft.system.package.metadata\application.local 2&gt;nul
      </Command>
    </PreBuildEvent>
    
  4. Por fim, indique que você deseja incluir o diretório especial e seu conteúdo no pacote implantado.

    <ItemGroup>
      <!-- Include any locally built system experience -->
      <Media Include="$(IntDir)\microsoft.system.package.metadata\application.local\**">
        <Link>microsoft.system.package.metadata\application.local</Link>
      </Media>
    </ItemGroup>
    

A abordagem descrita aqui (usando um diretório intermediário) mantém a inscrição de controle do código-fonte limpa e reduz a possibilidade de confirmar acidentalmente um binário compilado.

Em seguida, tudo o que você precisa fazer é (re)implantar seu projeto. Para obter uma (re)implantação limpa e completa, talvez você também precise desinstalar/limpar a implantação existente em seu dispositivo de destino.

Copiar os binários manualmente

Se você não conseguir usar .vcxproj da maneira mostrada acima, poderá chegar ao mesmo objetivo manualmente em seu dispositivo de destino com algumas etapas simples.

  1. Determine a pasta de instalação do pacote. Você pode fazer isso no PowerShell emitindo o comando Get-AppxPackage e procurando o InstallLocation retornado.

  2. Use esse InstallLocation para alterar as ACLs para permitir que você crie pastas/copie arquivos. Edite os espaços reservados <InstallLocation> neste script e execute o script:

    cd <InstallLocation>\Microsoft.system.package.metadata
    takeown /F . /A
    icacls  . /grant Administrators:F
    md <InstallLocation>\Microsoft.system.package.metadata\application.local
    
  3. Por fim, copie manualmente todas as DLLs criadas localmente (e que você deseje usar preferencialmente em relação às DLLs implantadas pelo sistema) no diretório application.local e (re)inicie o aplicativo.

Verificar se tudo funcionou

Para confirmar se a DLL correta está sendo carregada em runtime, você pode usar o Visual Studio com o depurador anexado.

  1. Abra a janela Módulos (Depurar>Windows>Módulos).
  2. Localize a DLL e certifique-se de que o Caminho indique a cópia redirecionada e não a versão implantada pelo sistema.
  3. Confirme se apenas uma cópia de uma determinada DLL está carregada.

Como redirecionar DLLs para aplicativos não empacotados

O arquivo de redirecionamento deve ser nomeado <your_app_name>.local. Portanto, se o nome do aplicativo for Editor.exe, nomeie o arquivo de redirecionamento como Editor.exe.local. Você deve instalar o arquivo de redirecionamento na pasta do executável. Você também deve instalar as DLLs na pasta do executável.

O conteúdo de um arquivo de redirecionamento é ignorado; sua presença por si só faz com que o carregador de DLL verifique a pasta do executável primeiro sempre que carrega uma DLL. Para atenuar o problema COM, esse redirecionamento se aplica ao carregamento de caminho completo e ao carregamento de nome parcial. Portanto, o redirecionamento ocorre no caso COM e também independentemente do caminho especificado para LoadLibrary ou LoadLibraryEx. Se a DLL não for encontrada na pasta do executável, o carregamento seguirá sua ordem de pesquisa habitual. Por exemplo, se o aplicativo C:\myapp\myapp.exe chamar LoadLibrary usando o seguinte caminho:

C:\Program Files\Common Files\System\mydll.dll

E se C:\myapp\myapp.exe.local e C:\myapp\mydll.dll existirem, LoadLibrary carregará C:\myapp\mydll.dll. Caso contrário, LoadLibrary carregará C:\Program Files\Common Files\System\mydll.dll.

Como alternativa, se existir uma pasta nomeada C:\myapp\myapp.exe.local e ela contiver mydll.dll, LoadLibrary carregará C:\myapp\myapp.exe.local\mydll.dll.

Se você estiver usando o redirecionamento de DLL e o aplicativo não tiver acesso a todas as unidades e diretórios na ordem de pesquisa, LoadLibrary interromperá a pesquisa assim que o acesso for negado. Se você não estiver usando o redirecionamento de DLL, o LoadLibrary ignorará os diretórios que ele não pode acessar e continuará pesquisando.

É uma boa prática instalar DLLs de aplicativo na mesma pasta que contém o aplicativo; mesmo se você não estiver usando o redirecionamento de DLL. Isso garante que a instalação do aplicativo não substitua outras cópias da DLL (causando falhas em outros aplicativos). Além disso, se você seguir essa boa prática, outros aplicativos não substituirão sua cópia da DLL (e não causarão falhas no aplicativo).