Pergunta

Eu tenho código de invólucro C# que chama funções de uma DLL nativa (C ++). Atualmente, posso adicionar uma referência à DLL C# e definir a opção 'Copiar local' como true. No entanto, a DLL nativa, que é uma dependência, não pode ser adicionada como referência - portanto, não há opção 'cópia local'.

Eu tentei as seguintes abordagens

  1. Usando eventos pós-construção para copiar a DLL nativa da pasta LIBS para o $(TargetFolder)

    copy "$(ProjectDir)Libs\NQuantLibc.dll" "$(TargetDir)NQuantLibc.dll"

  2. Incluído a DLL nativa como um item existente no projeto (Add -> Item existente -> Incluir DLL). Esta opção me permite usar a opção 'cópia local'. A desvantagem dessa abordagem é que a DLL sempre mostra como um item de projeto.

Eu também tentei "Mostrar todos os arquivos", o que me permitiu ver a pasta LIBS. Em seguida, incluo o arquivo nquantlibc.dll no projeto, o que me permitiu definir a opção 'cópia local'. No entanto, isso me deu um resultado inesperado. Criou uma subpasta LIBS contendo a DLL dentro da pasta BIN (por exemplo, bin/debug/Libs/NQuantLibc.dll). Não é o ideal, pois a DLL C# não conseguiu chamar adequadamente a DLL nativa, pois não estava lá.

Ambas as opções acima acima funcionam. Existem maneiras melhores de copiar uma DLL nativa para a pasta BIN, de modo que a dependência sempre seja resolvida? Como alternativa, existe uma abordagem diferente para esse tipo de cenário?

Foi útil?

Solução

Use Project + Adicionar item existente e selecione a DLL. Selecione o arquivo adicionado na janela Solution Explorer. Na janela Propriedades, altere a configuração do diretório de cópia para "copiar se mais recente".

Outras dicas

Você pode adicionar a DLL nativa como um item vinculado e usar "Copie se mais recente".
O problema com as DLLs nativas é que às vezes você deseja usar DLLs diferentes de acordo com a configuração do projeto (depuração/versão ou plataforma).

Você pode editar o .csproj do projeto e vincular a DLL nativa condicionalmente:

 <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|Win32' ">
    <Content Include="..\..\bin\Win32\Release\NQuantLibc.dll">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
 </ItemGroup>   
 <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|Win32' ">
    <Content Include="..\..\bin\Win32\Debug\NQuantLibc_d.dll">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
  <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
    <Content Include="..\..\bin\x64\Debug\NQuantLibc_d.dll">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
  <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
    <Content Include="..\..\bin\x64\Release\NQuantLibc.dll">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>

Observe que a opção de cópia está definida como Preservenewest o que significa "copie se mais recente".

Encontrei uma maneira melhor. Nuget pode adicionar arquivos .Targets, as bruxas são armazenadas na pasta Build do pacote, em seu projeto. Com essa maneira, você pode copiar em cada um compra alguns arquivos do seu pacote onde quiser. No exemplo seguinte, eu armazenei uma DLL não -dotnet em uma pasta "binários". Em cada compilação, ele verifica se as DLLs já estão copiadas na pasta de saída ($ em variável de saída de saída) e copie -as, se necessário.

Conteúdo NUSPEC:

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>Example</id>
        <version>1.0.0</version>
        <authors>Example</authors>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>Example</description>
    </metadata>
    <files>
        <file src="Non-DotNet.dll" target="binaries\Non-DotNet.dll" />
        <file src="DotNet.dll" target="lib\net40\DotNet.dll" />
        <file src="Example.targets" target="build\Example.targets" />
    </files>
</package>

Exemplo.Targets Conteúdo:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="CopyBinaries" BeforeTargets="BeforeBuild">
        <CreateItem Include="$(MSBuildThisFileDirectory)..\binaries\**\*.*">
            <Output TaskParameter="Include" ItemName="PackageBinaries" /> 
        </CreateItem>

        <Copy SourceFiles="@(PackageBinaries)"
              DestinationFolder="$(OutputPath)"
              SkipUnchangedFiles="true"
              OverwriteReadOnlyFiles="true"
        />
    </Target>
</Project>

Adicione a DLL como um arquivo no projeto ("como link", talvez se você ainda quiser residir em outro diretório). Em seguida, defina a ação de compilação como conteúdo e copiar para o diretório de saída como true.

Se você está bem com a pasta "Libs" criada, pode tentar adicioná -la ao Caminho de sondagem para o seu aplicativo, adicionando o seguinte no app.config Arquivo:

<configuration>
   <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <probing privatePath="Libs;Bin2"/>
      </assemblyBinding>
   </runtime>
</configuration>

Isso fará com que o tempo de execução observe todos os diretórios especificados para a DLL.

EDITAR Infelizmente, isso não afeta o carregamento de DLL não gerenciado por dllimport.

Aparentemente, eu tinha o mesmo problema, mas não queria fazer tanto a edição de arquivos do projeto, então acabei usando o seguinte script pós-construção:

xcopy /y "$(ProjectDir)\lib_$(Platform)\*.dll" "$(ProjectDir)$(OutDir)"

Apenas certifique -se de ter uma pasta para cada plataforma de segmentação necessária, por exemplo: lib_x86, lib_x64 e talvez lib_AnyCPU

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top